33import os
44from typing import Dict , Callable , List , Optional , Any , Union
55
6+ # import typing for typing.Sequence, which we don't want to confuse
7+ # with sbol3.Sequence
8+ import typing
9+
610import pyshacl
711import rdflib
812
@@ -31,7 +35,7 @@ class Document:
3135
3236 @staticmethod
3337 def register_builder (type_uri : str ,
34- builder : Callable [[str , str ], SBOLObject ]) -> None :
38+ builder : Callable [[str , str ], Identified ]) -> None :
3539 """A builder function will be called with an identity and a
3640 keyword argument type_uri.
3741
@@ -104,7 +108,6 @@ def _build_object(self, identity: str, types: List[str]) -> Optional[Identified]
104108 SBOL_TOP_LEVEL : CustomTopLevel
105109 }
106110 sbol_type = sbol_types [0 ]
107- result = None
108111 if sbol_type in extension_types :
109112 # Build an extension object
110113 types .remove (sbol_type )
@@ -260,7 +263,7 @@ def read_string(self, data: str, file_format: str) -> None:
260263 graph .parse (data = data , format = file_format )
261264 return self ._parse_graph (graph )
262265
263- def add (self , obj : TopLevel ) -> None :
266+ def _add (self , obj : TopLevel ) -> TopLevel :
264267 """Add objects to the document.
265268 """
266269 if not isinstance (obj , TopLevel ):
@@ -278,6 +281,43 @@ def add(self, obj: TopLevel) -> None:
278281 def assign_document (x : Identified ):
279282 x .document = self
280283 obj .traverse (assign_document )
284+ return obj
285+
286+ def _add_all (self , objects : typing .Sequence [TopLevel ]) -> typing .Sequence [TopLevel ]:
287+ # Perform type check of all objects.
288+ # We do this to avoid finding out part way through that an
289+ # object can't be added. That would leave the document in an
290+ # unknown state.
291+ for obj in objects :
292+ if not isinstance (obj , TopLevel ):
293+ if isinstance (obj , Identified ):
294+ raise TypeError (f'{ obj .identity } is not a TopLevel object' )
295+ else :
296+ raise TypeError (f'{ repr (obj )} is not a TopLevel object' )
297+
298+ # Dispatch to Document._add to add the individual objects
299+ for obj in objects :
300+ self ._add (obj )
301+ # return the passed argument
302+ return objects
303+
304+ def add (self , objects : Union [TopLevel , typing .Sequence [TopLevel ]]) -> Union [TopLevel , typing .Sequence [TopLevel ]]:
305+ # objects must be TopLevel or iterable. If neither, raise a TypeError.
306+ #
307+ # Note: Python documentation for collections.abc says "The only
308+ # reliable way to determine whether an object is iterable is to
309+ # call iter(obj)." `iter` will raise TypeError if the object is
310+ # not iterable
311+ if not isinstance (objects , TopLevel ):
312+ try :
313+ iter (objects )
314+ except TypeError :
315+ raise TypeError ('argument must be either TopLevel or Iterable' )
316+ # Now dispatch to the appropriate method
317+ if isinstance (objects , TopLevel ):
318+ return self ._add (objects )
319+ else :
320+ return self ._add_all (objects )
281321
282322 def _find_in_objects (self , search_string : str ) -> Optional [Identified ]:
283323 # TODO: implement recursive search
0 commit comments