All modules for which code is available
+- pynodes +
diff --git a/docs/Makefile b/docs/Makefile new file mode 100644 index 0000000..d0c3cbf --- /dev/null +++ b/docs/Makefile @@ -0,0 +1,20 @@ +# Minimal makefile for Sphinx documentation +# + +# You can set these variables from the command line, and also +# from the environment for the first two. +SPHINXOPTS ?= +SPHINXBUILD ?= sphinx-build +SOURCEDIR = source +BUILDDIR = build + +# Put it first so that "make" without argument is like "make help". +help: + @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) + +.PHONY: help Makefile + +# Catch-all target: route all unknown targets to Sphinx using the new +# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). +%: Makefile + @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) diff --git a/docs/build/doctrees/environment.pickle b/docs/build/doctrees/environment.pickle new file mode 100644 index 0000000..ea57da2 Binary files /dev/null and b/docs/build/doctrees/environment.pickle differ diff --git a/docs/build/doctrees/generated/pynodes.doctree b/docs/build/doctrees/generated/pynodes.doctree new file mode 100644 index 0000000..7e240f1 Binary files /dev/null and b/docs/build/doctrees/generated/pynodes.doctree differ diff --git a/docs/build/doctrees/index.doctree b/docs/build/doctrees/index.doctree new file mode 100644 index 0000000..8a549ee Binary files /dev/null and b/docs/build/doctrees/index.doctree differ diff --git a/docs/build/html/.buildinfo b/docs/build/html/.buildinfo new file mode 100644 index 0000000..60ba9a7 --- /dev/null +++ b/docs/build/html/.buildinfo @@ -0,0 +1,4 @@ +# Sphinx build info version 1 +# This file hashes the configuration used when building these files. When it is not found, a full rebuild will be done. +config: f163178657211e96cbf2c6fd362c54f7 +tags: 645f666f9bcd5a90fca523b33c5a78b7 diff --git a/docs/build/html/_modules/index.html b/docs/build/html/_modules/index.html new file mode 100644 index 0000000..88797e8 --- /dev/null +++ b/docs/build/html/_modules/index.html @@ -0,0 +1,95 @@ + + + +
+ + +
+from __future__ import annotations # for Class forward reference
+import random
+
+from typing import ClassVar
+
+
+
+[docs]
+class Node:
+ """
+ Should be subclassed only
+ """
+ # Class Var for statistics
+ total_nodes_number: ClassVar[int] = 0
+ deepest_level: ClassVar[int] = 0
+ largest_sibling_number: ClassVar[int] = 0
+ all_nodes: ClassVar[list[Node]] = []
+
+ def __init_subclass__(cls):
+ """ each subclass must define its own ClassVar """
+ # TODO to be renamed for clarity
+ super().__init_subclass__()
+ cls.total_nodes_number: ClassVar[int] = 0
+ cls.deepest_level: ClassVar[int] = 0
+ cls.largest_sibling_number: ClassVar[int] = 0
+ cls.all_nodes: ClassVar[list[Node]] = []
+ return cls
+
+ def __new__(cls, name: str, parent: Node | None = None) -> None:
+ for n in cls.all_nodes:
+ if name == n.name:
+ raise Exception('Node with this name already exist')
+ else:
+ return super().__new__(cls)
+
+ def __init__(self, name: str, parent: Node | None = None) -> None:
+ self.name = name
+ self.parent = parent # is set with add_child
+ self.children: list[Node] = []
+ type(self).total_nodes_number += 1
+ type(self).all_nodes.append(self)
+
+ @property
+ def siblings(self) -> list[Node]:
+ """ returns all the siblings of the Node object """
+ if self.has_siblings():
+ return self.parent.children
+
+ @property
+ def parents(self) -> list[Node]:
+ """ returns all the ancestors of the Node object """
+ parents = []
+ p = self
+ while p.has_parent():
+ p = p.parent
+ parents.append(p)
+ return parents
+
+ @property
+ def level(self) -> int:
+ """ returns the level of the Node object """
+ level = 0
+ p = self
+ while p.has_parent():
+ level += 1
+ p = p.parent
+ return level
+
+ @property
+ def path(self) -> str:
+ """
+ returns a representation of the ancestor lineage of self
+ """
+ path = ''
+ for p in reversed(self.parents):
+ path += p.name+'.'
+ path += self.name
+ return path
+
+ def is_sibling(self, other: str) -> bool:
+ if other in [s.name for s in self.siblings]:
+ return True
+ else:
+ return False
+
+ def is_child(self, other: str) -> bool:
+ if other in [s.name for s in self.children]:
+ return True
+ else:
+ return False
+
+
+[docs]
+ def pretty_print(self) -> None:
+ """ print children tree from current instance """
+ dashes = ' '*self.level+'|'+'--'*self.level+' '
+ print(f'{dashes}{self.name}')
+ for c in self.children:
+ c.pretty_print()
+
+
+
+[docs]
+ def add_child(self, child: Node) -> None:
+ """ add new child node to current instance """
+ child.parent = self
+ if child.name not in [c.name for c in self.children]:
+ self.children.append(child)
+ if child.level > type(self).deepest_level:
+ type(self).deepest_level = child.level
+ if len(self.children) > type(self).largest_sibling_number:
+ type(self).largest_sibling_number = len(self.children)
+ else:
+ raise Exception('Child with same name already exists')
+
+
+ def has_parent(self) -> bool:
+ if self.parent is not None:
+ return True
+ return False
+
+ def has_children(self) -> bool:
+ if self.children is not None:
+ return True
+ return False
+
+ def has_siblings(self) -> bool:
+ if self.has_parent() and self.parent.has_children() \
+ and len(self.parent.children) > 0:
+ return True
+ return False
+
+ def get_child(self, name: str) -> Node | None:
+ for c in self.children:
+ if c.name == name:
+ return c
+ return None
+
+ def get_sibling(self, name: str) -> Node | None:
+ for c in self.siblings:
+ if c.name == name:
+ return c
+ return None
+
+ def get_children(self, name: str) -> list[Node]:
+ # refactoring, recursion is not good
+ results = []
+ if self.name == name:
+ results.append(self)
+ for c in self.children:
+ results += c.get_children(name)
+ return results
+
+
+[docs]
+ @staticmethod
+ def check_lineage(nodes: list[Node]) -> bool:
+ """
+ check if the list of nodes is a straight lineage:
+ node 1 (ancestor) -> node 2 -> node 3 -> ... -> node n (grand
+ children)
+
+ """
+ for i in range(1, len(nodes)):
+ if nodes[i].parent != nodes[i-1]:
+ return False
+
+ return True
+
+
+
+[docs]
+ @classmethod
+ def reset_stats(cls) -> None:
+ """
+ reset all the ClassVar members
+ """
+ cls.total_nodes_number = 0
+ cls.deepest_level = 0
+ cls.largest_sibling_number = 0
+ cls.all_nodes = []
+
+
+
+[docs]
+ @classmethod
+ def create_random_nodes(cls, type_: str = 'cmd', depth: int = 0) -> Node:
+ """
+ Creates random tree of nodes for testing purpose
+ """
+ def create_node(level, i):
+ id_ = cls.total_nodes_number + 1
+ return cls(f'{type_}_'+str(id_))
+
+ def create_node_list(level: int, width: int = 5):
+ return [create_node(level, i)
+ for i in range(random.randint(1, width))]
+
+ def create_arg_tree(arg: cls):
+ if arg.level < depth:
+ for a in create_node_list(arg.level):
+ arg.add_child(a)
+ create_arg_tree(a)
+ return arg
+
+ arg = cls('parser')
+
+ return create_arg_tree(arg)
+
+
+ def __lt__(self, other):
+ return self.level < other.level
+
+ def __gt__(self, other):
+ return self.level > other.level
+
+ def __le__(self, other):
+ return self.level <= other.level
+
+ def __ge__(self, other):
+ return self.level >= other.level
+
+ def __str__(self) -> str:
+ return self.name
+
+
+
+if __name__ == "__main__":
+
+# while True:
+# master_node = Node.create_random_nodes(depth=3, type_='cmd')
+# node_list = [master_node, master_node.children[0],
+# master_node.children[0].children[0]]
+# random.shuffle(node_list)
+# print([n.name for n in node_list])
+# print(Node.check_lineage(node_list))
+# input('pause')
+ n0 = Node('name0')
+ n1 = Node('name0')
+