def body(self, dtype=str, allow_empty=False, **options): """ Returns the body of the serial element Parameters ---------- dtype : type The type of the returned value allow_empty : bool Whether the body can be empty Returns ------- body : int | float | str The return type of the body """ value = self.visitor.get_body(self._serial_elem, **options) self.unprocessed_body = False if value is None: if allow_empty: return None else: raise NineMLSerializationError( "Missing required body of {} node".format(self.name)) try: return dtype(value) except ValueError: raise NineMLSerializationError( "Cannot convert body of {} node ({}) to {}".format( self.name, value, dtype))
def children(self, nineml_classes, n='*', allow_ref=False, parent_elem=None, **options): """ Extract a child or children of class 'cls' from the serial element 'elem'. The number of expected children is specifed by 'n'. Parameters ---------- nineml_classes : list(type(NineMLObject)) | type(NineMLObject) The type(s) of the children to extract from the element n : int | str Either a number, a tuple of allowable numbers or the wildcards '+' or '*' allow_ref : bool Whether the child is expected to be a allow_ref or not. parent_elem : <serial-elem> The element that the children are nested in options : dict Options that can be passed to specific branches of the element tree (unlikely to be used but included for completeness) Returns ------- children : list(BaseNineMLObject) Child extracted from the element """ children = [] name_map = self._get_name_map(nineml_classes) ref_node_name = self.visitor.node_name(Reference) if parent_elem is None: parent_elem = self._serial_elem if allow_ref: for ref_elem in self.visitor.get_children(parent_elem, Reference.nineml_type, **options): ref = self.visitor.visit(ref_elem, Reference, **options) if self.visitor.node_name(type(ref.target)) in name_map: children.append(ref.target) self.unprocessed_children.discard(ref_node_name) for nineml_type, nineml_cls in name_map.items(): for child_elem in self.visitor.get_children( parent_elem, nineml_type, **options): children.append( self.visitor.visit(child_elem, nineml_cls, **options)) self.unprocessed_children.discard(nineml_type) if n == '+': if not children: raise NineMLSerializationError( "Expected at least 1 child of type {} in {} element". format("|".join(name_map), self.name)) elif n != '*' and len(children) != n: raise NineMLSerializationError( "Expected {} child of type {} in {} element".format( n, "|".join(name_map), self.name)) return children
def visit(self, serial_elem, nineml_cls, allow_ref=False, **options): # @UnusedVariable @IgnorePep8 """ Visits a serial element, unserializes it and returns the resultant 9ML object Parameters ---------- serial_elem : <serial-element> The serial element to unserialize nineml_cls : type The class of the 9ML object to unserialize the serial element to. Will call the 'unserialize_node' method of this class on the serial element allow_ref : bool Whether to attempt to load the serial element as a 9ML reference (depends on the context it is in) options : dict(str, object) Serialization format-specific options for the method """ annotations = self._extract_annotations(serial_elem, **options) # Set any loading options that are saved as annotations self._set_load_options_from_annotations(options, annotations) # Create node to wrap around serial element for convenient access in # "unserialize" class methods node = NodeToUnserialize(self, serial_elem, self.node_name(nineml_cls), **options) # Call the unserialize method of the given class to unserialize the # object if self._version[0] == 1 and hasattr(nineml_cls, 'unserialize_node_v1'): nineml_object = nineml_cls.unserialize_node_v1(node, **options) else: nineml_object = nineml_cls.unserialize_node(node, **options) # Check for unprocessed children/attributes if node.unprocessed_children: raise NineMLSerializationError( "The following unrecognised children '{}' found in the " "node that unserialized to {}".format( "', '".join(node.unprocessed_children), repr(nineml_object))) if node.unprocessed_attr: raise NineMLSerializationError( "The following unrecognised attributes '{}' found in the " "node that unserialized to {}".format( "', '".join(node.unprocessed_attr), nineml_object)) if node.unprocessed_body: raise NineMLSerializationError( "The body of {} node ({}) has not been processed".format( nineml_object, node.unprocessed_body)) # Add annotations to nineml object nineml_object._annotations = annotations return nineml_object
def _get_v1_component_type(self, elem): """ Gets the appropriate component class for a 9MLv1 component. NB in 9ML v1 DynamicProperties, ConnectionRuleProperties and RandomDistributionProperties are all stored in 'Component' elements and can only be differentiated by the component class they reference in the 'Definition' element. """ try: defn_elem = self.get_child(elem, 'Prototype') except NineMLMissingSerializationError: defn_elem = self.get_child(elem, 'Definition') name = self.get_body(defn_elem) try: url = self.get_attr(defn_elem, 'url') if url.startswith('.'): url = os.path.realpath( os.path.join(os.path.dirname(self.url), url)) except KeyError: url = None if url is not None and url != self.url: defn_cls = type(Reference(name, self.document, url=url).target) else: try: elem_type, doc_elem = next( (t, e) for t, e in self.get_all_children(self.root) if self._get_elem_name(e) == name) except StopIteration: raise NineMLSerializationError( "Referenced '{}' component or component class is missing " "from document {} ({})".format( name, self.document.url, "', '".join( self._get_elem_name(e) for _, e in self.get_all_children(self.root)))) if elem_type == 'ComponentClass': defn_cls = self._get_v1_component_class_type(doc_elem) elif elem_type == 'Component': return self._get_v1_component_type(doc_elem) else: raise NineMLSerializationError( "Referenced object '{}' in {} is not component or " "component class it is a {}".format( name, self.document.url, elem_type)) if issubclass(defn_cls, nineml.user.component.Component): cls = defn_cls elif defn_cls == nineml.Dynamics: cls = nineml.DynamicsProperties elif defn_cls == nineml.ConnectionRule: cls = nineml.ConnectionRuleProperties elif defn_cls == nineml.RandomDistribution: cls = nineml.RandomDistributionProperties else: assert False return cls
def extract_version(self): namespace = self.get_namespace(self.root) try: version = nineml_version_re.match(namespace).group(1) except AttributeError: raise NineMLSerializationError( "Provided document '{}' is not in a valid 9ML namespace {}". format(self.url, namespace)) return version
def standardize_version(cls, version): try: version = float(version) except: pass if isinstance(version, float): version = str(version) if isinstance(version, str): try: version = tuple( int(i) for i in version_re.match(version).groups()) except AttributeError: raise NineMLSerializationError( "Invalid NineML version string '{}'".format(version)) if not isinstance(version, (tuple, list)) and len(version) != 3: raise NineMLSerializationError( "Unrecognised version - {}".format(version)) return version
def get_children(self, parent, nineml_type, **options): # @UnusedVariable try: children = parent[nineml_type] except KeyError: children = [] if not isinstance(children, list): raise NineMLSerializationError( "Single child of type '{}' found in {}".format( nineml_type, parent)) return iter(children)
def get_child(self, parent, nineml_type, **options): children = list(self.get_children(parent, nineml_type, **options)) if not children: raise NineMLMissingSerializationError("Expected {} in {}".format( nineml_type, parent)) elif len(children) > 1: raise NineMLSerializationError( "Expected 1 {} in {} but found {}".format( nineml_type, parent, len(children))) return children[0]
def get_children(self, parent, nineml_type, **options): # @UnusedVariable try: children = parent[nineml_type] except KeyError: return iter([]) if not children.attrs[self.MULT_ATTR]: raise NineMLSerializationError( "'{}' is not a multiple element within {}" .format(nineml_type, parent)) return iter(children.values())
def child(self, nineml_object, within=None, reference=None, multiple=False, **options): """ Serialize a single nineml_object. optionally "within" a simple containing tag. Parameters ---------- nineml_object : NineMLObject A type of the children to extract from the element within : str | NoneType The name of the sub-element to extract the child from reference : bool | None Whether the child should be written as a allow_ref or not. If None the ref_style option is used to determine whether it is or not. Note that if reference is not False and there is another member of the container that can be written as a reference then the 'children' method with n=1 should be used instead. options : dict Options that can be passed to specific branches of the element tree (unlikely to be used but included for completeness) Returns ------- child_elem : <serial-elem> The serialized child element or the 'within' element """ if within is not None: if within in self.withins and not multiple: raise NineMLSerializationError( "'{}' already added to serialization".format(within)) serial_elem = self.visitor.create_elem(within, parent=self._serial_elem) self.withins.add(within) else: serial_elem = self._serial_elem # If the visitor doesn't support body content (i.e. all but XML) and # the nineml_object can be flattened into a single attribute if self.visitor.flat_body(nineml_object): mock_node = MockNodeToSerialize() nineml_object.serialize_node(mock_node, **options) self.visitor.set_attr(serial_elem, nineml_object.nineml_type, mock_node._body) child_elem = None else: child_elem = self.visitor.visit(nineml_object, parent=serial_elem, reference=reference, multiple=multiple, **options) return child_elem if within is None else serial_elem
def get_child(self, parent, nineml_type, **options): # @UnusedVariable try: child = parent[nineml_type] except KeyError: raise NineMLMissingSerializationError( "Missing '{}' in parent {}".format(nineml_type, parent)) if not isinstance(child, dict): raise NineMLSerializationError( "Muliple children of type '{}' found in {}".format( nineml_type, parent)) return child
def get_child(self, parent, nineml_type, **options): # @UnusedVariable try: elem = parent[nineml_type] except KeyError: raise NineMLMissingSerializationError( "{} doesn't have a '{}' child".format(parent, nineml_type)) if elem.attrs[self.MULT_ATTR]: raise NineMLSerializationError( "'{}' is a multiple element within {}" .format(nineml_type, parent)) return elem
def from_file(self, file): # @ReservedAssignment try: xml = etree.parse(file) except (etree.LxmlError, IOError) as e: try: name = file.url except AttributeError: name = file.name raise NineMLSerializationError( "Could not read URL or file path '{}': \n{}".format(name, e)) return xml.getroot()
def unserialize_node(cls, node, **options): # @UnusedVariable value = node.child((SingleValue, ArrayValue, RandomDistributionValue), allow_ref=True, **options) units_str = node.attr('units', **options) try: units = node.document[units_str] except KeyError: raise NineMLSerializationError( "Did not find definition of '{}' units in the current " "document.".format(units_str)) return cls(value=value, units=units)
def unserialize_node_v1(cls, node, **options): # @UnusedVariable cr_elem = node.visitor.get_child(node.serial_element, 'ConnectionRule', **options) node.unprocessed_children.remove('ConnectionRule') if list(node.visitor.get_all_children(cr_elem)): raise NineMLSerializationError( "Not expecting {} blocks within 'ConnectionRule' block".format( ', '.join(node.visitor.get_children(cr_elem)))) standard_library = node.visitor.get_attr(cr_elem, 'standard_library', **options) return cls(name=node.attr('name', **options), standard_library=standard_library, parameters=node.children(Parameter, **options))
def get_nineml_class(self, nineml_type, elem, assert_doc_level=True): """ Loads a nineml class from the nineml package. NB that all `DocumentLevelObjects` need to be imported into the root nineml package Parameters ---------- nineml_type : str The name of the nineml class to return elem : <serial-element> The serial element of which to find the class. Only used for component and component-class type objects when using 9MLv1 format. assert_doc_level : bool Whether to check whether the given nineml_type is a document-level object or not. """ try: nineml_cls = getattr(nineml, nineml_type) if assert_doc_level and not issubclass(nineml_cls, DocumentLevelObject): raise NineMLSerializationError( "'{}' element does not correspond to a recognised " "document-level object".format(nineml_cls.__name__)) except AttributeError: nineml_cls = None if self.major_version == 1: if nineml_type == 'ComponentClass': nineml_cls = self._get_v1_component_class_type(elem) elif nineml_type == 'Component': nineml_cls = self._get_v1_component_type(elem) if nineml_cls is None: raise NineMLSerializationError( "Unrecognised element type '{}' found in document".format( nineml_type)) except TypeError: pass return nineml_cls
def unserialize_node(cls, node, **options): # @UnusedVariable if node.name == 'ExternalArrayValue': url = node.attr('url', **options) with contextlib.closing(urlopen(url)) as f: # FIXME: Should use a non-numpy version of this load function values = numpy.loadtxt(f) return cls( values, (node.attr('url', **options), node.attr('mimetype', **options), node.attr('columnName', **options))) else: rows = [] for name, elem in node.visitor.get_all_children( node.serial_element, **options): if name != 'ArrayValueRow': raise NineMLSerializationError( "Unrecognised element {} found in ArrayValue".format( name)) rows.append( (int(node.visitor.get_attr(elem, 'index', **options)), float(node.visitor.get_attr(elem, 'value', **options)))) node.unprocessed_children.discard('ArrayValueRow') sorted_rows = sorted(rows, key=itemgetter(0)) indices, values = list(zip(*sorted_rows)) if indices[0] < 0: raise NineMLSerializationError( "Negative indices found in array rows") if len(list(itertools.groupby(indices))) != len(indices): groups = [list(g) for g in itertools.groupby(indices)] raise NineMLSerializationError( "Duplicate indices ({}) found in array rows".format( ', '.join(str(g[0]) for g in groups if len(g) > 1))) if indices[-1] >= len(indices): raise NineMLSerializationError( "Indices greater or equal to the number of array rows") return cls(values)
def _get_elem_name(self, elem): """ Returns the name of an element. NB Units use 'symbol' as their unique identifier (from LEMS) all other elements use 'name' """ try: try: name = self.get_attr(elem, 'name') except KeyError: name = self.get_attr(elem, 'symbol') except KeyError: raise NineMLSerializationError( "Missing 'name' (or 'symbol') attribute from document " "level object '{}' ('{}')".format( elem, "', '".join(self.get_attr_keys(elem)))) return name
def _get_v1_component_class_type(self, elem): """ Gets the appropriate component-class class for a 9MLv1 component-class. NB in 9ML v1 Dynamics, ConnectionRule and RandomDistribution are all stored in 'ComponentClass' elements and can only be differentiated by the nested 'Dynamics'|'ConnectionRule'|'RandomDistribution' element. """ try: nineml_type = next(n for n, _ in self.get_all_children(elem) if n in ('Dynamics', 'ConnectionRule', 'RandomDistribution')) except StopIteration: raise NineMLSerializationError( "No type defining block in ComponentClass ('{}')".format( "', '".join(n for n, _ in self.get_all_children(elem)))) return getattr(nineml, nineml_type)
def __init__( self, root, version=None, # @ReservedAssignment @IgnorePep8 url=None, document=None, **kwargs): super(XMLUnserializer, self).__init__(root, version=version, url=url, document=document, **kwargs) if self.root is not None: if strip_xmlns(self.root.tag) != self.node_name(Document): raise NineMLSerializationError( "Provided XML document is not enclosed within a '{}' " "element".format(self.node_name(Document)))
def write(url, *nineml_objects, **kwargs): """ Writes NineML objects or single document to file given by a path Parameters ---------- url : str A path on the local file system (either absoluate or relative). The format for the serialization is written in is determined by the extension of the url. register : bool Whether to store the document in the cache after writing version : str | float | int The version to serialize the NineML objects to """ register = kwargs.pop('register', True) # Encapsulate the NineML element in a document if it is not already if len(nineml_objects) == 1 and isinstance(nineml_objects[0], nineml.Document): document = nineml_objects[0] if document.url is not None and document.url != url: document = document.clone() else: document = nineml.Document(*nineml_objects, **kwargs) format = format_from_url(url) # @ReservedAssignment try: Serializer = format_to_serializer[format] except KeyError: raise NineMLSerializationError( "Unrecognised format '{}' in url '{}', can be one of '{}'".format( format, url, "', '".join(list(format_to_serializer.keys())))) if Serializer is None: raise NineMLSerializerNotImportedError( "Cannot write to '{}' as {} serializer cannot be " "imported. Please check the required dependencies are correctly " "installed".format(url, format)) with Serializer.open_file(url) as file: # @ReservedAssignment # file is passed to the serializer for serializations that store # elements dynamically, such as HDF5 serializer = Serializer(document=document, fname=file, **kwargs) serializer.serialize() serializer.to_file(serializer.root, file, **kwargs) if register: document._url = url nineml.Document.registry[url] = (weakref.ref(document), time.ctime(os.path.getmtime(url)))
def attr(self, name, dtype=str, in_body=False, **options): """ Extract an attribute from the serial element ``elem``. Parameters ---------- name : str The name of the attribute to retrieve. dtype : type The type of the returned value in_body : bool Whether the attribute is within the body of a sub-element (for serializations that support body elements, e.g. XML) options : dict Options that can be passed to specific branches of the element tree (unlikely to be used but included for completeness) Returns ------- attr : (int | str | float | bool) The attribute to retrieve """ try: if in_body and self.visitor.supports_bodies: attr_elem = self.visitor.get_child(self._serial_elem, name, **options) self.unprocessed_children.remove(name) value = self.visitor.get_body(attr_elem, **options) else: value = self.visitor.get_attr(self._serial_elem, name, **options) self.unprocessed_attr.discard(name) try: return dtype(value) except ValueError: raise NineMLSerializationError( "Cannot convert '{}' attribute of {} node ({}) to {}". format(name, self.name, value, dtype)) except KeyError: try: return options['default'] except KeyError: raise NineMLMissingSerializationError( "Node {} does not have required attribute '{}'".format( self.name, name))
def serialize( nineml_object, format=DEFAULT_FORMAT, version=DEFAULT_VERSION, # @ReservedAssignment @IgnorePep8 document=None, to_str=False, **kwargs): """ Serializes a NineML object into a serialized element Parameters ---------- nineml_object : BaseNineMLObject The object to serialize format : str The name of the format (which matches a key format_to_serializer) version : str | float | int The version to serialize the NineML objects to document : Document The document to write local references to to_str : bool To serialize to a string instead of a serial element. """ if isinstance(nineml_object, nineml.Document): if document is not None and document is not nineml_object: raise NineMLSerializationError( "Supplied 'document' kwarg ({}) is not the same as document to" " serialize ({})".format(document, nineml_object)) document = nineml_object Serializer = format_to_serializer[format] if isinstance(nineml_object, DocumentLevelObject) and document is None: document = Document(nineml_object) serializer = Serializer(version=version, document=document, **kwargs) serial_elem = serializer.visit(nineml_object, **kwargs) if to_str: serialized = serializer.to_str(serial_elem, nineml_type=nineml_object.nineml_type, **kwargs) else: serialized = serializer.to_elem(serial_elem, nineml_type=nineml_object.nineml_type, **kwargs) return serialized
def create_elem(self, name, parent, namespace=None, multiple=False, **options): # @UnusedVariable @IgnorePep8 if multiple: if name not in parent: parent.create_group(name) parent[name].attrs[self.MULT_ATTR] = True # Add a new group named by the next available index new_index = len(parent[name]) elem = parent[name].create_group(str(new_index)) else: if name in parent: raise NineMLSerializationError( "'{}' already exists in parent ({}) when creating " "singleton element".format(name, parent)) elem = parent.create_group(name) elem.attrs[self.MULT_ATTR] = False if namespace is not None: elem.attrs[self.NS_ATTR] = namespace return elem
def create_elem( self, name, parent, namespace=None, multiple=False, # @UnusedVariable @IgnorePep8 **options): # @UnusedVariable elem = OrderedDict() if multiple: if name not in parent: parent[name] = [] parent[name].append(elem) else: if name in parent: raise NineMLSerializationError( "'{}' already exists in parent ({}) when creating " "singleton element".format(name, parent)) parent[name] = elem if namespace is not None: self.set_attr(elem, self.NS_ATTR, namespace, **options) return elem
def read(url, relative_to=None, reload=False, register=True, **kwargs): # @ReservedAssignment @IgnorePep8 """ Reads a NineML document from the given url or file system path and returns a Document object. Parameters ---------- url : str An url or path on the local file system (either absoluate or relative). The format the file is read-from/written-to is determined by the extension of the url/filename. If a '#' is in the ``url`` string then the part of the string after the '#' is treated as the name of the object to return from the Document. relative_to : URL | None The URL/file path to resolve relative file paths from reload : bool Whether to reload the document from file if it is already in the cache or not. register : bool Whether to store the document in the cache after it is read """ if not isinstance(url, basestring): raise NineMLIOError( "{} is not a valid URL (it is not even a string)".format(url)) if '#' in url: url, name = url.split('#') else: name = None if file_path_re.match(url) is not None: if url.startswith('.'): if relative_to is None: relative_to = os.getcwd() url = os.path.abspath(os.path.join(relative_to, url)) mtime = time.ctime(os.path.getmtime(url)) elif url_re.match(url) is not None: mtime = None # Cannot load mtime of a general URL else: raise NineMLIOError( "{} is not a valid URL or file path (NB: relative file paths must " "start with './')".format(url)) if reload: nineml.Document.registry.pop(url, None) try: # Try to use cached document in registry doc_ref, loaded_mtime = nineml.Document.registry[url] if loaded_mtime != mtime or doc_ref() is None or not register: raise NineMLReloadDocumentException() doc = doc_ref() except (KeyError, NineMLReloadDocumentException): # Reload from file # Get the unserializer based on the url extension format = format_from_url(url) # @ReservedAssignment try: Unserializer = format_to_unserializer[format] except KeyError: raise NineMLSerializationError( "Unrecognised format '{}' in url '{}', can be one of '{}'". format(format, url, "', '".join(list(format_to_unserializer.keys())))) if Unserializer is None: raise NineMLSerializerNotImportedError( "Cannot write to '{}' as {} serializer cannot be imported. " "Please check the required dependencies are correctly " "installed".format(url, format)) if file_path_re.match(url) is not None: file = open(url) # @ReservedAssignment elif url_re.match(url) is not None: file = urlopen(url) # @ReservedAssignment else: raise NineMLIOError("Unrecognised url '{}'".format(url)) with contextlib.closing(file): doc = Unserializer(root=file, url=url, **kwargs).unserialize() if register: nineml.Document.registry[url] = weakref.ref(doc), mtime if name is not None: nineml_obj = doc[name] else: nineml_obj = doc return nineml_obj
def child(self, nineml_classes, within=None, allow_ref=False, allow_none=False, allow_within_attrs=False, **options): """ Extract a child of class ``cls`` from the serial element ``elem``. The number of expected children is specifed by ``n``. Parameters ---------- nineml_classes : list(type(NineMLObject)) | type(NineMLObject) The type(s) of the children to extract from the element within : str | NoneType The name of the sub-element to extract the child from allow_ref : bool | 'only' Whether the child is can be a allow_ref or not. If 'only' then only allow_refs will be found. Note if there are more than one type references possible in a given container then the 'children' method with n=1 should be used instead. allow_none : bool Whether the child is allowed to be missing, in which case None will be returned allow_within_attrs : bool Allow 'within' containers to have attributes options : dict Options that can be passed to specific branches of the element tree (unlikely to be used but included for completeness) Returns ------- child : BaseNineMLObject Child extracted from the element """ # Create a dictionary mapping nineml_types to the valid nineml classes name_map = self._get_name_map(nineml_classes) # If the child is nested within another element, find that containing # element and use it as the parent elem if within is not None: try: parent_elem = self.visitor.get_child(self._serial_elem, within, **options) # If the within element is found it cannot be empty allow_none = False # Check the 'within' element for attributes (it typically # shouldn't have any) if not allow_within_attrs: if any(not a.startswith('@') for a in self.visitor.get_attr_keys(parent_elem)): raise NineMLSerializationError( "'{}' elements can not have attributes ('{}')". format( within, "', '".join( self.visitor.get_attr_keys(parent_elem)))) except NineMLMissingSerializationError: if allow_none: return None else: raise self.unprocessed_children.discard(within) else: parent_elem = self._serial_elem # Check to see if the child has been flattened into an attribute (for # classes that are serialized to a single body element in formats that # support body content, i.e. XML, such as SingleValue) for nineml_cls in name_map.values(): if self.visitor.flat_body(nineml_cls): try: mock_node = MockNodeToUnSerialize( self.visitor.get_attr(parent_elem, nineml_cls.nineml_type, **options)) child = nineml_cls.unserialize_node(mock_node, **options) self.unprocessed_attr.discard(nineml_cls.nineml_type) return child except KeyError: pass child = None # Check to see if there is a valid reference to the child element if allow_ref: try: ref_elem = self.visitor.get_child(parent_elem, Reference.nineml_type) except NineMLMissingSerializationError: ref_elem = None if ref_elem is not None: ref = self.visitor.visit(ref_elem, Reference, **options) if any(isinstance(ref.target, c) for c in name_map.values()): child = ref.target self.unprocessed_children.discard(Reference.nineml_type) # If there were no valid references to the child element look for # the child element itself if child is None: if allow_ref == 'only': raise NineMLMissingSerializationError( "Missing reference to '{}' type elements in {}{}".format( "', '".join(name_map), ('{} of '.format(within) if within is not None else ''), self.name)) child_elems = [] for nineml_type, cls in name_map.items(): try: child_elems.append( (cls, self.visitor.get_child(parent_elem, nineml_type))) child_nineml_type = nineml_type except NineMLMissingSerializationError: pass if len(child_elems) > 1: raise NineMLUnexpectedMultipleSerializationError( "Multiple {} children found within {} (found {})".format( '|'.join(name_map), self.name, ', '.join(child_elems))) elif not child_elems: raise NineMLMissingSerializationError( "No '{}' child found within {}".format( '|'.join(name_map), self.name)) child_cls, child_elem = child_elems[0] child = self.visitor.visit(child_elem, child_cls, **options) self.unprocessed_children.discard(child_nineml_type) return child
def to_file(self, serial_elem, file, **options): # @UnusedVariable @IgnorePep8 @ReservedAssignment if file.name != self._file.filename: raise NineMLSerializationError( "Can only write elems to the file that is named in the " "__init__ method as the file is written to as the elements " "are serialized.")
def _get_reference_url(self, nineml_object, reference=None, ref_style='prefer', absolute_refs=False, **options): # @UnusedVariable @IgnorePep8 """ Determine whether to write the elemnt as a reference or not depending on whether it needs to be, as determined by ``reference``, e.g. in the case of populations referenced from projections, or whether the user would prefer it to be, ``ref_style``. If neither kwarg is set whether the element is written as a reference is determined by whether it has already been added to a document or not. Parameters ---------- nineml_object: BaseNineMLObject The NineML object to write as a reference reference : bool | None Whether the child should be written as a reference or not. If None the ref_style option is used to determine whether it is or not. ref_style : (None | 'prefer' | 'force' | 'inline' | 'force_inline' | 'local') The strategy used to write references if they are not explicitly required by the serialization (i.e. for Projections and Selections) absolute_refs : bool Whether to write references using relative or absolute paths Returns ------- url : bool | None The url used for the reference if required. If None, the local document should be used, if False then the object shouldn't be written as a reference """ if ref_style == 'force': write_ref = True elif ref_style == 'force_inline': write_ref = False elif reference is not None: write_ref = reference elif ref_style == 'prefer': # Write the element as a reference write_ref = True elif ref_style == 'inline': # Write the element inline write_ref = False elif ref_style == 'local': write_ref = True elif ref_style is None: # No preference is supplied so the element will be written as # a reference if it was loaded from a reference write_ref = nineml_object.document is not None else: raise NineMLSerializationError( "Unrecognised ref_style '{}'".format(ref_style)) # If the element is to be written as a reference and it is not in the # current document add it if write_ref: if (nineml_object.document is None or nineml_object.document.url == self.document.url or ref_style == 'local'): # A local reference url = None else: url = nineml_object.document.url # If absolute_refs is not provided (recommended) the relative path # is used if url is not None and not absolute_refs: url = os.path.relpath(nineml_object.document.url, os.path.dirname(self.document.url)) # Ensure the relative path starts with the explicit # current directory '.' if not url.startswith('.'): url = './' + url else: url = False return url
def __init__( self, root, version=None, url=None, class_map=None, # @ReservedAssignment @IgnorePep8 document=None): if class_map is None: class_map = {} if document is None: document = Document(unserializer=self, url=url) self._url = url # Get root elem either from kwarg or file handle if hasattr(root, 'url'): self._root = self.from_urlfile(root) elif is_file_handle(root): self._root = self.from_file(root) elif isinstance(root, basestring): self._root = self.from_str(root) else: self._root = root # Get the version from the root element if version is not None: version = self.standardize_version(version) else: if self.root is None: raise NineMLSerializationError( "Version needs to be provided if root or file is not") extracted_version = self.standardize_version( self.extract_version()) if (version is not None and extracted_version != self.standardize_version(version)): raise NineMLSerializationError( "Explicit version {} does not match that of loaded " "root {}".format(extracted_version, version)) version = extracted_version super(BaseUnserializer, self).__init__(version, document=document) # Prepare elements in document for lazy loading self._doc_elems = {} if self.root is not None: self._annotation_elem = None for nineml_type, elem in self.get_all_children(self.root): # Strip out document level annotations if nineml_type == self.node_name(Annotations): if self._annotation_elem is not None: raise NineMLSerializationError( "Multiple annotations tags found in document") self._annotation_elem = elem continue name = self._get_elem_name(elem) # Check for duplicates if name in self._doc_elems: raise NineMLSerializationError( "Duplicate elements for name '{}' found in document". format(name)) # Get the 9ML class corresponding to the element name try: elem_cls = class_map[nineml_type] except KeyError: elem_cls = self.get_nineml_class(nineml_type, elem) self._doc_elems[name] = (elem, elem_cls) self._loaded_elems = [] # keeps track of loaded doc elements