コード例 #1
0
ファイル: nodes.py プロジェクト: apdavison/lib9ML
    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))
コード例 #2
0
ファイル: nodes.py プロジェクト: apdavison/lib9ML
    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
コード例 #3
0
ファイル: visitors.py プロジェクト: apdavison/lib9ML
    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
コード例 #4
0
ファイル: visitors.py プロジェクト: apdavison/lib9ML
 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
コード例 #5
0
ファイル: visitors.py プロジェクト: apdavison/lib9ML
 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
コード例 #6
0
ファイル: visitors.py プロジェクト: apdavison/lib9ML
 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
コード例 #7
0
ファイル: dict.py プロジェクト: apdavison/lib9ML
 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)
コード例 #8
0
 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]
コード例 #9
0
ファイル: hdf5.py プロジェクト: apdavison/lib9ML
 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())
コード例 #10
0
ファイル: nodes.py プロジェクト: apdavison/lib9ML
    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
コード例 #11
0
ファイル: dict.py プロジェクト: apdavison/lib9ML
 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
コード例 #12
0
ファイル: hdf5.py プロジェクト: apdavison/lib9ML
 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
コード例 #13
0
 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()
コード例 #14
0
 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)
コード例 #15
0
 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))
コード例 #16
0
ファイル: visitors.py プロジェクト: apdavison/lib9ML
    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
コード例 #17
0
ファイル: values.py プロジェクト: apdavison/lib9ML
 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)
コード例 #18
0
ファイル: visitors.py プロジェクト: apdavison/lib9ML
 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
コード例 #19
0
ファイル: visitors.py プロジェクト: apdavison/lib9ML
 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)
コード例 #20
0
 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)))
コード例 #21
0
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)))
コード例 #22
0
ファイル: nodes.py プロジェクト: apdavison/lib9ML
    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))
コード例 #23
0
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
コード例 #24
0
ファイル: hdf5.py プロジェクト: apdavison/lib9ML
 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
コード例 #25
0
ファイル: dict.py プロジェクト: apdavison/lib9ML
 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
コード例 #26
0
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
コード例 #27
0
ファイル: nodes.py プロジェクト: apdavison/lib9ML
    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
コード例 #28
0
ファイル: hdf5.py プロジェクト: apdavison/lib9ML
 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.")
コード例 #29
0
ファイル: visitors.py プロジェクト: apdavison/lib9ML
    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
コード例 #30
0
ファイル: visitors.py プロジェクト: apdavison/lib9ML
 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