Skip to content

Commit

Permalink
v4.0.0
Browse files Browse the repository at this point in the history
  • Loading branch information
Paebbels committed Mar 26, 2023
2 parents d5a34a7 + 89ab45d commit c71250e
Show file tree
Hide file tree
Showing 16 changed files with 2,387 additions and 739 deletions.
13 changes: 8 additions & 5 deletions doc/DataStructures/Tree.rst
Expand Up @@ -109,11 +109,13 @@ Features
* Fast and simple tree data structure based on a single :py:class:`~pyTooling.Tree.Node` class.
* A tree can be constructed top-down and bottom-up.
* A node can have a unique ID.
* Each node knows its level (distance from root).
* A node knows its level (distance from root).
* A node can have a value.
* A node can store key-value-pairs via dictionary syntax.
* A node has a reference to its parent node.
* Each node has a reference to the root node in a tree (representative node).
* A node has a reference to the root node in a tree (representative node).
* Rendering to simple ASCII art for debugging purposes.


.. _STRUCT/Tree/MissingFeatures:

Expand All @@ -131,8 +133,9 @@ Missing Features
Planned Features
================

* Rendering to simple ASCII art for debugging purposes.
* Allow filters (predicates) in generators to allow node filtering.
* Tree export to formats like GraphML, ...
* Export the tree data structure to file the YAML format.
* Allow nodes to have tags and group nodes by tags.
* Allow nodes to link to other nodes (implement proxy behavior?)

Expand All @@ -144,9 +147,9 @@ Out of Scope

* Preserve or recover the tree data structure before an erroneous operation caused an exception and aborted a tree
modification, which might leave the tree in a corrupted state.
* Export the tree data structure to various file formats like JSON, YAML, TOML, ...
* Export the tree data structure to various file formats like JSON, TOML, ...
* Import a tree data structure from various file formats like JSON, YAML, TOML, ...
* Tree visualization or rendering to complex formats like GraphML, GraphViz, Mermaid, ...
* Tree visualization or rendering to complex formats like GraphViz, Mermaid, ...


.. _STRUCT/Tree/ByFeature:
Expand Down
4 changes: 2 additions & 2 deletions doc/Dependency.rst
Expand Up @@ -125,7 +125,7 @@ the mandatory dependencies too.
+-------------------------------------------------------------------------------------------------+--------------+----------------------------------------------------------------------------------------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------+
| **Package** | **Version** | **License** | **Dependencies** |
+=================================================================================================+==============+==========================================================================================================+======================================================================================================================================================+
| `pyTooling <https://GitHub.com/pyTooling/pyTooling>`__ |2.13.0 | `Apache License, 2.0 <https://GitHub.com/pyTooling/pyTooling/blob/main/LICENSE.md>`__ | *None* |
| `pyTooling <https://GitHub.com/pyTooling/pyTooling>`__ |3.0.0 | `Apache License, 2.0 <https://GitHub.com/pyTooling/pyTooling/blob/main/LICENSE.md>`__ | *None* |
+-------------------------------------------------------------------------------------------------+--------------+----------------------------------------------------------------------------------------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------+
| `Sphinx <https://GitHub.com/sphinx-doc/sphinx>`__ | ≥5.3.0 | `BSD 3-Clause <https://GitHub.com/sphinx-doc/sphinx/blob/master/LICENSE>`__ | *Not yet evaluated.* |
+-------------------------------------------------------------------------------------------------+--------------+----------------------------------------------------------------------------------------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------+
Expand Down Expand Up @@ -166,7 +166,7 @@ install the mandatory dependencies too.
+----------------------------------------------------------------------------+--------------+----------------------------------------------------------------------------------------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------+
| **Package** | **Version** | **License** | **Dependencies** |
+============================================================================+==============+==========================================================================================================+======================================================================================================================================================+
| `pyTooling <https://GitHub.com/pyTooling/pyTooling>`__ |2.13.0 | `Apache License, 2.0 <https://GitHub.com/pyTooling/pyTooling/blob/main/LICENSE.md>`__ | *None* |
| `pyTooling <https://GitHub.com/pyTooling/pyTooling>`__ |3.0.0 | `Apache License, 2.0 <https://GitHub.com/pyTooling/pyTooling/blob/main/LICENSE.md>`__ | *None* |
+----------------------------------------------------------------------------+--------------+----------------------------------------------------------------------------------------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------+
| `wheel <https://GitHub.com/pypa/wheel>`__ | ≥0.38.1 | `MIT <https://github.com/pypa/wheel/blob/main/LICENSE.txt>`__ | *Not yet evaluated.* |
+----------------------------------------------------------------------------+--------------+----------------------------------------------------------------------------------------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------+
Expand Down
2 changes: 1 addition & 1 deletion doc/requirements.txt
@@ -1,6 +1,6 @@
-r ../requirements.txt

pyTooling>=2.13.0, <3.0
pyTooling>=3.0.0, <4.0

# Enforce latest version on ReadTheDocs
sphinx >=5.3, <6.0
Expand Down
5 changes: 3 additions & 2 deletions pyTooling/Common/__init__.py
Expand Up @@ -37,10 +37,11 @@
__email__ = "Paebbels@gmail.com"
__copyright__ = "2017-2022, Patrick Lehmann"
__license__ = "Apache License, Version 2.0"
__version__ = "3.0.0"
__version__ = "4.0.0"
__keywords__ = ["decorators", "meta-classes", "exceptions", "platform", "versioning", "licensing", "overloading",
"singleton", "tree", "graph", "timer", "data structure", "setuptools", "wheel", "installation",
"packaging", "path", "generic path", "generic library", "url"]
"packaging", "path", "generic path", "generic library", "url", "terminal", "shell", "TUI", "console",
"text user interface", "message logging", "abstract", "override"]

from collections import deque
from functools import reduce
Expand Down
169 changes: 136 additions & 33 deletions pyTooling/Graph/GraphML.py
Expand Up @@ -37,11 +37,11 @@
"""
from enum import Enum, auto
from pathlib import Path
from typing import Any, List, Dict
from typing import Any, List, Dict, Union, Optional as Nullable

from pyTooling.Decorators import export
from pyTooling.MetaClasses import ExtendedType
from pyTooling.Graph import Graph as pyToolingGraph
from pyTooling.Graph import Graph as pyToolingGraph, Subgraph as pyToolingSubgraph
from pyTooling.Tree import Node as pyToolingNode


Expand Down Expand Up @@ -289,7 +289,7 @@ def ToStringLines(self, indent: int = 2) -> List[str]:


@export
class Graph(BaseWithData):
class BaseGraph(BaseWithData):
_subgraphs: Dict[str, 'Subgraph']
_nodes: Dict[str, Node]
_edges: Dict[str, Edge]
Expand Down Expand Up @@ -340,6 +340,9 @@ def AddEdge(self, edge: Edge) -> Edge:
self._edges[edge._id] = edge
return edge

def GetEdge(self, edgeName: str) -> Edge:
return self._edges[edgeName]

def OpeningTag(self, indent: int = 1) -> str:
return f"""\
{' '*indent}<graph id="{self._id}"
Expand Down Expand Up @@ -368,13 +371,49 @@ def ToStringLines(self, indent: int = 1) -> List[str]:


@export
class Subgraph(Node, Graph):
class Graph(BaseGraph):
_document: 'GraphMLDocument'
_ids: Dict[str, Union[Node, Edge, 'Subgraph']]

def __init__(self, document: 'GraphMLDocument', identifier: str):
super().__init__(identifier)
self._document = document
self._ids = {}

def GetByID(self, identifier: str) -> Union[Node, Edge, 'Subgraph']:
return self._ids[identifier]

def AddSubgraph(self, subgraph: 'Subgraph') -> 'Subgraph':
result = super().AddSubgraph(subgraph)
self._ids[subgraph._subgraphID] = subgraph
subgraph._root = self
return result

def AddNode(self, node: Node) -> Node:
result = super().AddNode(node)
self._ids[node._id] = node
return result

def AddEdge(self, edge: Edge) -> Edge:
result = super().AddEdge(edge)
self._ids[edge._id] = edge
return result


@export
class Subgraph(Node, BaseGraph):
_subgraphID: str
_root: Nullable[Graph]

def __init__(self, nodeIdentifier: str, graphIdentifier: str):
super().__init__(nodeIdentifier)

self._subgraphID = graphIdentifier
self._root = None

@property
def RootGraph(self) -> Graph:
return self._root

@property
def SubgraphID(self) -> str:
Expand All @@ -384,6 +423,16 @@ def SubgraphID(self) -> str:
def HasClosingTag(self) -> bool:
return True

def AddNode(self, node: Node) -> Node:
result = super().AddNode(node)
self._root._ids[node._id] = node
return result

def AddEdge(self, edge: Edge) -> Edge:
result = super().AddEdge(edge)
self._root._ids[edge._id] = edge
return result

def Tag(self, indent: int = 2) -> str:
raise NotImplementedError()

Expand All @@ -399,7 +448,7 @@ def OpeningTag(self, indent: int = 1) -> str:
"""

def ClosingTag(self, indent: int = 2) -> str:
return Graph.ClosingTag(self, indent)
return BaseGraph.ClosingTag(self, indent)

def ToStringLines(self, indent: int = 2) -> List[str]:
lines = [super().OpeningTag(indent)]
Expand Down Expand Up @@ -435,11 +484,11 @@ class GraphMLDocument(Base):
def __init__(self, identifier: str = "G"):
super().__init__()

self._graph = Graph(identifier)
self._graph = Graph(self, identifier)
self._keys = {}

@property
def Graph(self) -> Graph:
def Graph(self) -> BaseGraph:
return self._graph

@property
Expand All @@ -457,37 +506,91 @@ def HasKey(self, keyName: str) -> bool:
return keyName in self._keys

def FromGraph(self, graph: pyToolingGraph):
document = self
self._graph._id = graph._name

nodeValue = self.AddKey(Key("nodeValue", AttributeContext.Node, "value", AttributeTypes.String))
edgeValue = self.AddKey(Key("edgeValue", AttributeContext.Edge, "value", AttributeTypes.String))

for vertex in graph.IterateVertices():
newNode = Node(vertex._id)
newNode.AddData(Data(nodeValue, vertex._value))
for key, value in vertex._dict.items():
if self.HasKey(str(key)):
nodeKey = self.GetKey(f"node{key!s}")
else:
nodeKey = self.AddKey(Key(f"node{key!s}", AttributeContext.Node, str(key), AttributeTypes.String))
newNode.AddData(Data(nodeKey, value))

self._graph.AddNode(newNode)

for edge in graph.IterateEdges():
source = self._graph.GetNode(edge._source._id)
target = self._graph.GetNode(edge._destination._id)

newEdge = Edge(edge._id, source, target)
newEdge.AddData(Data(edgeValue, edge._value))
for key, value in edge._dict.items():
if self.HasKey(str(key)):
edgeKey = self.GetKey(f"edge{key!s}")
else:
edgeKey = self.AddKey(Key(f"edge{key!s}", AttributeContext.Edge, str(key), AttributeTypes.String))
newEdge.AddData(Data(edgeKey, value))

self._graph.AddEdge(newEdge)
def translateGraph(rootGraph: Graph, pyTGraph: pyToolingGraph):
for vertex in pyTGraph.IterateVertices():
newNode = Node(vertex._id)
newNode.AddData(Data(nodeValue, vertex._value))
for key, value in vertex._dict.items():
if document.HasKey(str(key)):
nodeKey = document.GetKey(f"node{key!s}")
else:
nodeKey = document.AddKey(Key(f"node{key!s}", AttributeContext.Node, str(key), AttributeTypes.String))
newNode.AddData(Data(nodeKey, value))

rootGraph.AddNode(newNode)

for edge in pyTGraph.IterateEdges():
source = rootGraph.GetByID(edge._source._id)
target = rootGraph.GetByID(edge._destination._id)

newEdge = Edge(edge._id, source, target)
newEdge.AddData(Data(edgeValue, edge._value))
for key, value in edge._dict.items():
if self.HasKey(str(key)):
edgeKey = self.GetBy(f"edge{key!s}")
else:
edgeKey = self.AddKey(Key(f"edge{key!s}", AttributeContext.Edge, str(key), AttributeTypes.String))
newEdge.AddData(Data(edgeKey, value))

rootGraph.AddEdge(newEdge)

for link in pyTGraph.IterateLinks():
source = rootGraph.GetByID(link._source._id)
target = rootGraph.GetByID(link._destination._id)

newEdge = Edge(link._id, source, target)
newEdge.AddData(Data(edgeValue, link._value))
for key, value in link._dict.items():
if self.HasKey(str(key)):
edgeKey = self.GetKey(f"link{key!s}")
else:
edgeKey = self.AddKey(Key(f"link{key!s}", AttributeContext.Edge, str(key), AttributeTypes.String))
newEdge.AddData(Data(edgeKey, value))

rootGraph.AddEdge(newEdge)

def translateSubgraph(nodeGraph: Subgraph, pyTSubgraph: pyToolingSubgraph):
rootGraph = nodeGraph.RootGraph

for vertex in pyTSubgraph.IterateVertices():
newNode = Node(vertex._id)
newNode.AddData(Data(nodeValue, vertex._value))
for key, value in vertex._dict.items():
if self.HasKey(str(key)):
nodeKey = self.GetKey(f"node{key!s}")
else:
nodeKey = self.AddKey(Key(f"node{key!s}", AttributeContext.Node, str(key), AttributeTypes.String))
newNode.AddData(Data(nodeKey, value))

nodeGraph.AddNode(newNode)

for edge in pyTSubgraph.IterateEdges():
source = nodeGraph.GetNode(edge._source._id)
target = nodeGraph.GetNode(edge._destination._id)

newEdge = Edge(edge._id, source, target)
newEdge.AddData(Data(edgeValue, edge._value))
for key, value in edge._dict.items():
if self.HasKey(str(key)):
edgeKey = self.GetKey(f"edge{key!s}")
else:
edgeKey = self.AddKey(Key(f"edge{key!s}", AttributeContext.Edge, str(key), AttributeTypes.String))
newEdge.AddData(Data(edgeKey, value))

nodeGraph.AddEdge(newEdge)

for subgraph in graph.Subgraphs:
nodeGraph = Subgraph(subgraph.Name, "sg" + subgraph.Name)
self._graph.AddSubgraph(nodeGraph)
translateSubgraph(nodeGraph, subgraph)

translateGraph(self._graph, graph)

def FromTree(self, tree: pyToolingNode):
self._graph._id = tree._id
Expand Down

0 comments on commit c71250e

Please sign in to comment.