Ejemplo n.º 1
0
    def get(self, nsid: Union[str, Nsid]) -> NamespaceNodeBase:
        """
        Description:
            return a node object specified by NSID
        """
        log = LoggerAdapter(logger,
                            dict(name_ext=f"{self.__class__.__name__}.get"))
        self._validate_namespace_nsid_head(nsid)
        _nsid_ = Nsid(nsid)
        current_node = self.root
        nsid_segments = list_nsid_segments(nsid)[
            1:]  #- skip initial root segment

        n = 0
        while current_node.nsid != _nsid_:
            log.debug(f"target {_nsid_=} != {current_node.nsid=}")
            try:
                nsid_segment = nsid_segments[n]
            except IndexError as err:
                raise NamespaceInternalError(
                    f"while looking for nsid \"{_nsid_}\", ran out of nsid_segments: {nsid_segments} at index {n}"
                ) from err
            try:
                current_node = getattr(current_node, nsid_segment)
                if not isinstance(current_node, NamespaceNodeBase):
                    warn(
                        "Rogue node type detected in the namespace. Will most likely cause errors."
                    )
            except AttributeError:
                raise NamespaceLookupError(
                    f"{current_node} has no attribute named '{nsid_segment}'")
            n += 1
        log.debug(f"found {_nsid_=} == {current_node.nsid=}")
        return current_node
Ejemplo n.º 2
0
    def add_exactly_one(self,
                        nsid: Union[str, Nsid],
                        node_factory=NamespaceNodeBase,
                        *args,
                        **kwargs):
        """
            Description:
                add one and only one new node to this namespace and return the new node
            Input:
                nsid: nsid of new node to create
                node_factory: factory method to call to create the node
                *args, **kwargs: passed through to node_factory
            Output:
                exactly one new node created or raises an exception if:
                    - it looks like it will create more than one node: NamespaceLookupError
                    or
                    - if it didn't look like it but somehow it did: NamespaceInternalError
        """
        nsid_segments = list_nsid_segments(nsid, skip_root=True)
        if len(nsid_segments) > 1:
            try:
                #- if the parent exists, this will add only one
                self.get(get_parent_nsid(nsid))
            except NamespaceLookupError:
                err_msg = f"add_exactly_one: error: input \"{nsid}\" would create more" +\
                          f" than one new node. ({len(nsid_segments)} > 1)"
                raise ValueError(err_msg)

        new_nodes = self.add(nsid, node_factory, *args, **kwargs)

        if len(new_nodes) > 1:
            raise NamespaceInternalError(
                f"created more than one new node! ({new_nodes})")

        return new_nodes[0]
Ejemplo n.º 3
0
    def add(self,
            nsid: Union[str, Nsid],
            node_factory: Union[callable, None] = None,
            *args,
            **kwargs) -> List[NamespaceNodeBase]:
        """
            Description:
                add a new nsid to this namespace
            Input:
                nsid: the nsid to create in this namespace
                node_factory: what factory to use to create the node
                    NOTE: parent nodes will be created with this namespaces default_node_factory method
                *args: passed into the node_factory as args
                **kwargs: passed into the node_factory as kwargs
        """
        log = LoggerAdapter(logger,
                            dict(name_ext=f"{self.__class__.__name__}.add"))
        if find_common_prefix(str(self.root.nsid), nsid) is None:
            err_msg = f'child nsid ({nsid}) must share a common prefix with Namespace root node nsid'
            err_msg += f'({str(self.root.nsid)})'
            raise InvalidNsidError(err_msg)
        _nsid = Nsid(nsid)

        if node_factory is None:
            node_factory = self.default_node_factory

        #- find the deepest existing ancestor of the node we wish to add
        deepest_ancestor = self.root
        for current_nsid in get_nsid_ancestry(nsid):
            try:
                deepest_ancestor = self.get(current_nsid)
            except NamespaceLookupError:
                break
        else:
            #- we never hit break, so every single nsid in the entire ancestry exists, including the one we want to add
            raise NamespaceCollisionError(
                f'A node with the nsid "{nsid}" already exists in the namespace.'
            )

        #- if here, we have a valid deepest ancestor to start from
        child_nsid_tail = strip_common_prefix(str(deepest_ancestor.nsid),
                                              str(_nsid))[1]
        common_prefix = find_common_prefix(str(deepest_ancestor.nsid),
                                           str(_nsid))
        created_nodes = list(
        )  #- keep track of all the nodes we create to return them
        nsid_segments = list_nsid_segments(child_nsid_tail)
        for i, child_attribute_name in enumerate(nsid_segments):
            new_node_nsid = make_child_nsid(str(deepest_ancestor.nsid),
                                            child_attribute_name)
            #- use the node factory on the last node only
            if i == len(nsid_segments) - 1:
                log.debug(f"creating node: {node_factory=})")

                try:
                    new_node = node_factory(*args,
                                            nsid=new_node_nsid,
                                            namespace=self,
                                            **kwargs)
                except TypeError as e:
                    raise TypeError(
                        f"node_factory failed to create node: {str(e)}") from e

            else:
                new_node = self.default_node_factory(nsid=new_node_nsid,
                                                     namespace=self)

            created_nodes.append(new_node)
            setattr(deepest_ancestor, child_attribute_name, new_node)
            deepest_ancestor = getattr(deepest_ancestor, child_attribute_name)

        return created_nodes
Ejemplo n.º 4
0
 def test_list_nsid_segments2(self):
     nsid1 = '.'
     self.assertEqual(['.'], list_nsid_segments(nsid1))
Ejemplo n.º 5
0
 def test_list_nsid_segments(self):
     nsid1 = '.a.b.c'
     self.assertEqual(['.','a','b','c'], list_nsid_segments(nsid1))
Ejemplo n.º 6
0
 def test_list_nsid_segments3(self):
     nsid = '.a'
     self.assertEqual(['.', 'a'], list_nsid_segments(nsid))