66from collections import defaultdict
77from functools import cmp_to_key
88
9+ from rdflib .graph import Graph
910from rdflib .term import BNode , Literal , URIRef
1011from rdflib .exceptions import Error
1112from rdflib .serializer import Serializer
1213from rdflib .namespace import RDF , RDFS
14+ from io import TextIOWrapper
15+ from typing import IO , Dict , Optional
1316
1417__all__ = ["RecursiveSerializer" , "TurtleSerializer" ]
1518
@@ -44,10 +47,13 @@ class RecursiveSerializer(Serializer):
4447 indentString = " "
4548 roundtrip_prefixes = ()
4649
47- def __init__ (self , store ):
50+ def __init__ (self , store : Graph ):
4851
4952 super (RecursiveSerializer , self ).__init__ (store )
50- self .stream = None
53+ # TODO FIXME: Ideally stream should be optional, but nothing treats it
54+ # as such, so least weird solution is to just type it as not optional
55+ # even thoug it can sometimes be null.
56+ self .stream : IO [str ] = None # type: ignore[assignment]
5157 self .reset ()
5258
5359 def addNamespace (self , prefix , uri ):
@@ -166,9 +172,9 @@ def indent(self, modifier=0):
166172 """Returns indent string multiplied by the depth"""
167173 return (self .depth + modifier ) * self .indentString
168174
169- def write (self , text ):
170- """Write text in given encoding. """
171- self .stream .write (text . encode ( self . encoding , "replace" ) )
175+ def write (self , text : str ):
176+ """Write text"""
177+ self .stream .write (text )
172178
173179
174180SUBJECT = 0
@@ -184,15 +190,15 @@ class TurtleSerializer(RecursiveSerializer):
184190 short_name = "turtle"
185191 indentString = " "
186192
187- def __init__ (self , store ):
188- self ._ns_rewrite = {}
193+ def __init__ (self , store : Graph ):
194+ self ._ns_rewrite : Dict [ str , str ] = {}
189195 super (TurtleSerializer , self ).__init__ (store )
190196 self .keywords = {RDF .type : "a" }
191197 self .reset ()
192- self .stream = None
198+ self .stream : TextIOWrapper = None # type: ignore[assignment]
193199 self ._spacious = _SPACIOUS_OUTPUT
194200
195- def addNamespace (self , prefix , namespace ):
201+ def addNamespace (self , prefix : str , namespace : str ):
196202 # Turtle does not support prefix that start with _
197203 # if they occur in the graph, rewrite to p_blah
198204 # this is more complicated since we need to make sure p_blah
@@ -223,36 +229,60 @@ def reset(self):
223229 self ._started = False
224230 self ._ns_rewrite = {}
225231
226- def serialize (self , stream , base = None , encoding = None , spacious = None , ** args ):
232+ def _serialize_init (
233+ self ,
234+ stream : IO [bytes ],
235+ base : Optional [str ],
236+ encoding : Optional [str ],
237+ spacious : Optional [bool ],
238+ ) -> None :
227239 self .reset ()
228- self .stream = stream
240+ if encoding is not None :
241+ self .encoding = encoding
242+ self .stream = TextIOWrapper (
243+ stream , self .encoding , errors = "replace" , write_through = True
244+ )
229245 # if base is given here, use that, if not and a base is set for the graph use that
230246 if base is not None :
231247 self .base = base
232248 elif self .store .base is not None :
233249 self .base = self .store .base
234-
235250 if spacious is not None :
236251 self ._spacious = spacious
237252
238- self .preprocess ()
239- subjects_list = self .orderSubjects ()
240-
241- self .startDocument ()
242-
243- firstTime = True
244- for subject in subjects_list :
245- if self .isDone (subject ):
246- continue
247- if firstTime :
248- firstTime = False
249- if self .statement (subject ) and not firstTime :
250- self .write ("\n " )
251-
252- self .endDocument ()
253- stream .write ("\n " .encode ("latin-1" ))
254-
255- self .base = None
253+ def _serialize_end (self ) -> None :
254+ self .stream .flush ()
255+ self .stream .detach ()
256+ self .stream = None # type: ignore[assignment]
257+
258+ def serialize (
259+ self ,
260+ stream : IO [bytes ],
261+ base : Optional [str ] = None ,
262+ encoding : Optional [str ] = None ,
263+ spacious : Optional [bool ] = None ,
264+ ** args ,
265+ ):
266+ self ._serialize_init (stream , base , encoding , spacious )
267+ try :
268+ self .preprocess ()
269+ subjects_list = self .orderSubjects ()
270+
271+ self .startDocument ()
272+
273+ firstTime = True
274+ for subject in subjects_list :
275+ if self .isDone (subject ):
276+ continue
277+ if firstTime :
278+ firstTime = False
279+ if self .statement (subject ) and not firstTime :
280+ self .write ("\n " )
281+
282+ self .endDocument ()
283+ self .stream .write ("\n " )
284+ finally :
285+ self ._serialize_end ()
256286
257287 def preprocessTriple (self , triple ):
258288 super (TurtleSerializer , self ).preprocessTriple (triple )
0 commit comments