1
1
import 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
+ )
3
17
from xml .dom import XML_NAMESPACE
4
18
from xml .sax .saxutils import XMLGenerator
5
19
from xml .sax .xmlreader import AttributesNSImpl
6
20
7
21
from rdflib import BNode , Literal , URIRef , Variable
8
- from rdflib .compat import etree
9
22
from 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
10
32
11
33
SPARQL_XML_NAMESPACE = "http://www.w3.org/2005/sparql-results#"
12
34
RESULTS_NS_ET = "{%s}" % SPARQL_XML_NAMESPACE
27
49
28
50
class XMLResultParser (ResultParser ):
29
51
# 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]
31
53
return XMLResult (source )
32
54
33
55
34
56
class 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 )
43
78
44
79
boolean = tree .find (RESULTS_NS_ET + "boolean" )
45
80
results = tree .find (RESULTS_NS_ET + "results" )
@@ -56,8 +91,18 @@ def __init__(self, source, content_type: Optional[str] = None):
56
91
if type_ == "SELECT" :
57
92
self .bindings = []
58
93
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
59
99
r = {}
60
100
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
61
106
# type error: error: Argument 1 to "Variable" has incompatible type "Union[str, None, Any]"; expected "str"
62
107
# NOTE on type error: Element.get() can return None, and
63
108
# this will invariably fail if passed into Variable
@@ -80,7 +125,7 @@ def __init__(self, source, content_type: Optional[str] = None):
80
125
self .askAnswer = boolean .text .lower ().strip () == "true" # type: ignore[union-attr]
81
126
82
127
83
- def parseTerm (element ) :
128
+ def parseTerm (element : xml_etree . Element ) -> Union [ URIRef , Literal , BNode ] :
84
129
"""rdflib object (Literal, URIRef, BNode) for the given
85
130
elementtree element"""
86
131
tag , text = element .tag , element .text
@@ -90,15 +135,17 @@ def parseTerm(element):
90
135
datatype = None
91
136
lang = None
92
137
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]
94
140
elif element .get ("{%s}lang" % XML_NAMESPACE , None ):
95
141
lang = element .get ("{%s}lang" % XML_NAMESPACE )
96
142
97
143
ret = Literal (text , datatype = datatype , lang = lang )
98
144
99
145
return ret
100
146
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]
102
149
elif tag == RESULTS_NS_ET + "bnode" :
103
150
return BNode (text )
104
151
else :
@@ -109,14 +156,14 @@ class XMLResultSerializer(ResultSerializer):
109
156
def __init__ (self , result ):
110
157
ResultSerializer .__init__ (self , result )
111
158
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 :
114
160
writer = SPARQLXMLWriter (stream , encoding )
115
161
if self .result .type == "ASK" :
116
162
writer .write_header ([])
117
163
writer .write_ask (self .result .askAnswer )
118
164
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]
120
167
writer .write_results_header ()
121
168
for b in self .result .bindings :
122
169
writer .write_start_result ()
@@ -134,7 +181,7 @@ class SPARQLXMLWriter:
134
181
Python saxutils-based SPARQL XML Writer
135
182
"""
136
183
137
- def __init__ (self , output , encoding = "utf-8" ):
184
+ def __init__ (self , output : IO , encoding : str = "utf-8" ):
138
185
writer = XMLGenerator (output , encoding )
139
186
writer .startDocument ()
140
187
writer .startPrefixMapping ("" , SPARQL_XML_NAMESPACE )
@@ -147,7 +194,7 @@ def __init__(self, output, encoding="utf-8"):
147
194
self ._encoding = encoding
148
195
self ._results = False
149
196
150
- def write_header (self , allvarsL ) :
197
+ def write_header (self , allvarsL : Sequence [ Variable ]) -> None :
151
198
self .writer .startElementNS (
152
199
(SPARQL_XML_NAMESPACE , "head" ), "head" , AttributesNSImpl ({}, {})
153
200
)
@@ -161,48 +208,52 @@ def write_header(self, allvarsL):
161
208
self .writer .startElementNS (
162
209
(SPARQL_XML_NAMESPACE , "variable" ),
163
210
"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]
165
214
)
166
215
self .writer .endElementNS ((SPARQL_XML_NAMESPACE , "variable" ), "variable" )
167
216
self .writer .endElementNS ((SPARQL_XML_NAMESPACE , "head" ), "head" )
168
217
169
- def write_ask (self , val ) :
218
+ def write_ask (self , val : bool ) -> None :
170
219
self .writer .startElementNS (
171
220
(SPARQL_XML_NAMESPACE , "boolean" ), "boolean" , AttributesNSImpl ({}, {})
172
221
)
173
222
self .writer .characters (str (val ).lower ())
174
223
self .writer .endElementNS ((SPARQL_XML_NAMESPACE , "boolean" ), "boolean" )
175
224
176
- def write_results_header (self ):
225
+ def write_results_header (self ) -> None :
177
226
self .writer .startElementNS (
178
227
(SPARQL_XML_NAMESPACE , "results" ), "results" , AttributesNSImpl ({}, {})
179
228
)
180
229
self ._results = True
181
230
182
- def write_start_result (self ):
231
+ def write_start_result (self ) -> None :
183
232
self .writer .startElementNS (
184
233
(SPARQL_XML_NAMESPACE , "result" ), "result" , AttributesNSImpl ({}, {})
185
234
)
186
235
self ._resultStarted = True
187
236
188
- def write_end_result (self ):
237
+ def write_end_result (self ) -> None :
189
238
assert self ._resultStarted
190
239
self .writer .endElementNS ((SPARQL_XML_NAMESPACE , "result" ), "result" )
191
240
self ._resultStarted = False
192
241
193
- def write_binding (self , name , val ):
242
+ def write_binding (self , name : Variable , val : Identifier ):
194
243
assert self ._resultStarted
195
244
196
- attr_vals = {
245
+ attr_vals : Dict [ Tuple [ Optional [ str ], str ], str ] = {
197
246
(None , "name" ): str (name ),
198
247
}
199
- attr_qnames = {
248
+ attr_qnames : Dict [ Tuple [ Optional [ str ], str ], str ] = {
200
249
(None , "name" ): "name" ,
201
250
}
202
251
self .writer .startElementNS (
203
252
(SPARQL_XML_NAMESPACE , "binding" ),
204
253
"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]
206
257
)
207
258
208
259
if isinstance (val , URIRef ):
@@ -230,7 +281,9 @@ def write_binding(self, name, val):
230
281
self .writer .startElementNS (
231
282
(SPARQL_XML_NAMESPACE , "literal" ),
232
283
"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]
234
287
)
235
288
self .writer .characters (val )
236
289
self .writer .endElementNS ((SPARQL_XML_NAMESPACE , "literal" ), "literal" )
@@ -240,7 +293,7 @@ def write_binding(self, name, val):
240
293
241
294
self .writer .endElementNS ((SPARQL_XML_NAMESPACE , "binding" ), "binding" )
242
295
243
- def close (self ):
296
+ def close (self ) -> None :
244
297
if self ._results :
245
298
self .writer .endElementNS ((SPARQL_XML_NAMESPACE , "results" ), "results" )
246
299
self .writer .endElementNS ((SPARQL_XML_NAMESPACE , "sparql" ), "sparql" )
0 commit comments