11import logging
2- from typing import IO , Optional
2+ import xml .etree .ElementTree as xml_etree # noqa: N813
3+ from io import BytesIO
4+ from typing import (
5+ IO ,
6+ TYPE_CHECKING ,
7+ Any ,
8+ BinaryIO ,
9+ Dict ,
10+ Optional ,
11+ Sequence ,
12+ TextIO ,
13+ Tuple ,
14+ Union ,
15+ cast ,
16+ )
317from xml .dom import XML_NAMESPACE
418from xml .sax .saxutils import XMLGenerator
519from xml .sax .xmlreader import AttributesNSImpl
620
721from rdflib import BNode , Literal , URIRef , Variable
8- from rdflib .compat import etree
922from rdflib .query import Result , ResultException , ResultParser , ResultSerializer
23+ from rdflib .term import Identifier
24+
25+ try :
26+ # https://adamj.eu/tech/2021/12/29/python-type-hints-optional-imports/
27+ import lxml .etree as lxml_etree
28+
29+ FOUND_LXML = True
30+ except ImportError :
31+ FOUND_LXML = False
1032
1133SPARQL_XML_NAMESPACE = "http://www.w3.org/2005/sparql-results#"
1234RESULTS_NS_ET = "{%s}" % SPARQL_XML_NAMESPACE
2749
2850class XMLResultParser (ResultParser ):
2951 # TODO FIXME: content_type should be a keyword only arg.
30- def parse (self , source , content_type : Optional [str ] = None ): # type: ignore[override]
52+ def parse (self , source : IO , content_type : Optional [str ] = None ): # type: ignore[override]
3153 return XMLResult (source )
3254
3355
3456class XMLResult (Result ):
35- def __init__ (self , source , content_type : Optional [str ] = None ):
36-
37- try :
38- # try use as if etree is from lxml, and if not use it as normal.
39- parser = etree .XMLParser (huge_tree = True ) # type: ignore[call-arg]
40- tree = etree .parse (source , parser )
41- except TypeError :
42- tree = etree .parse (source )
57+ def __init__ (self , source : IO , content_type : Optional [str ] = None ):
58+ parser_encoding : Optional [str ] = None
59+ if hasattr (source , "encoding" ):
60+ if TYPE_CHECKING :
61+ assert isinstance (source , TextIO )
62+ parser_encoding = "utf-8"
63+ source_str = source .read ()
64+ source = BytesIO (source_str .encode (parser_encoding ))
65+ else :
66+ if TYPE_CHECKING :
67+ assert isinstance (source , BinaryIO )
68+
69+ if FOUND_LXML :
70+ lxml_parser = lxml_etree .XMLParser (huge_tree = True , encoding = parser_encoding )
71+ tree = cast (
72+ xml_etree .ElementTree ,
73+ lxml_etree .parse (source , parser = lxml_parser ),
74+ )
75+ else :
76+ xml_parser = xml_etree .XMLParser (encoding = parser_encoding )
77+ tree = xml_etree .parse (source , parser = xml_parser )
4378
4479 boolean = tree .find (RESULTS_NS_ET + "boolean" )
4580 results = tree .find (RESULTS_NS_ET + "results" )
@@ -56,8 +91,18 @@ def __init__(self, source, content_type: Optional[str] = None):
5691 if type_ == "SELECT" :
5792 self .bindings = []
5893 for result in results : # type: ignore[union-attr]
94+ if result .tag != f"{ RESULTS_NS_ET } result" :
95+ # This is here because with lxml this also gets comments,
96+ # not just elements. Also this should not operate on non
97+ # "result" elements.
98+ continue
5999 r = {}
60100 for binding in result :
101+ if binding .tag != f"{ RESULTS_NS_ET } binding" :
102+ # This is here because with lxml this also gets
103+ # comments, not just elements. Also this should not
104+ # operate on non "binding" elements.
105+ continue
61106 # type error: error: Argument 1 to "Variable" has incompatible type "Union[str, None, Any]"; expected "str"
62107 # NOTE on type error: Element.get() can return None, and
63108 # this will invariably fail if passed into Variable
@@ -80,7 +125,7 @@ def __init__(self, source, content_type: Optional[str] = None):
80125 self .askAnswer = boolean .text .lower ().strip () == "true" # type: ignore[union-attr]
81126
82127
83- def parseTerm (element ) :
128+ def parseTerm (element : xml_etree . Element ) -> Union [ URIRef , Literal , BNode ] :
84129 """rdflib object (Literal, URIRef, BNode) for the given
85130 elementtree element"""
86131 tag , text = element .tag , element .text
@@ -90,15 +135,17 @@ def parseTerm(element):
90135 datatype = None
91136 lang = None
92137 if element .get ("datatype" , None ):
93- datatype = URIRef (element .get ("datatype" ))
138+ # type error: Argument 1 to "URIRef" has incompatible type "Optional[str]"; expected "str"
139+ datatype = URIRef (element .get ("datatype" )) # type: ignore[arg-type]
94140 elif element .get ("{%s}lang" % XML_NAMESPACE , None ):
95141 lang = element .get ("{%s}lang" % XML_NAMESPACE )
96142
97143 ret = Literal (text , datatype = datatype , lang = lang )
98144
99145 return ret
100146 elif tag == RESULTS_NS_ET + "uri" :
101- return URIRef (text )
147+ # type error: Argument 1 to "URIRef" has incompatible type "Optional[str]"; expected "str"
148+ return URIRef (text ) # type: ignore[arg-type]
102149 elif tag == RESULTS_NS_ET + "bnode" :
103150 return BNode (text )
104151 else :
@@ -109,14 +156,14 @@ class XMLResultSerializer(ResultSerializer):
109156 def __init__ (self , result ):
110157 ResultSerializer .__init__ (self , result )
111158
112- def serialize (self , stream : IO , encoding : str = "utf-8" , ** kwargs ):
113-
159+ def serialize (self , stream : IO , encoding : str = "utf-8" , ** kwargs : Any ) -> None :
114160 writer = SPARQLXMLWriter (stream , encoding )
115161 if self .result .type == "ASK" :
116162 writer .write_header ([])
117163 writer .write_ask (self .result .askAnswer )
118164 else :
119- writer .write_header (self .result .vars )
165+ # type error: Argument 1 to "write_header" of "SPARQLXMLWriter" has incompatible type "Optional[List[Variable]]"; expected "Sequence[Variable]"
166+ writer .write_header (self .result .vars ) # type: ignore[arg-type]
120167 writer .write_results_header ()
121168 for b in self .result .bindings :
122169 writer .write_start_result ()
@@ -134,7 +181,7 @@ class SPARQLXMLWriter:
134181 Python saxutils-based SPARQL XML Writer
135182 """
136183
137- def __init__ (self , output , encoding = "utf-8" ):
184+ def __init__ (self , output : IO , encoding : str = "utf-8" ):
138185 writer = XMLGenerator (output , encoding )
139186 writer .startDocument ()
140187 writer .startPrefixMapping ("" , SPARQL_XML_NAMESPACE )
@@ -147,7 +194,7 @@ def __init__(self, output, encoding="utf-8"):
147194 self ._encoding = encoding
148195 self ._results = False
149196
150- def write_header (self , allvarsL ) :
197+ def write_header (self , allvarsL : Sequence [ Variable ]) -> None :
151198 self .writer .startElementNS (
152199 (SPARQL_XML_NAMESPACE , "head" ), "head" , AttributesNSImpl ({}, {})
153200 )
@@ -161,48 +208,52 @@ def write_header(self, allvarsL):
161208 self .writer .startElementNS (
162209 (SPARQL_XML_NAMESPACE , "variable" ),
163210 "variable" ,
164- AttributesNSImpl (attr_vals , attr_qnames ),
211+ # type error: Argument 1 to "AttributesNSImpl" has incompatible type "Dict[Tuple[None, str], str]"; expected "Mapping[Tuple[str, str], str]"
212+ # type error: Argument 2 to "AttributesNSImpl" has incompatible type "Dict[Tuple[None, str], str]"; expected "Mapping[Tuple[str, str], str]" [arg-type]
213+ AttributesNSImpl (attr_vals , attr_qnames ), # type: ignore[arg-type]
165214 )
166215 self .writer .endElementNS ((SPARQL_XML_NAMESPACE , "variable" ), "variable" )
167216 self .writer .endElementNS ((SPARQL_XML_NAMESPACE , "head" ), "head" )
168217
169- def write_ask (self , val ) :
218+ def write_ask (self , val : bool ) -> None :
170219 self .writer .startElementNS (
171220 (SPARQL_XML_NAMESPACE , "boolean" ), "boolean" , AttributesNSImpl ({}, {})
172221 )
173222 self .writer .characters (str (val ).lower ())
174223 self .writer .endElementNS ((SPARQL_XML_NAMESPACE , "boolean" ), "boolean" )
175224
176- def write_results_header (self ):
225+ def write_results_header (self ) -> None :
177226 self .writer .startElementNS (
178227 (SPARQL_XML_NAMESPACE , "results" ), "results" , AttributesNSImpl ({}, {})
179228 )
180229 self ._results = True
181230
182- def write_start_result (self ):
231+ def write_start_result (self ) -> None :
183232 self .writer .startElementNS (
184233 (SPARQL_XML_NAMESPACE , "result" ), "result" , AttributesNSImpl ({}, {})
185234 )
186235 self ._resultStarted = True
187236
188- def write_end_result (self ):
237+ def write_end_result (self ) -> None :
189238 assert self ._resultStarted
190239 self .writer .endElementNS ((SPARQL_XML_NAMESPACE , "result" ), "result" )
191240 self ._resultStarted = False
192241
193- def write_binding (self , name , val ):
242+ def write_binding (self , name : Variable , val : Identifier ):
194243 assert self ._resultStarted
195244
196- attr_vals = {
245+ attr_vals : Dict [ Tuple [ Optional [ str ], str ], str ] = {
197246 (None , "name" ): str (name ),
198247 }
199- attr_qnames = {
248+ attr_qnames : Dict [ Tuple [ Optional [ str ], str ], str ] = {
200249 (None , "name" ): "name" ,
201250 }
202251 self .writer .startElementNS (
203252 (SPARQL_XML_NAMESPACE , "binding" ),
204253 "binding" ,
205- AttributesNSImpl (attr_vals , attr_qnames ),
254+ # type error: Argument 1 to "AttributesNSImpl" has incompatible type "Dict[Tuple[None, str], str]"; expected "Mapping[Tuple[str, str], str]"
255+ # type error: Argument 2 to "AttributesNSImpl" has incompatible type "Dict[Tuple[None, str], str]"; expected "Mapping[Tuple[str, str], str]"
256+ AttributesNSImpl (attr_vals , attr_qnames ), # type: ignore[arg-type]
206257 )
207258
208259 if isinstance (val , URIRef ):
@@ -230,7 +281,9 @@ def write_binding(self, name, val):
230281 self .writer .startElementNS (
231282 (SPARQL_XML_NAMESPACE , "literal" ),
232283 "literal" ,
233- AttributesNSImpl (attr_vals , attr_qnames ),
284+ # type error: Argument 1 to "AttributesNSImpl" has incompatible type "Dict[Tuple[Optional[str], str], str]"; expected "Mapping[Tuple[str, str], str]"
285+ # type error: Argument 2 to "AttributesNSImpl" has incompatible type "Dict[Tuple[Optional[str], str], str]"; expected "Mapping[Tuple[str, str], str]"
286+ AttributesNSImpl (attr_vals , attr_qnames ), # type: ignore[arg-type]
234287 )
235288 self .writer .characters (val )
236289 self .writer .endElementNS ((SPARQL_XML_NAMESPACE , "literal" ), "literal" )
@@ -240,7 +293,7 @@ def write_binding(self, name, val):
240293
241294 self .writer .endElementNS ((SPARQL_XML_NAMESPACE , "binding" ), "binding" )
242295
243- def close (self ):
296+ def close (self ) -> None :
244297 if self ._results :
245298 self .writer .endElementNS ((SPARQL_XML_NAMESPACE , "results" ), "results" )
246299 self .writer .endElementNS ((SPARQL_XML_NAMESPACE , "sparql" ), "sparql" )
0 commit comments