From f253ea916723f541709f819f55f27144f96d44a8 Mon Sep 17 00:00:00 2001 From: Andrei Bondarenko Date: Tue, 13 Jul 2021 03:39:48 +0200 Subject: [PATCH 01/46] Added requirements.txt --- requirements.txt | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 requirements.txt diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..e69de29 From 9b15e5d05e54d66b3641ba6dc3341680de22eb82 Mon Sep 17 00:00:00 2001 From: Andrei Bondarenko Date: Tue, 13 Jul 2021 03:44:19 +0200 Subject: [PATCH 02/46] Added venv folder --- .gitignore | 51 ++++++++++++++++++++++++++------------------------- 1 file changed, 26 insertions(+), 25 deletions(-) diff --git a/.gitignore b/.gitignore index c348ba3..d2e0969 100644 --- a/.gitignore +++ b/.gitignore @@ -10,6 +10,7 @@ __pycache__/ # Distribution / packaging .Python env/ +venv/ build/ develop-eggs/ dist/ @@ -59,31 +60,31 @@ docs/_build/ target/ # ---> macOS -.DS_Store -.AppleDouble -.LSOverride - -# Icon must end with two \r -Icon - - -# Thumbnails -._* - -# Files that might appear in the root of a volume -.DocumentRevisions-V100 -.fseventsd -.Spotlight-V100 -.TemporaryItems -.Trashes -.VolumeIcon.icns - -# Directories potentially created on remote AFP share -.AppleDB -.AppleDesktop -Network Trash Folder -Temporary Items -.apdisk +.DS_Store +.AppleDouble +.LSOverride + +# Icon must end with two \r +Icon + + +# Thumbnails +._* + +# Files that might appear in the root of a volume +.DocumentRevisions-V100 +.fseventsd +.Spotlight-V100 +.TemporaryItems +.Trashes +.VolumeIcon.icns + +# Directories potentially created on remote AFP share +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk # ---> VisualStudioCode .settings From 09c23cb708899939be5c8c581fae5d76501db0fe Mon Sep 17 00:00:00 2001 From: Andrei Bondarenko Date: Tue, 13 Jul 2021 03:46:20 +0200 Subject: [PATCH 03/46] Created state package --- state/__init__.py | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 state/__init__.py diff --git a/state/__init__.py b/state/__init__.py new file mode 100644 index 0000000..e69de29 From 009cb50eb1f5d52ae925480a4cb93d030aff892e Mon Sep 17 00:00:00 2001 From: Andrei Bondarenko Date: Tue, 13 Jul 2021 04:00:05 +0200 Subject: [PATCH 04/46] Added personal vscode settings file --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index d2e0969..211200f 100644 --- a/.gitignore +++ b/.gitignore @@ -88,6 +88,7 @@ Temporary Items # ---> VisualStudioCode .settings +.vscode/settings.json # ---> Linux From 19a79d4050a426f77d6a9c953270cf59025a512f Mon Sep 17 00:00:00 2001 From: Andrei Bondarenko Date: Tue, 13 Jul 2021 04:00:41 +0200 Subject: [PATCH 05/46] Added interface for state --- state/base.py | 287 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 287 insertions(+) create mode 100644 state/base.py diff --git a/state/base.py b/state/base.py new file mode 100644 index 0000000..07f7055 --- /dev/null +++ b/state/base.py @@ -0,0 +1,287 @@ +from abc import ABC, abstractmethod +from typing import Any, List, Tuple, TypeVar, Optional, Union, Generator +import uuid + +primitive_types = (int, float, str, bool) + + +Node = str +Edge = str +Element = Union[Node, Edge] + + +class State(ABC): + """ + Abstract base class for MvS CRUD interface defined in: + http://msdl.cs.mcgill.ca/people/yentl/files/thesis.pdf + """ + + @staticmethod + def new_id() -> str: + """ + Generates a new UUID + """ + return str(uuid.uuid4()) + + @staticmethod + def is_valid_datavalue(value: Any) -> bool: + """ + Checks whether value type is supported. + + Args: + value: value whose type needs to be checked + + Returns: + True if value type is supported, False otherwise. + """ + if not isinstance(value, primitive_types): + return False + elif isinstance(value, int) and not (-2**63 <= value <= 2**63 - 1): + return False + return True + + def purge(self): + """ + Implements a garbage collection routine for implementations that don't have automatic garbage collection. + """ + pass + + # ========================================================================= + # CREATE + # ========================================================================= + + @abstractmethod + def create_node(self) -> Node: + """ + Creates node. + + Returns: + The created node. + """ + pass + + @abstractmethod + def create_edge(self, source: Element, target: Element) -> Optional[Edge]: + """ + Creates edge. Source and target elements should already exist. + + Args: + source: source element of edge + target: target element of edge + + Returns: + The created edge, None if source or target element doesn't exist. + """ + pass + + @abstractmethod + def create_nodevalue(self, value: Any) -> Optional[Node]: + """ + Creates node containing value. + + Args: + value: value to assign to new node + + Returns: + The created node, None if type of value is not supported. + """ + pass + + @abstractmethod + def create_dict(self, source: Element, value: Any, target: Element) -> Optional[Tuple[Edge, Edge, Node]]: + """ + Creates named edge between two graph elements. + + Args: + source: source element of edge + value: edge label + target: target element of edge + + Returns: + Tuple containing the created edges and label node, None is source or target don't exist + or value type doesn't exist. First edge goes from source to target, second from edge to label. + """ + pass + + # ========================================================================= + # READ + # ========================================================================= + + @abstractmethod + def read_root(self) -> Node: + """ + Reads state's root node. + + Returns: + The state's root node. + """ + pass + + @abstractmethod + def read_value(self, node: Node) -> Optional[Any]: + """ + Reads value of given node. + + Args: + node: node whose value to read + + Returns: + I node exists, value stored in node, else None. + """ + pass + + @abstractmethod + def read_outgoing(self, elem: Element) -> Optional[List[Edge]]: + """ + Retrieves edges whose source is given element. + Args: + elem: source element of edges to retrieve + + Returns: + If elem exists, list of edges whose source is elem, else None. + """ + pass + + @abstractmethod + def read_incoming(self, elem: Element) -> Optional[List[Edge]]: + """ + Retrieves edges whose target is given element. + Args: + elem: target element of edges to retrieve + + Returns: + If elem exists, list of edges whose target is elem, else None. + """ + pass + + @abstractmethod + def read_edge(self, edge: Edge) -> Tuple[Optional[Node], Optional[Node]]: + """ + Reads source and target of given edge. + + Args: + edge: edge whose source and target to read + + Returns: + If edge exists, tuple containing source (first) and target (second) node, else (None, None) + """ + pass + + @abstractmethod + def read_dict(self, elem: Element, value: Any) -> Optional[Element]: + """ + Reads element connected to given element through edge with label = value. + + Args: + elem: source element + value: edge label + + Returns: + If elem doesn't exist or no edge is found with given label, None, else target element of edge with label = value originating from source. + """ + pass + + @abstractmethod + def read_dict_keys(self, elem: Element) -> Optional[List[Any]]: + """ + Reads labels of outgoing edges starting in given node. + + Args: + elem: source element + + Returns: + If elem exists, list of (unique) edge labels, else None. + """ + pass + + @abstractmethod + def read_dict_edge(self, elem: Element, value: Any) -> Optional[Edge]: + """ + Reads edge between two elements connected through edge with label = value. + + Args: + elem: source element + value: edge label + + Returns: + If elem doesn't exist or no edge is found with given label, None, else edge with label = value originating from source. + """ + pass + + @abstractmethod + def read_dict_node(self, elem: Element, value_node: Node) -> Optional[Element]: + """ + Reads element connected to given element through edge with label node = value_node. + + Args: + elem: source element + value_node: edge label node + + Returns: + If elem exists, target element of edge with label stored in value_node originating from elem, else None. + """ + pass + + @abstractmethod + def read_dict_node_edge(self, elem: Element, value_node: Node) -> Optional[Edge]: + """ + Reads edge connecting two elements through edge with label node = value_node. + + Args: + elem: source element + value_node: edge label node + + Returns: + If elem exists, edge with label node = value_node, originating from source, else None. + """ + pass + + @abstractmethod + def read_reverse_dict(self, elem: Element, value: Any) -> Optional[List[Element]]: + """ + Retrieves a list of all elements that have an outgoing edge, having label = value, towards the passed element. + + Args: + elem: target element + value: edge label + + Returns: + If elem exists, list of elements with an outgoing edge with label = value towards elem, else None. + """ + pass + + # ========================================================================= + # UPDATE + # ========================================================================= + """ + Updates are done by performing subsequent CREATE and DELETE operations: + http://msdl.cs.mcgill.ca/people/yentl/files/thesis.pdf + """ + + # ========================================================================= + # DELETE + # ========================================================================= + + @abstractmethod + def delete_node(self, node: Node) -> None: + """ + Deletes given node from state graph. + Args: + node: node to be deleted + + Returns: + None + """ + pass + + @abstractmethod + def delete_edge(self, edge: Edge) -> None: + """ + Deletes given edge from state graph. + Args: + edge: edge to be deleted + + Returns: + None + """ + pass From 7aa2be3ad9225cd5caf06e6c4669859204a33f9b Mon Sep 17 00:00:00 2001 From: Andrei Bondarenko Date: Tue, 13 Jul 2021 04:27:58 +0200 Subject: [PATCH 06/46] Added reference to MV source code --- state/base.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/state/base.py b/state/base.py index 07f7055..e33c0e7 100644 --- a/state/base.py +++ b/state/base.py @@ -14,6 +14,8 @@ class State(ABC): """ Abstract base class for MvS CRUD interface defined in: http://msdl.cs.mcgill.ca/people/yentl/files/thesis.pdf + This code is based on: + https://msdl.uantwerpen.be/git/yentl/modelverse/src/master/state/modelverse_state """ @staticmethod @@ -88,7 +90,7 @@ class State(ABC): pass @abstractmethod - def create_dict(self, source: Element, value: Any, target: Element) -> Optional[Tuple[Edge, Edge, Node]]: + def create_dict(self, source: Element, value: Any, target: Element) -> None: """ Creates named edge between two graph elements. From d814ffbc6b9e131ff0726e777e707e4cfcab3685 Mon Sep 17 00:00:00 2001 From: Andrei Bondarenko Date: Tue, 13 Jul 2021 04:28:16 +0200 Subject: [PATCH 07/46] Added PyState implementation --- state/pystate.py | 286 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 286 insertions(+) create mode 100644 state/pystate.py diff --git a/state/pystate.py b/state/pystate.py new file mode 100644 index 0000000..fff6565 --- /dev/null +++ b/state/pystate.py @@ -0,0 +1,286 @@ +from typing import Any, List, Tuple, Optional + +from .base import State, Node, Edge, Element + + +class PyState(State): + """ + State interface implemented using Python data structures. + + This code is based on: + https://msdl.uantwerpen.be/git/yentl/modelverse/src/master/state/modelverse_state/main.py + """ + def __init__(self): + self.edges = {} + self.outgoing = {} + self.incoming = {} + self.values = {} + self.nodes = set() + # Set used for garbage collection + self.GC = True + self.to_delete = set() + + self.cache = {} + self.cache_node = {} + + self.root = self.create_node() + + def create_node(self) -> Node: + new_id = self.new_id() + self.nodes.add(new_id) + return new_id + + def create_edge(self, source: Element, target: Element) -> Optional[Edge]: + if source not in self.edges and source not in self.nodes: + return None + elif target not in self.edges and target not in self.nodes: + return None + else: + new_id = self.new_id() + self.outgoing.setdefault(source, set()).add(new_id) + self.incoming.setdefault(target, set()).add(new_id) + self.edges[new_id] = (source, target) + if source in self.edges: + # We are creating something dict_readable + # Fill in the cache already! + dict_source, dict_target = self.edges[source] + if target in self.values: + self.cache.setdefault(dict_source, {})[self.values[target]] = source + self.cache_node.setdefault(dict_source, {})[target] = source + return new_id + + def create_nodevalue(self, value: Any) -> Optional[Node]: + if not self.is_valid_datavalue(value): + return None + new_id = self.new_id() + self.values[new_id] = value + self.nodes.add(new_id) + return new_id + + def create_dict(self, source: Element, value: Any, target: Element) -> None: + if source not in self.nodes and source not in self.edges: + return None + elif target not in self.nodes and target not in self.edges: + return None + elif not self.is_valid_datavalue(value): + return None + else: + n = self.create_nodevalue(value) + e = self.create_edge(source, target) + assert n is not None and e is not None + e2 = self.create_edge(e, n) + self.cache.setdefault(source, {})[value] = e + self.cache_node.setdefault(source, {})[n] = e + + def read_root(self) -> Node: + return self.root + + def read_value(self, node: Node) -> Any: + if node in self.values: + return self.values[node] + else: + return None + + def read_outgoing(self, elem: Element) -> Optional[List[Edge]]: + if elem in self.edges or elem in self.nodes: + if elem in self.outgoing: + return list(self.outgoing[elem]) + else: + return [] + else: + return None + + def read_incoming(self, elem: Element) -> Optional[List[Edge]]: + if elem in self.edges or elem in self.nodes: + if elem in self.incoming: + return list(self.incoming[elem]) + else: + return [] + else: + return None + + def read_edge(self, edge: Edge) -> Tuple[Optional[Element], Optional[Element]]: + if edge in self.edges: + return self.edges[edge][0], self.edges[edge][1] + else: + return None, None + + def read_dict(self, elem: Element, value: Any) -> Optional[Element]: + e = self.read_dict_edge(elem, value) + if e is None: + return None + else: + return self.edges[e][1] + + def read_dict_keys(self, elem: Element) -> Optional[List[Any]]: + if elem not in self.nodes and elem not in self.edges: + return None + + result = [] + # NOTE: cannot just use the cache here, as some keys in the cache might not actually exist; + # we would have to check all of them anyway + if elem in self.outgoing: + for e1 in self.outgoing[elem]: + if e1 in self.outgoing: + for e2 in self.outgoing[e1]: + result.append(self.edges[e2][1]) + return result + + def read_dict_edge(self, elem: Element, value: Any) -> Optional[Edge]: + try: + first = self.cache[elem][value] + # Got hit, so validate + if (self.edges[first][0] == elem) and (value in [self.values[self.edges[i][1]] + for i in self.outgoing[first] + if self.edges[i][1] in self.values]): + return first + # Hit but invalid now + del self.cache[elem][value] + return None + except KeyError: + return None + + def read_dict_node(self, elem: Element, value_node: Node) -> Optional[Element]: + e = self.read_dict_node_edge(elem, value_node) + if e is None: + return None + else: + self.cache_node.setdefault(elem, {})[value_node] = e + return self.edges[e][1] + + def read_dict_node_edge(self, elem: Element, value_node: Node) -> Optional[Edge]: + try: + first = self.cache_node[elem][value_node] + # Got hit, so validate + if (self.edges[first][0] == elem) and \ + (value_node in [self.edges[i][1] for i in self.outgoing[first]]): + return first + # Hit but invalid now + del self.cache_node[elem][value_node] + return None + except KeyError: + return None + + def read_reverse_dict(self, elem: Element, value: Any) -> Optional[List[Element]]: + if elem not in self.nodes and elem not in self.edges: + return None + # Get all outgoing links + matches = [] + if elem in self.incoming: + for e1 in self.incoming[elem]: + # For each link, we read the links that might link to a data value + if e1 in self.outgoing: + for e2 in self.outgoing[e1]: + # Now read out the target of the link + target = self.edges[e2][1] + # And access its value + if target in self.values and self.values[target] == value: + # Found a match + matches.append(e1) + return [self.edges[e][0] for e in matches] + + def delete_node(self, node: Node) -> None: + if node == self.root: + return + elif node not in self.nodes: + return + + self.nodes.remove(node) + + if node in self.values: + del self.values[node] + + s = set() + if node in self.outgoing: + for e in self.outgoing[node]: + s.add(e) + del self.outgoing[node] + if node in self.incoming: + for e in self.incoming[node]: + s.add(e) + del self.incoming[node] + + for e in s: + self.delete_edge(e) + + if node in self.outgoing: + del self.outgoing[node] + if node in self.incoming: + del self.incoming[node] + + def delete_edge(self, edge: Edge) -> None: + if edge not in self.edges: + return + + s, t = self.edges[edge] + if t in self.incoming: + self.incoming[t].remove(edge) + if s in self.outgoing: + self.outgoing[s].remove(edge) + + del self.edges[edge] + + s = set() + if edge in self.outgoing: + for e in self.outgoing[edge]: + s.add(e) + if edge in self.incoming: + for e in self.incoming[edge]: + s.add(e) + + for e in s: + self.delete_edge(e) + + if edge in self.outgoing: + del self.outgoing[edge] + if edge in self.incoming: + del self.incoming[edge] + + if self.GC and (t in self.incoming and not self.incoming[t]) and (t not in self.edges): + # Remove this node as well + # Edges aren't deleted like this, as they might have a reachable target and source! + # If they haven't, they will be removed because the source was removed. + self.to_delete.add(t) + + def purge(self): + while self.to_delete: + t = self.to_delete.pop() + if t in self.incoming and not self.incoming[t]: + self.delete_node(t) + + values = set(self.edges) + values.update(self.nodes) + visit_list = [self.root] + + while visit_list: + elem = visit_list.pop() + if elem in values: + # Remove it from the leftover values + values.remove(elem) + if elem in self.edges: + visit_list.extend(self.edges[elem]) + if elem in self.outgoing: + visit_list.extend(self.outgoing[elem]) + if elem in self.incoming: + visit_list.extend(self.incoming[elem]) + + dset = set() + for key in self.cache: + if key not in self.nodes and key not in self.edges: + dset.add(key) + for key in dset: + del self.cache[key] + + dset = set() + for key in self.cache_node: + if key not in self.nodes and key not in self.edges: + dset.add(key) + for key in dset: + del self.cache_node[key] + + # All remaining elements are to be purged + if len(values) > 0: + while values: + v = values.pop() + if v in self.nodes: + self.delete_node(v) From 9a16377f3099c145c543046829df90d75d830ceb Mon Sep 17 00:00:00 2001 From: Andrei Bondarenko Date: Tue, 13 Jul 2021 04:36:17 +0200 Subject: [PATCH 08/46] Added DevState --- state/devstate.py | 47 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) create mode 100644 state/devstate.py diff --git a/state/devstate.py b/state/devstate.py new file mode 100644 index 0000000..24f622b --- /dev/null +++ b/state/devstate.py @@ -0,0 +1,47 @@ +from .pystate import PyState + + +class DevState(PyState): + """ + Version of PyState that allows dumping to .dot files + + node id's are generated sequentially to make writing tests easier + """ + + free_id = 0 + + def __init__(self): + super().__init__() + + @staticmethod + def new_id() -> str: + DevState.free_id += 1 + return str(DevState.free_id - 1) + + def dump(self, path: str, png_path: str = None): + """Dumps the whole MV graph to a graphviz .dot-file + + Args: + path (str): path for .dot-file + png_path (str, optional): path for .png image generated from the .dot-file. Defaults to None. + """ + with open(path, "w") as f: + f.write("digraph main {\n") + for n in sorted(self.nodes): + if n in self.values: + f.write("\"a_%s\" [label=\"%s\"];\n" % ( + n, str(self.values[n]).replace('"', '\\"'))) + else: + f.write("\"a_%s\" [label=\"\"];\n" % n) + for i, e in sorted(list(self.edges.items())): + f.write("\"a_%s\" [label=\"e_%s\" shape=point];\n" % (i, i)) + f.write("\"a_%s\" -> \"a_%s\" [arrowhead=none];\n" % (e[0], i)) + f.write("\"a_%s\" -> \"a_%s\";\n" % (i, e[1])) + f.write("}") + + if png_path is not None: + # generate png from dot-file + bashCommand = f"dot -Tpng {path} -o {png_path}" + import subprocess + process = subprocess.Popen( + bashCommand.split(), stdout=subprocess.PIPE) + output, error = process.communicate() From b961e91d9b996eb337d5f0eab12233fca8e14aa9 Mon Sep 17 00:00:00 2001 From: Andrei Bondarenko Date: Tue, 13 Jul 2021 04:56:08 +0200 Subject: [PATCH 09/46] Added tests for State --- state/test/__init__.py | 0 state/test/conftest.py | 2 + state/test/fixtures/__init__.py | 0 state/test/fixtures/state.py | 19 ++ state/test/test_create_dict.py | 41 +++ state/test/test_create_edge.py | 144 ++++++++++ state/test/test_create_node.py | 22 ++ state/test/test_create_nodevalue.py | 167 +++++++++++ state/test/test_delete_edge.py | 396 +++++++++++++++++++++++++++ state/test/test_delete_node.py | 227 +++++++++++++++ state/test/test_read_dict.py | 94 +++++++ state/test/test_read_dict_edge.py | 94 +++++++ state/test/test_read_dict_keys.py | 51 ++++ state/test/test_read_dict_node.py | 74 +++++ state/test/test_read_edge.py | 136 +++++++++ state/test/test_read_incoming.py | 275 +++++++++++++++++++ state/test/test_read_outgoing.py | 265 ++++++++++++++++++ state/test/test_read_reverse_dict.py | 165 +++++++++++ state/test/test_read_value.py | 88 ++++++ 19 files changed, 2260 insertions(+) create mode 100644 state/test/__init__.py create mode 100644 state/test/conftest.py create mode 100644 state/test/fixtures/__init__.py create mode 100644 state/test/fixtures/state.py create mode 100644 state/test/test_create_dict.py create mode 100644 state/test/test_create_edge.py create mode 100644 state/test/test_create_node.py create mode 100644 state/test/test_create_nodevalue.py create mode 100644 state/test/test_delete_edge.py create mode 100644 state/test/test_delete_node.py create mode 100644 state/test/test_read_dict.py create mode 100644 state/test/test_read_dict_edge.py create mode 100644 state/test/test_read_dict_keys.py create mode 100644 state/test/test_read_dict_node.py create mode 100644 state/test/test_read_edge.py create mode 100644 state/test/test_read_incoming.py create mode 100644 state/test/test_read_outgoing.py create mode 100644 state/test/test_read_reverse_dict.py create mode 100644 state/test/test_read_value.py diff --git a/state/test/__init__.py b/state/test/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/state/test/conftest.py b/state/test/conftest.py new file mode 100644 index 0000000..ecbc30d --- /dev/null +++ b/state/test/conftest.py @@ -0,0 +1,2 @@ +import pytest +from .fixtures.state import state diff --git a/state/test/fixtures/__init__.py b/state/test/fixtures/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/state/test/fixtures/state.py b/state/test/fixtures/state.py new file mode 100644 index 0000000..ab7eb87 --- /dev/null +++ b/state/test/fixtures/state.py @@ -0,0 +1,19 @@ +import pytest +from state.pystate import PyState +# from state.rdfstate import RDFState +# from state.neo4jstate import Neo4jState + + +@pytest.fixture(params=[ + (PyState,), + # (RDFState, "http://example.org/#"), + # (Neo4jState,) +]) +def state(request): + if len(request.param) > 1: + state = request.param[0](*request.param[1:]) + else: + state = request.param[0]() + yield state + # if isinstance(state, Neo4jState): + # state.close(clear=True) diff --git a/state/test/test_create_dict.py b/state/test/test_create_dict.py new file mode 100644 index 0000000..2c3025f --- /dev/null +++ b/state/test/test_create_dict.py @@ -0,0 +1,41 @@ +import pytest + + +@pytest.mark.usefixtures("state") +def test_create_dict_simple(state): + id1 = state.create_node() + id2 = state.create_node() + assert id1 is not None + assert id2 is not None + + n = state.create_dict(id1, "abc", id2) + assert n is None + + v = state.read_dict(id1, "abc") + assert v == id2 + + +@pytest.mark.usefixtures("state") +def test_create_dict_no_source(state): + id1 = 100000 + id2 = state.create_node() + assert id2 is not None + + n = state.create_dict(id1, "abc", id2) + assert n is None + + v = state.read_dict(id1, "abc") + assert v is None + + +@pytest.mark.usefixtures("state") +def test_create_dict_no_target(state): + id2 = 100000 + id1 = state.create_node() + assert id1 is not None + + n = state.create_dict(id1, "abc", id2) + assert n is None + + v = state.read_dict(id1, "abc") + assert v is None diff --git a/state/test/test_create_edge.py b/state/test/test_create_edge.py new file mode 100644 index 0000000..a379dbd --- /dev/null +++ b/state/test/test_create_edge.py @@ -0,0 +1,144 @@ +import pytest + + +@pytest.mark.usefixtures("state") +def test_create_edge_invalid_source(state): + a = -1 + b = state.create_node() + assert b is not None + + e = state.create_edge(a, b) + assert e is None + + +@pytest.mark.usefixtures("state") +def test_create_edge_invalid_target(state): + b = -1 + a = state.create_node() + assert a is not None + + e = state.create_edge(a, b) + assert e is None + + +@pytest.mark.usefixtures("state") +def test_create_edge_invalid_both(state): + a = -1 + b = -1 + e = state.create_edge(a, b) + assert e is None + + +@pytest.mark.usefixtures("state") +def test_create_edge_node_to_node(state): + a = state.create_node() + assert a is not None + b = state.create_node() + assert b is not None + + edge = state.create_edge(a, b) + assert edge is not None + + +@pytest.mark.usefixtures("state") +def test_create_edge_multiple(state): + a = state.create_node() + assert a is not None + b = state.create_node() + assert b is not None + + edge1 = state.create_edge(a, b) + assert edge1 is not None + + edge2 = state.create_edge(a, b) + assert edge2 is not None + + assert edge1 != edge2 + + +@pytest.mark.usefixtures("state") +def test_create_edge_many(state): + v = set() + for i in range(1000): + a = state.create_node() + assert a is not None + b = state.create_node() + assert b is not None + + edge = state.create_edge(a, b) + assert edge is not None + + v.add(edge) + assert len(v) == 1000 + + +@pytest.mark.usefixtures("state") +def test_create_edge_edge_to_node(state): + a = state.create_node() + assert a is not None + b = state.create_node() + assert b is not None + + edge1 = state.create_edge(a, b) + assert edge1 is not None + + edge2 = state.create_edge(edge1, b) + assert edge2 is not None + + assert edge1 != edge2 + + +@pytest.mark.usefixtures("state") +def test_create_edge_node_to_edge(state): + a = state.create_node() + assert a is not None + b = state.create_node() + assert b is not None + + edge1 = state.create_edge(a, b) + assert edge1 is not None + + edge2 = state.create_edge(a, edge1) + assert edge2 is not None + + assert edge1 != edge2 + + +@pytest.mark.usefixtures("state") +def test_create_edge_edge_to_edge(state): + a = state.create_node() + assert a is not None + b = state.create_node() + assert b is not None + + edge1 = state.create_edge(a, b) + assert edge1 is not None + + edge2 = state.create_edge(a, b) + assert edge2 is not None + + assert edge1 != edge2 + + edge3 = state.create_edge(edge1, edge2) + assert edge3 is not None + + +@pytest.mark.usefixtures("state") +def test_create_edge_loop_node(state): + a = state.create_node() + assert a is not None + + edge = state.create_edge(a, a) + assert edge is not None + + +@pytest.mark.usefixtures("state") +def test_create_edge_loop_edge(state): + a = state.create_node() + assert a is not None + + edge1 = state.create_edge(a, a) + assert edge1 is not None + + edge2 = state.create_edge(edge1, edge1) + assert edge2 is not None diff --git a/state/test/test_create_node.py b/state/test/test_create_node.py new file mode 100644 index 0000000..11110a8 --- /dev/null +++ b/state/test/test_create_node.py @@ -0,0 +1,22 @@ +import pytest + + +@pytest.mark.usefixtures("state") +def test_create_node_different_id_simple(state): + id1 = state.create_node() + assert id1 is not None + id2 = state.create_node() + assert id2 is not None + + assert id1 != id2 + + +@pytest.mark.usefixtures("state") +def test_create_node_different_id_long(state): + results = set() + for i in range(1000): + v = state.create_node() + assert v is not None + results.add(v) + + assert len(results) == 1000 diff --git a/state/test/test_create_nodevalue.py b/state/test/test_create_nodevalue.py new file mode 100644 index 0000000..9ee275f --- /dev/null +++ b/state/test/test_create_nodevalue.py @@ -0,0 +1,167 @@ +import pytest + + +@pytest.mark.usefixtures("state") +def test_create_nodevalue_different_id_simple(state): + id1 = state.create_nodevalue(1) + id2 = state.create_nodevalue(1) + + assert id1 is not None + assert id2 is not None + assert id1 != id2 + + +@pytest.mark.usefixtures("state") +def test_create_nodevalue_read(state): + id1 = state.create_nodevalue(1) + assert id1 is not None + val = state.read_value(id1) + assert val == 1 + + +@pytest.mark.usefixtures("state") +def test_create_nodevalue_integer_ib_zero(state): + # Nicely within range + v = set() + size = 0 + for i in range(-10, 10): + id1 = state.create_nodevalue(i) + assert id1 is not None + size += 1 + v.add(id1) + assert len(v) == size + + +@pytest.mark.usefixtures("state") +def test_create_nodevalue_boolean(state): + id1 = state.create_nodevalue(True) + id2 = state.create_nodevalue(False) + + assert id1 is not None + assert id2 is not None + assert id1 != id2 + + +@pytest.mark.usefixtures("state") +def test_create_nodevalue_boolean_same(state): + id1 = state.create_nodevalue(True) + id2 = state.create_nodevalue(True) + + assert id1 is not None + assert id2 is not None + assert id1 != id2 + + +@pytest.mark.usefixtures("state") +def test_create_nodevalue_float_keeps_type(state): + id1 = state.create_nodevalue(0.0) + assert id1 is not None + + v = state.read_value(id1) + assert type(v) == float + assert v == 0.0 + + +@pytest.mark.usefixtures("state") +def test_create_nodevalue_string_empty(state): + id1 = state.create_nodevalue("") + assert id1 is not None + + v = state.read_value(id1) + assert type(v) == str + assert v == "" + + +@pytest.mark.usefixtures("state") +def test_create_nodevalue_string_normal(state): + id1 = state.create_nodevalue("ABC") + assert id1 is not None + + v = state.read_value(id1) + assert type(v) == str + assert v == "ABC" + + +@pytest.mark.usefixtures("state") +def test_create_nodevalue_string_not_parsed(state): + id1 = state.create_nodevalue("1") + assert id1 is not None + + v = state.read_value(id1) + assert type(v) == str + assert v == "1" + + id1 = state.create_nodevalue("1.0") + assert id1 is not None + + v = state.read_value(id1) + assert type(v) == str + assert v == "1.0" + + id1 = state.create_nodevalue("-1.0") + assert id1 is not None + + v = state.read_value(id1) + assert type(v) == str + assert v == "-1.0" + + id1 = state.create_nodevalue("True") + assert id1 is not None + + v = state.read_value(id1) + assert type(v) == str + assert v == "True" + + +@pytest.mark.usefixtures("state") +def test_create_nodevalue_junk(state): + class Unknown(object): + pass + + n = state.create_nodevalue(Unknown()) + assert n is None + + +@pytest.mark.usefixtures("state") +def test_create_nodevalue_type_type(state): + id1 = state.create_nodevalue({"type": "Type"}) + assert id1 is not None + + v = state.read_value(id1) + assert v == {"type": "Type"} + + +@pytest.mark.usefixtures("state") +def test_create_nodevalue_integer_type(state): + id1 = state.create_nodevalue({"type": "Integer"}) + assert id1 is not None + + v = state.read_value(id1) + assert v == {"type": "Integer"} + + +@pytest.mark.usefixtures("state") +def test_create_nodevalue_float_type(state): + id1 = state.create_nodevalue({"type": "Float"}) + assert id1 is not None + + v = state.read_value(id1) + assert v == {"type": "Float"} + + +@pytest.mark.usefixtures("state") +def test_create_nodevalue_boolean_type(state): + id1 = state.create_nodevalue({"type": "Boolean"}) + assert id1 is not None + + v = state.read_value(id1) + assert v == {"type": "Boolean"} + + +@pytest.mark.usefixtures("state") +def test_create_nodevalue_string_type(state): + id1 = state.create_nodevalue({"type": "String"}) + assert id1 is not None + + v = state.read_value(id1) + assert v == {"type": "String"} diff --git a/state/test/test_delete_edge.py b/state/test/test_delete_edge.py new file mode 100644 index 0000000..538dd13 --- /dev/null +++ b/state/test/test_delete_edge.py @@ -0,0 +1,396 @@ +import pytest + + +@pytest.mark.usefixtures("state") +def test_delete_edge_no_exists(state): + e = state.delete_edge(1) + assert e is None + + +@pytest.mark.usefixtures("state") +def test_delete_edge_node(state): + a = state.create_node() + assert a is not None + + e = state.delete_edge(a) + assert e is None + + +@pytest.mark.usefixtures("state") +def test_delete_edge_nodevalue(state): + a = state.create_nodevalue(1) + assert a is not None + + e = state.delete_edge(a) + assert e is None + + +@pytest.mark.usefixtures("state") +def test_delete_edge_normal(state): + a = state.create_nodevalue(1) + b = state.create_node() + c = state.create_edge(a, b) + assert a is not None + assert b is not None + assert c is not None + + n = state.delete_edge(c) + assert n is None + + l = state.read_outgoing(a) + assert l is not None + assert set(l) == set([]) + + l = state.read_incoming(a) + assert l is not None + assert set(l) == set([]) + + l = state.read_outgoing(b) + assert l is not None + assert set(l) == set([]) + + l = state.read_incoming(b) + assert l is not None + assert set(l) == set([]) + + +@pytest.mark.usefixtures("state") +def test_delete_edge_remove_recursive(state): + a = state.create_nodevalue(1) + b = state.create_node() + c = state.create_edge(a, b) + d = state.create_edge(c, b) + assert a is not None + assert b is not None + assert c is not None + assert d is not None + + n = state.delete_edge(c) + assert n is None + + l = state.read_value(a) + assert l == 1 + + l = state.read_outgoing(a) + assert l is not None + assert set(l) == set([]) + + l = state.read_incoming(a) + assert l is not None + assert set(l) == set([]) + + s, t = state.read_edge(c) + assert s is None + assert t is None + + s, t = state.read_edge(d) + assert s is None + assert t is None + + l = state.read_outgoing(b) + assert l is not None + assert set(l) == set([]) + + +@pytest.mark.usefixtures("state") +def test_delete_edge_remove_edge_recursive_deep(state): + a = state.create_node() + b = state.create_node() + c = state.create_node() + d = state.create_edge(a, b) + e = state.create_edge(d, c) + f = state.create_node() + g = state.create_edge(f, e) + h = state.create_edge(b, c) + assert a is not None + assert b is not None + assert c is not None + assert d is not None + assert e is not None + assert f is not None + assert g is not None + assert h is not None + + n = state.delete_edge(d) + assert n is None + + l = state.read_outgoing(a) + assert l is not None + assert set(l) == set([]) + + l = state.read_incoming(a) + assert l is not None + assert set(l) == set([]) + + l = state.read_outgoing(b) + assert l is not None + assert set(l) == set([h]) + + l = state.read_incoming(b) + assert l is not None + assert set(l) == set([]) + + l = state.read_outgoing(c) + assert l is not None + assert set(l) == set([]) + + l = state.read_incoming(c) + assert l is not None + assert set(l) == set([h]) + + s, t = state.read_edge(d) + assert s is None + assert t is None + + s, t = state.read_edge(e) + assert s is None + assert t is None + + s, t = state.read_edge(g) + assert s is None + assert t is None + + l = state.read_outgoing(f) + assert l is not None + assert set(l) == set([]) + + l = state.read_incoming(f) + assert l is not None + assert set(l) == set([]) + + s, t = state.read_edge(h) + assert s == b + assert t == c + + +@pytest.mark.usefixtures("state") +def test_delete_edge_remove_edge_recursive_steps(state): + a = state.create_node() + b = state.create_node() + c = state.create_node() + d = state.create_edge(a, b) + e = state.create_edge(d, c) + f = state.create_node() + g = state.create_edge(f, e) + h = state.create_edge(b, c) + assert a is not None + assert b is not None + assert c is not None + assert d is not None + assert e is not None + assert f is not None + assert g is not None + assert h is not None + + n = state.delete_edge(g) + assert n is None + + l = state.read_outgoing(a) + assert l is not None + assert set(l) == set([d]) + + l = state.read_incoming(a) + assert l is not None + assert set(l) == set([]) + + l = state.read_outgoing(b) + assert l is not None + assert set(l) == set([h]) + + l = state.read_incoming(b) + assert l is not None + assert set(l) == set([d]) + + l = state.read_outgoing(c) + assert l is not None + assert set(l) == set([]) + + l = state.read_incoming(c) + assert l is not None + assert set(l) == set([h, e]) + + s, t = state.read_edge(d) + assert s == a + assert t == b + + l = state.read_outgoing(d) + assert l is not None + assert set(l) == set([e]) + + l = state.read_incoming(d) + assert l is not None + assert set(l) == set([]) + + s, t = state.read_edge(e) + assert s == d + assert t == c + + l = state.read_outgoing(e) + assert l is not None + assert set(l) == set([]) + + l = state.read_incoming(e) + assert l is not None + assert set(l) == set([]) + + s, t = state.read_edge(g) + assert s is None + assert t is None + + l = state.read_outgoing(g) + assert l is None + + l = state.read_incoming(g) + assert l is None + + l = state.read_outgoing(f) + assert l is not None + assert set(l) == set([]) + + l = state.read_incoming(f) + assert l is not None + assert set(l) == set([]) + + s, t = state.read_edge(h) + assert s == b + assert t == c + + n = state.delete_edge(e) + assert n is None + + l = state.read_outgoing(a) + assert l is not None + assert set(l) == set([d]) + + l = state.read_incoming(a) + assert l is not None + assert set(l) == set([]) + + l = state.read_outgoing(b) + assert l is not None + assert set(l) == set([h]) + + l = state.read_incoming(b) + assert l is not None + assert set(l) == set([d]) + + l = state.read_outgoing(c) + assert l is not None + assert set(l) == set([]) + + l = state.read_incoming(c) + assert l is not None + assert set(l) == set([h]) + + s, t = state.read_edge(d) + assert s == a + assert t == b + + l = state.read_outgoing(d) + assert l is not None + assert set(l) == set([]) + + l = state.read_incoming(d) + assert l is not None + assert set(l) == set([]) + + s, t = state.read_edge(e) + assert s is None + assert t is None + + l = state.read_outgoing(e) + assert l is None + + l = state.read_incoming(e) + assert l is None + + s, t = state.read_edge(g) + assert s is None + assert t is None + + l = state.read_outgoing(g) + assert l is None + + l = state.read_incoming(g) + assert l is None + + l = state.read_outgoing(f) + assert l is not None + assert set(l) == set([]) + + l = state.read_incoming(f) + assert l is not None + assert set(l) == set([]) + + s, t = state.read_edge(h) + assert s == b + assert t == c + + n = state.delete_edge(d) + assert n is None + + l = state.read_outgoing(a) + assert l is not None + assert set(l) == set([]) + + l = state.read_incoming(a) + assert l is not None + assert set(l) == set([]) + + l = state.read_outgoing(b) + assert l is not None + assert set(l) == set([h]) + + l = state.read_incoming(b) + assert l is not None + assert set(l) == set([]) + + l = state.read_outgoing(c) + assert l is not None + assert set(l) == set([]) + + l = state.read_incoming(c) + assert l is not None + assert set(l) == set([h]) + + s, t = state.read_edge(d) + assert s is None + assert t is None + + l = state.read_outgoing(d) + assert l is None + + l = state.read_incoming(d) + assert l is None + + s, t = state.read_edge(e) + assert s is None + assert t is None + + l = state.read_outgoing(e) + assert l is None + + l = state.read_incoming(e) + assert l is None + + s, t = state.read_edge(g) + assert s is None + assert t is None + + l = state.read_outgoing(g) + assert l == None + + l = state.read_incoming(g) + assert l == None + + l = state.read_outgoing(f) + assert l is not None + assert set(l) == set([]) + + l = state.read_incoming(f) + assert l is not None + assert set(l) == set([]) + + s, t = state.read_edge(h) + assert s == b + assert t == c diff --git a/state/test/test_delete_node.py b/state/test/test_delete_node.py new file mode 100644 index 0000000..67cdde7 --- /dev/null +++ b/state/test/test_delete_node.py @@ -0,0 +1,227 @@ +import pytest + + +@pytest.mark.usefixtures("state") +def test_delete_node_no_exists(state): + n = state.delete_node(-1) + assert n is None + + +@pytest.mark.usefixtures("state") +def test_delete_node_no_value(state): + a = state.create_node() + assert a is not None + + n = state.delete_node(a) + assert n is None + + +@pytest.mark.usefixtures("state") +def test_delete_node_value(state): + a = state.create_nodevalue(1) + assert a is not None + + d = state.read_value(a) + assert d == 1 + + n = state.delete_node(a) + assert n is None + + d = state.read_value(a) + assert d is None + + +@pytest.mark.usefixtures("state") +def test_delete_node_edge(state): + a = state.create_node() + b = state.create_node() + c = state.create_edge(a, b) + assert a is not None + assert b is not None + assert c is not None + + n = state.delete_node(c) + assert n is None + + +@pytest.mark.usefixtures("state") +def test_delete_node_remove_edge_outgoing(state): + a = state.create_nodevalue(1) + b = state.create_node() + c = state.create_edge(a, b) + assert a is not None + assert b is not None + assert c is not None + + n = state.delete_node(a) + assert n is None + + d = state.read_value(a) + assert d is None + + s, t = state.read_edge(c) + assert s is None + assert t is None + + d = state.read_outgoing(b) + assert d is not None + assert set(d) == set([]) + + +@pytest.mark.usefixtures("state") +def test_delete_node_remove_edge_incoming(state): + a = state.create_nodevalue(1) + b = state.create_node() + c = state.create_edge(b, a) + assert a is not None + assert b is not None + assert c is not None + + n = state.delete_node(a) + assert n is None + + d = state.read_value(a) + assert d is None + + s, t = state.read_edge(c) + assert s is None + assert t is None + + d = state.read_outgoing(b) + assert d is not None + assert set(d) == set([]) + + +@pytest.mark.usefixtures("state") +def test_delete_node_remove_edge_both(state): + a = state.create_nodevalue(1) + b = state.create_node() + c = state.create_edge(a, b) + e = state.create_node() + f = state.create_edge(e, a) + assert a is not None + assert b is not None + assert c is not None + assert e is not None + assert f is not None + + n = state.delete_node(a) + assert n is None + + d = state.read_value(a) + assert d is None + + s, t = state.read_edge(c) + assert s is None + assert t is None + + d = state.read_incoming(b) + assert d is not None + assert set(d) == set([]) + + s, t = state.read_edge(f) + assert s is None + assert t is None + + d = state.read_outgoing(e) + assert d is not None + assert set(d) == set([]) + + +@pytest.mark.usefixtures("state") +def test_delete_node_remove_edge_recursive(state): + a = state.create_nodevalue(1) + b = state.create_node() + c = state.create_edge(a, b) + d = state.create_edge(c, b) + assert a is not None + assert b is not None + assert c is not None + assert d is not None + + n = state.delete_node(a) + assert n is None + + d = state.read_value(a) + assert d is None + + s, t = state.read_edge(c) + assert s is None + assert t is None + + s, t = state.read_edge(d) + assert s is None + assert t is None + + d = state.read_outgoing(b) + assert d is not None + assert set(d) == set([]) + + +@pytest.mark.usefixtures("state") +def test_delete_node_remove_edge_recursive_deep(state): + a = state.create_node() + b = state.create_node() + c = state.create_node() + d = state.create_edge(a, b) + e = state.create_edge(d, c) + f = state.create_node() + g = state.create_edge(f, e) + h = state.create_edge(b, c) + assert a is not None + assert b is not None + assert c is not None + assert d is not None + assert e is not None + assert f is not None + assert g is not None + assert h is not None + + n = state.delete_node(a) + assert n is None + + l = state.read_outgoing(a) + assert l is None + + l = state.read_incoming(a) + assert l is None + + l = state.read_outgoing(b) + assert l is not None + assert set(l) == set([h]) + + l = state.read_incoming(b) + assert l is not None + assert set(l) == set([]) + + l = state.read_outgoing(c) + assert l is not None + assert set(l) == set([]) + + l = state.read_incoming(c) + assert l is not None + assert set(l) == set([h]) + + s, t = state.read_edge(d) + assert s is None + assert t is None + + s, t = state.read_edge(e) + assert s is None + assert t is None + + s, t = state.read_edge(g) + assert s is None + assert t is None + + l = state.read_outgoing(f) + assert l is not None + assert set(l) == set([]) + + l = state.read_incoming(f) + assert l is not None + assert set(l) == set([]) + + s, t = state.read_edge(h) + assert s == b + assert t == c diff --git a/state/test/test_read_dict.py b/state/test/test_read_dict.py new file mode 100644 index 0000000..22b3606 --- /dev/null +++ b/state/test/test_read_dict.py @@ -0,0 +1,94 @@ +import pytest + + +@pytest.mark.usefixtures("state") +def test_read_dict_no_exists(state): + assert state.read_dict(-1, "abc") is None + + +@pytest.mark.usefixtures("state") +def test_read_dict_not_found_node(state): + a = state.create_node() + assert a is not None + + # Passing data is not enforced, as the data will be interpreted if necessary + assert state.read_dict(a, "abc") is None + + +@pytest.mark.usefixtures("state") +def test_read_dict_not_found_nodevalue(state): + a = state.create_nodevalue(1) + assert a is not None + + # Passing data is not enforced, as the data will be interpreted if necessary + assert state.read_dict(a, "abc") is None + + +@pytest.mark.usefixtures("state") +def test_read_dict_not_found_edge(state): + a = state.create_node() + b = state.create_node() + c = state.create_edge(a, b) + assert a is not None + assert b is not None + assert c is not None + + # Passing data is not enforced, as the data will be interpreted if necessary + assert state.read_dict(c, "abc") is None + + +@pytest.mark.usefixtures("state") +def test_read_dict_no_primitive(state): + a = state.create_node() + assert a is not None + + # Passing data is not enforced, as the data will be interpreted if necessary + assert state.read_dict(a, a) is None + + +@pytest.mark.usefixtures("state") +def test_read_dict_node_simple(state): + a = state.create_node() + b = state.create_node() + c = state.create_nodevalue("f") + d = state.create_edge(a, b) + e = state.create_edge(d, c) + assert a is not None + assert b is not None + assert c is not None + assert d is not None + assert e is not None + + l = state.read_dict(a, "f") + assert l == b + + +@pytest.mark.usefixtures("state") +def test_read_dict_node_multi(state): + a = state.create_node() + b = state.create_node() + c = state.create_nodevalue("f") + d = state.create_edge(a, b) + e = state.create_edge(d, c) + assert a is not None + assert b is not None + assert c is not None + assert d is not None + assert e is not None + + g = state.create_node() + h = state.create_nodevalue("k") + i = state.create_edge(a, g) + j = state.create_edge(i, h) + assert g is not None + assert h is not None + assert i is not None + assert j is not None + + l = state.read_dict(a, "f") + assert l == b + + l = state.read_dict(a, "k") + assert l == g + + assert state.read_dict(a, "l") is None diff --git a/state/test/test_read_dict_edge.py b/state/test/test_read_dict_edge.py new file mode 100644 index 0000000..52d8a53 --- /dev/null +++ b/state/test/test_read_dict_edge.py @@ -0,0 +1,94 @@ +import pytest + + +@pytest.mark.usefixtures("state") +def test_read_dict_edge_no_exists(state): + assert state.read_dict_edge(-1, "abc") is None + + +@pytest.mark.usefixtures("state") +def test_read_dict_edge_not_found_node(state): + a = state.create_node() + assert a is not None + + # Passing data is not enforced, as the data will be interpreted if necessary + assert state.read_dict_edge(a, "abc") is None + + +@pytest.mark.usefixtures("state") +def test_read_dict_edge_not_found_nodevalue(state): + a = state.create_nodevalue(1) + assert a is not None + + # Passing data is not enforced, as the data will be interpreted if necessary + assert state.read_dict_edge(a, "abc") is None + + +@pytest.mark.usefixtures("state") +def test_read_dict_edge_not_found_edge(state): + a = state.create_node() + b = state.create_node() + c = state.create_edge(a, b) + assert a is not None + assert b is not None + assert c is not None + + # Passing data is not enforced, as the data will be interpreted if necessary + assert state.read_dict_edge(c, "abc") is None + + +@pytest.mark.usefixtures("state") +def test_read_dict_edge_no_primitive(state): + a = state.create_node() + assert a is not None + + # Passing data is not enforced, as the data will be interpreted if necessary + assert state.read_dict_edge(a, a) is None + + +@pytest.mark.usefixtures("state") +def test_read_dict_edge_node_simple(state): + a = state.create_node() + b = state.create_node() + c = state.create_nodevalue("f") + d = state.create_edge(a, b) + e = state.create_edge(d, c) + assert a is not None + assert b is not None + assert c is not None + assert d is not None + assert e is not None + + l = state.read_dict_edge(a, "f") + assert l == d + + +@pytest.mark.usefixtures("state") +def test_read_dict_edge_node_multi(state): + a = state.create_node() + b = state.create_node() + c = state.create_nodevalue("f") + d = state.create_edge(a, b) + e = state.create_edge(d, c) + assert a is not None + assert b is not None + assert c is not None + assert d is not None + assert e is not None + + g = state.create_node() + h = state.create_nodevalue("k") + i = state.create_edge(a, g) + j = state.create_edge(i, h) + assert g is not None + assert h is not None + assert i is not None + assert j is not None + + l = state.read_dict_edge(a, "f") + assert l == d + + l = state.read_dict_edge(a, "k") + assert l == i + + assert state.read_dict_edge(a, "l") is None diff --git a/state/test/test_read_dict_keys.py b/state/test/test_read_dict_keys.py new file mode 100644 index 0000000..baa32f6 --- /dev/null +++ b/state/test/test_read_dict_keys.py @@ -0,0 +1,51 @@ +import pytest + + +@pytest.mark.usefixtures("state") +def test_read_dict_keys_no_exists(state): + assert state.read_dict_keys(100000) is None + + +@pytest.mark.usefixtures("state") +def test_read_dict_keys_simple(state): + a = state.create_node() + b = state.create_node() + c = state.create_nodevalue("f") + d = state.create_edge(a, b) + e = state.create_edge(d, c) + assert a is not None + assert b is not None + assert c is not None + assert d is not None + assert e is not None + + l = state.read_dict_keys(a) + assert l is not None + assert set(l) == set([c]) + + +@pytest.mark.usefixtures("state") +def test_read_dict_keys_multi(state): + a = state.create_node() + b = state.create_node() + c = state.create_nodevalue("f") + d = state.create_edge(a, b) + e = state.create_edge(d, c) + assert a is not None + assert b is not None + assert c is not None + assert d is not None + assert e is not None + + g = state.create_node() + h = state.create_nodevalue("k") + i = state.create_edge(a, g) + j = state.create_edge(i, h) + assert g is not None + assert h is not None + assert i is not None + assert j is not None + + l = state.read_dict_keys(a) + assert l is not None + assert set(l) == set([c, h]) diff --git a/state/test/test_read_dict_node.py b/state/test/test_read_dict_node.py new file mode 100644 index 0000000..55d3782 --- /dev/null +++ b/state/test/test_read_dict_node.py @@ -0,0 +1,74 @@ +import pytest + + +@pytest.mark.usefixtures("state") +def test_read_dict_node_no_exists(state): + assert state.read_dict_node(-1, "abc") is None + + +@pytest.mark.usefixtures("state") +def test_read_dict_node_not_found_edge(state): + a = state.create_node() + b = state.create_node() + c = state.create_edge(a, b) + assert a is not None + assert b is not None + assert c is not None + + # Passing data is not enforced, as the data will be interpreted if necessary + assert state.read_dict_node(c, "abc") is None + + +@pytest.mark.usefixtures("state") +def test_read_dict_node_no_primitive(state): + a = state.create_node() + assert a is not None + + # Passing data is not enforced, as the data will be interpreted if necessary + assert state.read_dict_node(a, a) is None + + +@pytest.mark.usefixtures("state") +def test_read_dict_node_node_simple(state): + a = state.create_node() + b = state.create_node() + c = state.create_node() + d = state.create_edge(a, b) + e = state.create_edge(d, c) + assert a is not None + assert b is not None + assert c is not None + assert d is not None + assert e is not None + + l = state.read_dict_node(a, c) + assert l == b + + +@pytest.mark.usefixtures("state") +def test_read_dict_node_multi(state): + a = state.create_node() + b = state.create_node() + c = state.create_node() + d = state.create_edge(a, b) + e = state.create_edge(d, c) + assert a is not None + assert b is not None + assert c is not None + assert d is not None + assert e is not None + + g = state.create_node() + h = state.create_node() + i = state.create_edge(a, g) + j = state.create_edge(i, h) + assert g is not None + assert h is not None + assert i is not None + assert j is not None + + l = state.read_dict_node(a, c) + assert l == b + + l = state.read_dict_node(a, h) + assert l == g diff --git a/state/test/test_read_edge.py b/state/test/test_read_edge.py new file mode 100644 index 0000000..e477c2c --- /dev/null +++ b/state/test/test_read_edge.py @@ -0,0 +1,136 @@ +import pytest + + +@pytest.mark.usefixtures("state") +def test_read_edge_node(state): + b = state.create_node() + assert b is not None + + s, t = state.read_edge(b) + assert s is None + assert t is None + + +@pytest.mark.usefixtures("state") +def test_read_edge_no_exists(state): + s, t = state.read_edge(-1) + assert s is None + assert t is None + + +@pytest.mark.usefixtures("state") +def test_read_edge_nodevalue(state): + b = state.create_nodevalue(1) + assert b is not None + + s, t = state.read_edge(b) + assert s is None + assert t is None + + +@pytest.mark.usefixtures("state") +def test_read_edge_normal(state): + a = state.create_node() + b = state.create_node() + c = state.create_edge(a, b) + assert a is not None + assert b is not None + assert c is not None + + s, t = state.read_edge(c) + assert s == a + assert t == b + + +@pytest.mark.usefixtures("state") +def test_read_edge_edge_to_edge(state): + a = state.create_node() + b = state.create_node() + c = state.create_edge(a, b) + d = state.create_edge(a, b) + e = state.create_edge(c, d) + assert a is not None + assert b is not None + assert c is not None + assert d is not None + assert e is not None + + s, t = state.read_edge(c) + assert s == a + assert t == b + + s, t = state.read_edge(d) + assert s == a + assert t == b + + s, t = state.read_edge(e) + assert s == c + assert t == d + + +@pytest.mark.usefixtures("state") +def test_read_edge_edge_to_node(state): + a = state.create_node() + b = state.create_node() + c = state.create_edge(a, b) + d = state.create_edge(c, b) + assert a is not None + assert b is not None + assert c is not None + assert d is not None + + s, t = state.read_edge(c) + assert s == a + assert t == b + + s, t = state.read_edge(d) + assert s == c + assert t == b + + +@pytest.mark.usefixtures("state") +def test_read_edge_node_to_edge(state): + a = state.create_node() + b = state.create_node() + c = state.create_edge(a, b) + d = state.create_edge(b, c) + assert a is not None + assert b is not None + assert c is not None + assert d is not None + + s, t = state.read_edge(c) + assert s == a + assert t == b + + s, t = state.read_edge(d) + assert s == b + assert t == c + + +@pytest.mark.usefixtures("state") +def test_read_edge_node_to_nodevalue(state): + a = state.create_node() + b = state.create_nodevalue(1) + c = state.create_edge(a, b) + assert a is not None + assert b is not None + assert c is not None + + s, t = state.read_edge(c) + assert s == a + assert t == b + + +@pytest.mark.usefixtures("state") +def test_read_edge_nodevalue_to_nodevalue(state): + a = state.create_nodevalue(1) + b = state.create_nodevalue(1) + c = state.create_edge(a, b) + assert a is not None + assert b is not None + assert c is not None + + s, t = state.read_edge(c) + assert s == a + assert t == b diff --git a/state/test/test_read_incoming.py b/state/test/test_read_incoming.py new file mode 100644 index 0000000..b5a7a26 --- /dev/null +++ b/state/test/test_read_incoming.py @@ -0,0 +1,275 @@ +import pytest + + +@pytest.mark.usefixtures("state") +def test_read_incoming_node_none(state): + b = state.create_node() + assert b is not None + + l = state.read_incoming(b) + assert l is not None + assert set(l) == set([]) + + +@pytest.mark.usefixtures("state") +def test_read_incoming_node_one(state): + a = state.create_node() + b = state.create_node() + c = state.create_edge(a, b) + assert a is not None + assert b is not None + assert c is not None + + l = state.read_incoming(a) + assert l is not None + assert set(l) == set([]) + + l = state.read_incoming(b) + assert l is not None + assert set(l) == set([c]) + + +@pytest.mark.usefixtures("state") +def test_read_incoming_node_multi(state): + a = state.create_node() + b = state.create_node() + c = state.create_edge(a, b) + d = state.create_edge(a, b) + e = state.create_edge(a, b) + assert a is not None + assert b is not None + assert c is not None + assert d is not None + assert e is not None + + l = state.read_incoming(a) + assert l is not None + assert set(l) == set([]) + + l = state.read_incoming(b) + assert l is not None + assert set(l) == set([c, d, e]) + + +@pytest.mark.usefixtures("state") +def test_read_incoming_node_multi_others_unaffected(state): + a = state.create_node() + b = state.create_node() + c = state.create_edge(a, b) + d = state.create_edge(a, b) + e = state.create_edge(a, b) + assert a is not None + assert b is not None + assert c is not None + assert d is not None + assert e is not None + + f = state.create_node() + assert f is not None + + l = state.read_incoming(a) + assert l is not None + assert set(l) == set([]) + + l = state.read_incoming(b) + assert l is not None + assert set(l) == set([c, d, e]) + + l = state.read_incoming(f) + assert l is not None + assert set(l) == set([]) + + +@pytest.mark.usefixtures("state") +def test_read_incoming_edge_none(state): + a = state.create_node() + b = state.create_node() + c = state.create_edge(a, b) + assert a is not None + assert b is not None + assert c is not None + + l = state.read_incoming(c) + assert l is not None + assert set(l) == set([]) + + +@pytest.mark.usefixtures("state") +def test_read_incoming_edge_one(state): + a = state.create_node() + b = state.create_node() + c = state.create_edge(a, b) + d = state.create_edge(c, a) + e = state.create_edge(a, c) + assert a is not None + assert b is not None + assert c is not None + assert d is not None + assert e is not None + + l = state.read_incoming(c) + assert l is not None + assert set(l) == set([e]) + + l = state.read_incoming(d) + assert l is not None + assert set(l) == set([]) + + l = state.read_incoming(e) + assert l is not None + assert set(l) == set([]) + + +@pytest.mark.usefixtures("state") +def test_read_incoming_edge_multi(state): + a = state.create_node() + b = state.create_node() + c = state.create_edge(a, b) + d = state.create_edge(a, c) + e = state.create_edge(b, c) + f = state.create_edge(d, c) + assert a is not None + assert b is not None + assert c is not None + assert d is not None + assert e is not None + assert f is not None + + l = state.read_incoming(b) + assert l is not None + assert set(l) == set([c]) + + l = state.read_incoming(c) + assert l is not None + assert set(l) == set([d, e, f]) + + l = state.read_incoming(d) + assert l is not None + assert set(l) == set([]) + + l = state.read_incoming(e) + assert l is not None + assert set(l) == set([]) + + l = state.read_incoming(f) + assert l is not None + assert set(l) == set([]) + + +@pytest.mark.usefixtures("state") +def test_read_incoming_nodevalue_none(state): + b = state.create_nodevalue(1) + assert b is not None + + l = state.read_incoming(b) + assert l is not None + assert set(l) == set([]) + + +@pytest.mark.usefixtures("state") +def test_read_incoming_nodevalue_one(state): + a = state.create_nodevalue(1) + b = state.create_node() + c = state.create_edge(b, a) + assert a is not None + assert b is not None + assert c is not None + + l = state.read_incoming(a) + assert l is not None + assert set(l) == set([c]) + + l = state.read_incoming(b) + assert l is not None + assert set(l) == set([]) + + +@pytest.mark.usefixtures("state") +def test_read_incoming_nodevalue_multi(state): + a = state.create_nodevalue(1) + b = state.create_node() + c = state.create_edge(b, a) + d = state.create_edge(b, a) + e = state.create_edge(b, a) + assert a is not None + assert b is not None + assert c is not None + assert d is not None + assert e is not None + + l = state.read_incoming(a) + assert l is not None + assert set(l) == set([c, d, e]) + + l = state.read_incoming(b) + assert l is not None + assert set(l) == set([]) + + +@pytest.mark.usefixtures("state") +def test_read_incoming_nodevalue_multi_others_unaffected(state): + a = state.create_nodevalue(1) + b = state.create_node() + c = state.create_edge(b, a) + d = state.create_edge(b, a) + e = state.create_edge(b, a) + assert a is not None + assert b is not None + assert c is not None + assert d is not None + assert e is not None + + f = state.create_nodevalue(1) + assert f is not None + + l = state.read_incoming(a) + assert l is not None + assert set(l) == set([c, d, e]) + + l = state.read_incoming(b) + assert l is not None + assert set(l) == set([]) + + l = state.read_incoming(f) + assert l is not None + assert set(l) == set([]) + + +@pytest.mark.usefixtures("state") +def test_read_incoming_node_deleted(state): + b = state.create_node() + assert b is not None + + n = state.delete_node(b) + assert n is None + + l = state.read_incoming(b) + assert l is None + + +@pytest.mark.usefixtures("state") +def test_read_incoming_nodevalue_deleted(state): + b = state.create_nodevalue(1) + assert b is not None + + n = state.delete_node(b) + assert n is None + + l = state.read_incoming(b) + assert l is None + + +@pytest.mark.usefixtures("state") +def test_read_incoming_edge_deleted(state): + a = state.create_node() + b = state.create_node() + c = state.create_edge(a, b) + assert a is not None + assert b is not None + assert c is not None + + n = state.delete_edge(c) + assert n is None + + l = state.read_incoming(c) + assert l is None diff --git a/state/test/test_read_outgoing.py b/state/test/test_read_outgoing.py new file mode 100644 index 0000000..1aa217e --- /dev/null +++ b/state/test/test_read_outgoing.py @@ -0,0 +1,265 @@ +import pytest + + +@pytest.mark.usefixtures("state") +def test_read_outgoing_node_none(state): + b = state.create_node() + assert b is not None + + l = state.read_outgoing(b) + assert l is not None + assert set(l) == set([]) + + +@pytest.mark.usefixtures("state") +def test_read_outgoing_node_one(state): + a = state.create_node() + b = state.create_node() + c = state.create_edge(a, b) + assert a is not None + assert b is not None + assert c is not None + + l = state.read_outgoing(a) + assert l is not None + assert set(l) == set([c]) + + l = state.read_outgoing(b) + assert l is not None + assert set(l) == set([]) + + +@pytest.mark.usefixtures("state") +def test_read_outgoing_node_multi(state): + a = state.create_node() + b = state.create_node() + c = state.create_edge(a, b) + d = state.create_edge(a, b) + e = state.create_edge(a, b) + assert a is not None + assert b is not None + assert c is not None + assert d is not None + assert e is not None + + l = state.read_outgoing(a) + assert l is not None + assert set(l) == set([c, d, e]) + + l = state.read_outgoing(b) + assert l is not None + assert set(l) == set([]) + + +@pytest.mark.usefixtures("state") +def test_read_outgoing_node_multi_others_unaffected(state): + a = state.create_node() + b = state.create_node() + c = state.create_edge(a, b) + d = state.create_edge(a, b) + e = state.create_edge(a, b) + assert a is not None + assert b is not None + assert c is not None + assert d is not None + assert e is not None + + f = state.create_node() + assert f is not None + + l = state.read_outgoing(a) + assert l is not None + assert set(l) == set([c, d, e]) + + l = state.read_outgoing(b) + assert l is not None + assert set(l) == set([]) + + l = state.read_outgoing(f) + assert l is not None + assert set(l) == set([]) + + +@pytest.mark.usefixtures("state") +def test_read_outgoing_edge_none(state): + a = state.create_node() + b = state.create_node() + c = state.create_edge(a, b) + assert a is not None + assert b is not None + assert c is not None + + l = state.read_outgoing(c) + assert l is not None + assert set(l) == set([]) + + +@pytest.mark.usefixtures("state") +def test_read_outgoing_edge_one(state): + a = state.create_node() + b = state.create_node() + c = state.create_edge(a, b) + d = state.create_edge(c, a) + assert a is not None + assert b is not None + assert c is not None + assert d is not None + + l = state.read_outgoing(c) + assert l is not None + assert set(l) == set([d]) + + l = state.read_outgoing(d) + assert l is not None + assert set(l) == set([]) + + +@pytest.mark.usefixtures("state") +def test_read_outgoing_edge_multi(state): + a = state.create_node() + b = state.create_node() + c = state.create_edge(a, b) + d = state.create_edge(c, a) + e = state.create_edge(c, b) + f = state.create_edge(c, d) + assert a is not None + assert b is not None + assert c is not None + assert d is not None + assert e is not None + assert f is not None + + l = state.read_outgoing(c) + assert l is not None + assert set(l) == set([d, e, f]) + + l = state.read_outgoing(d) + assert l is not None + assert set(l) == set([]) + + l = state.read_outgoing(e) + assert l is not None + assert set(l) == set([]) + + l = state.read_outgoing(f) + assert l is not None + assert set(l) == set([]) + + +@pytest.mark.usefixtures("state") +def test_read_outgoing_nodevalue_none(state): + b = state.create_nodevalue(1) + assert b is not None + + l = state.read_outgoing(b) + assert l is not None + assert set(l) == set([]) + + +@pytest.mark.usefixtures("state") +def test_read_outgoing_nodevalue_one(state): + a = state.create_nodevalue(1) + b = state.create_node() + c = state.create_edge(a, b) + assert a is not None + assert b is not None + assert c is not None + + l = state.read_outgoing(a) + assert l is not None + assert set(l) == set([c]) + + l = state.read_outgoing(b) + assert l is not None + assert set(l) == set([]) + + +@pytest.mark.usefixtures("state") +def test_read_outgoing_nodevalue_multi(state): + a = state.create_nodevalue(1) + b = state.create_node() + c = state.create_edge(a, b) + d = state.create_edge(a, b) + e = state.create_edge(a, b) + assert a is not None + assert b is not None + assert c is not None + assert d is not None + assert e is not None + + l = state.read_outgoing(a) + assert l is not None + assert set(l) == set([c, d, e]) + + l = state.read_outgoing(b) + assert l is not None + assert set(l) == set([]) + + +@pytest.mark.usefixtures("state") +def test_read_outgoing_nodevalue_multi_others_unaffected(state): + a = state.create_nodevalue(1) + b = state.create_node() + c = state.create_edge(a, b) + d = state.create_edge(a, b) + e = state.create_edge(a, b) + assert a is not None + assert b is not None + assert c is not None + assert d is not None + assert e is not None + + f = state.create_nodevalue(1) + assert f is not None + + l = state.read_outgoing(a) + assert l is not None + assert set(l) == set([c, d, e]) + + l = state.read_outgoing(b) + assert l is not None + assert set(l) == set([]) + + l = state.read_outgoing(f) + assert l is not None + assert set(l) == set([]) + + +@pytest.mark.usefixtures("state") +def test_read_outgoing_node_deleted(state): + b = state.create_node() + assert b is not None + + n = state.delete_node(b) + assert n is None + + l = state.read_outgoing(b) + assert l is None + + +@pytest.mark.usefixtures("state") +def test_read_outgoing_nodevalue_deleted(state): + b = state.create_nodevalue(1) + assert b is not None + + n = state.delete_node(b) + assert n is None + + l = state.read_outgoing(b) + assert l is None + + +@pytest.mark.usefixtures("state") +def test_read_outgoing_edge_deleted(state): + a = state.create_node() + b = state.create_node() + c = state.create_edge(a, b) + assert a is not None + assert b is not None + assert c is not None + + n = state.delete_edge(c) + assert n is None + + l = state.read_outgoing(c) + assert l is None diff --git a/state/test/test_read_reverse_dict.py b/state/test/test_read_reverse_dict.py new file mode 100644 index 0000000..313563f --- /dev/null +++ b/state/test/test_read_reverse_dict.py @@ -0,0 +1,165 @@ +import pytest + + +@pytest.mark.usefixtures("state") +def test_read_reverse_dict_no_exists(state): + l = state.read_reverse_dict(-1, "abc") + assert l is None + + +@pytest.mark.usefixtures("state") +def test_read_reverse_dict_not_found_node(state): + a = state.create_node() + assert a is not None + + # Passing data is not enforced, as the data will be interpreted if necessary + l = state.read_reverse_dict(a, "abc") + assert l == [] + + +@pytest.mark.usefixtures("state") +def test_read_reverse_dict_not_found_nodevalue(state): + a = state.create_nodevalue(1) + assert a is not None + + # Passing data is not enforced, as the data will be interpreted if necessary + l = state.read_reverse_dict(a, "abc") + assert l == [] + + +@pytest.mark.usefixtures("state") +def test_read_reverse_dict_not_found_edge(state): + a = state.create_node() + b = state.create_node() + c = state.create_edge(a, b) + assert a is not None + assert b is not None + assert c is not None + + # Passing data is not enforced, as the data will be interpreted if necessary + l = state.read_reverse_dict(c, "abc") + assert l == [] + + +@pytest.mark.usefixtures("state") +def test_read_reverse_dict_no_primitive(state): + a = state.create_node() + assert a is not None + + # Passing data is not enforced, as the data will be interpreted if necessary + l = state.read_reverse_dict(a, a) + assert l == [] + + +@pytest.mark.usefixtures("state") +def test_read_reverse_dict_node_simple(state): + a = state.create_node() + b = state.create_node() + c = state.create_nodevalue("f") + d = state.create_edge(a, b) + e = state.create_edge(d, c) + assert a is not None + assert b is not None + assert c is not None + assert d is not None + assert e is not None + + l = state.read_reverse_dict(b, "f") + assert set(l) == set([a]) + + +@pytest.mark.usefixtures("state") +def test_read_reverse_dict_no_match(state): + a = state.create_node() + b = state.create_node() + c = state.create_nodevalue("g") + d = state.create_edge(a, b) + e = state.create_edge(d, c) + assert a is not None + assert b is not None + assert c is not None + assert d is not None + assert e is not None + + l = state.read_reverse_dict(b, "f") + assert l == [] + + +@pytest.mark.usefixtures("state") +def test_read_reverse_dict_node_multi(state): + a = state.create_node() + b = state.create_node() + c = state.create_nodevalue("f") + d = state.create_edge(a, b) + e = state.create_edge(d, c) + assert a is not None + assert b is not None + assert c is not None + assert d is not None + assert e is not None + + g = state.create_node() + h = state.create_nodevalue("k") + i = state.create_edge(a, g) + j = state.create_edge(i, h) + assert g is not None + assert h is not None + assert i is not None + assert j is not None + + l = state.read_reverse_dict(b, "f") + assert set(l) == set([a]) + + l = state.read_reverse_dict(g, "k") + assert set(l) == set([a]) + + l = state.read_reverse_dict(a, "l") + assert l == [] + + +@pytest.mark.usefixtures("state") +def test_read_reverse_dict_node_multi_ambiguous(state): + a = state.create_node() + b = state.create_node() + c = state.create_nodevalue("f") + d = state.create_edge(b, a) + e = state.create_edge(d, c) + assert a is not None + assert b is not None + assert c is not None + assert d is not None + assert e is not None + + g = state.create_node() + h = state.create_nodevalue("f") + i = state.create_edge(g, a) + j = state.create_edge(i, h) + assert g is not None + assert h is not None + assert i is not None + assert j is not None + + l = state.read_reverse_dict(a, "f") + assert set(l) == set([b, g]) + + +@pytest.mark.usefixtures("state") +def test_read_reverse_dict_node_uncertain(state): + a = state.create_node() + b = state.create_node() + c = state.create_nodevalue("f") + d = state.create_edge(a, b) + e = state.create_edge(d, c) + assert a is not None + assert b is not None + assert c is not None + assert d is not None + assert e is not None + + h = state.create_nodevalue("g") + i = state.create_edge(d, h) + assert h is not None + assert i is not None + + l = state.read_reverse_dict(b, "f") + assert set(l) == set([a]) diff --git a/state/test/test_read_value.py b/state/test/test_read_value.py new file mode 100644 index 0000000..9aa7d66 --- /dev/null +++ b/state/test/test_read_value.py @@ -0,0 +1,88 @@ +import pytest + + +@pytest.mark.usefixtures("state") +def test_read_value_different_id_simple(state): + id1 = state.create_nodevalue(1) + id2 = state.create_nodevalue(2) + assert id1 is not None + assert id2 is not None + + v1 = state.read_value(id1) + v2 = state.read_value(id2) + assert v1 == 1 + assert v2 == 2 + + +@pytest.mark.usefixtures("state") +def test_read_value_integer_ib_negative(state): + # Just within range + for i in range(-2 ** 63, -2 ** 63 + 10): + id1 = state.create_nodevalue(i) + assert id1 is not None + + v = state.read_value(id1) + assert v == i + + +@pytest.mark.usefixtures("state") +def test_read_value_integer_ib_zero(state): + # Nicely within range + for i in range(-10, 10): + id1 = state.create_nodevalue(i) + assert id1 is not None + + v = state.read_value(id1) + assert v == i + + +@pytest.mark.usefixtures("state") +def test_read_value_integer_ib_positive(state): + # Just within range + for i in range(2 ** 63 - 10, 2 ** 63): + id1 = state.create_nodevalue(i) + assert id1 is not None + + v = state.read_value(id1) + assert v == i + + +@pytest.mark.usefixtures("state") +def test_read_value_boolean(state): + id1 = state.create_nodevalue(True) + id2 = state.create_nodevalue(False) + assert id1 is not None + assert id2 is not None + + v1 = state.read_value(id1) + v2 = state.read_value(id2) + assert v1 == True + assert v2 == False + + +@pytest.mark.usefixtures("state") +def test_read_nodevalue_boolean_same(state): + id1 = state.create_nodevalue(True) + id2 = state.create_nodevalue(True) + assert id1 is not None + assert id2 is not None + + v1 = state.read_value(id1) + v2 = state.read_value(id2) + assert v1 == True + assert v2 == True + + +@pytest.mark.usefixtures("state") +def test_read_value_no_exist(state): + v1 = state.read_value(100000) + assert v1 is None + + +@pytest.mark.usefixtures("state") +def test_read_value_no_value(state): + id1 = state.create_node() + assert id1 is not None + + v1 = state.read_value(id1) + assert v1 is None From 128b3ec1e40633ad319150c44d6de0984fb927da Mon Sep 17 00:00:00 2001 From: Andrei Bondarenko Date: Tue, 13 Jul 2021 05:08:31 +0200 Subject: [PATCH 10/46] Added support to state for type nodes --- state/base.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/state/base.py b/state/base.py index e33c0e7..42b92aa 100644 --- a/state/base.py +++ b/state/base.py @@ -1,8 +1,9 @@ from abc import ABC, abstractmethod -from typing import Any, List, Tuple, TypeVar, Optional, Union, Generator +from typing import Any, List, Tuple, Optional, Union import uuid primitive_types = (int, float, str, bool) +type_values = ("Integer", "Float", "String", "Boolean", "Type") Node = str @@ -36,6 +37,8 @@ class State(ABC): Returns: True if value type is supported, False otherwise. """ + if isinstance(value, dict) and value.get("type", None) in type_values: + return True if not isinstance(value, primitive_types): return False elif isinstance(value, int) and not (-2**63 <= value <= 2**63 - 1): @@ -100,8 +103,7 @@ class State(ABC): target: target element of edge Returns: - Tuple containing the created edges and label node, None is source or target don't exist - or value type doesn't exist. First edge goes from source to target, second from edge to label. + Nothing. """ pass From 1ba6aecdb9ed94c5860e93fd185e1a6c61e0a72e Mon Sep 17 00:00:00 2001 From: Andrei Bondarenko Date: Tue, 13 Jul 2021 05:13:55 +0200 Subject: [PATCH 11/46] Added correct printing of type nodes --- state/devstate.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/state/devstate.py b/state/devstate.py index 24f622b..83b50f9 100644 --- a/state/devstate.py +++ b/state/devstate.py @@ -28,8 +28,11 @@ class DevState(PyState): f.write("digraph main {\n") for n in sorted(self.nodes): if n in self.values: + x = self.values[n] + if isinstance(x, dict): + x = f"{x.get('type')}" f.write("\"a_%s\" [label=\"%s\"];\n" % ( - n, str(self.values[n]).replace('"', '\\"'))) + n, str(x).replace('"', '\\"'))) else: f.write("\"a_%s\" [label=\"\"];\n" % n) for i, e in sorted(list(self.edges.items())): From 8905dad9785c28147dfd35aa30240d67be86ba07 Mon Sep 17 00:00:00 2001 From: Andrei Bondarenko Date: Tue, 13 Jul 2021 05:35:26 +0200 Subject: [PATCH 12/46] Added some packages --- README.md | 10 +++++++++- requirements.txt | 1 + requirements_dev.txt | 2 ++ 3 files changed, 12 insertions(+), 1 deletion(-) create mode 100644 requirements_dev.txt diff --git a/README.md b/README.md index aac9945..3c0638f 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,11 @@ # MV2 -This repository contains the code for my take on (a part of) the [Modelverse](https://msdl.uantwerpen.be/git/yentl/modelverse) for my Master's thesis. \ No newline at end of file +This repository contains the code for my take on (a part of) the [Modelverse](https://msdl.uantwerpen.be/git/yentl/modelverse) for my Master's thesis. + +## Development packages + +Some packages were used during development, but are not needed for succesful runtime (e.g. linter, autoformatter). These can be found under `requirements_dev.txt`. + +## Mandatory packages + +Python packages required to succesfully run/test the code in this repository can be found under `requirements.txt`. \ No newline at end of file diff --git a/requirements.txt b/requirements.txt index e69de29..11b890f 100644 --- a/requirements.txt +++ b/requirements.txt @@ -0,0 +1 @@ +pytest==6.2.4 \ No newline at end of file diff --git a/requirements_dev.txt b/requirements_dev.txt new file mode 100644 index 0000000..dcf816b --- /dev/null +++ b/requirements_dev.txt @@ -0,0 +1,2 @@ +autopep8==1.5.7 +mypy==0.910 \ No newline at end of file From 6961ba97cc171bdbd097a782f75fbffae724d945 Mon Sep 17 00:00:00 2001 From: Andrei Bondarenko Date: Tue, 13 Jul 2021 06:13:14 +0200 Subject: [PATCH 13/46] Removed relative imports --- state/devstate.py | 2 +- state/pystate.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/state/devstate.py b/state/devstate.py index 83b50f9..bccb850 100644 --- a/state/devstate.py +++ b/state/devstate.py @@ -1,4 +1,4 @@ -from .pystate import PyState +from state.pystate import PyState class DevState(PyState): diff --git a/state/pystate.py b/state/pystate.py index fff6565..b88a358 100644 --- a/state/pystate.py +++ b/state/pystate.py @@ -1,6 +1,6 @@ from typing import Any, List, Tuple, Optional -from .base import State, Node, Edge, Element +from state.base import State, Node, Edge, Element class PyState(State): From 34881ad4f49a3dbee80088db5725a1d21390f957 Mon Sep 17 00:00:00 2001 From: Andrei Bondarenko Date: Tue, 13 Jul 2021 10:56:55 +0200 Subject: [PATCH 14/46] Fixed string dumping --- state/devstate.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/state/devstate.py b/state/devstate.py index bccb850..3fa3971 100644 --- a/state/devstate.py +++ b/state/devstate.py @@ -31,8 +31,10 @@ class DevState(PyState): x = self.values[n] if isinstance(x, dict): x = f"{x.get('type')}" + else: + x = repr(x) f.write("\"a_%s\" [label=\"%s\"];\n" % ( - n, str(x).replace('"', '\\"'))) + n, x.replace('"', '\\"'))) else: f.write("\"a_%s\" [label=\"\"];\n" % n) for i, e in sorted(list(self.edges.items())): From 6d652c213da840dbdbc607b3a25ddf8020d0fe63 Mon Sep 17 00:00:00 2001 From: Andrei Bondarenko Date: Tue, 13 Jul 2021 19:49:20 +0200 Subject: [PATCH 15/46] Added Element dataclass --- core/element.py | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 core/element.py diff --git a/core/element.py b/core/element.py new file mode 100644 index 0000000..f428528 --- /dev/null +++ b/core/element.py @@ -0,0 +1,31 @@ +from dataclasses import dataclass, asdict, astuple +from typing import TypeVar, Generic, Optional + +# Some typing information for Python static type checkers +Literal = TypeVar('Literal', int, float, bool, str) # Must be int, float, bool or str + + +@dataclass +class Element(Generic[Literal]): + """ + An Element can represent one of following two things, based on the value of its attributes: + + * An element (node or edge) in the State (id is not None). In this case the value can be None because it hasn't + yet been read OR because the element doesn't have a value. + * A value for which a node has not yet been materialized in the State (id is None, value is not None). + + + If you are familiar with the Modelverse Kernel, this class serves a function similar to the {id: ..., value: ...} + dict that is used there. + """ + id: Optional[str] = None + value: Optional[Literal] = None + + def is_none(self) -> bool: + return self.id is None and self.value is None + + +String = Element[str] +Integer = Element[int] +Float = Element[float] +Boolean = Element[bool] From f6747c6929cc2f74bc762be00d80b6601864688e Mon Sep 17 00:00:00 2001 From: Andrei Bondarenko Date: Tue, 13 Jul 2021 19:50:49 +0200 Subject: [PATCH 16/46] initial bottom implementation --- core/__init__.py | 0 core/bottom.py | 93 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 93 insertions(+) create mode 100644 core/__init__.py create mode 100644 core/bottom.py diff --git a/core/__init__.py b/core/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/core/bottom.py b/core/bottom.py new file mode 100644 index 0000000..16fde5e --- /dev/null +++ b/core/bottom.py @@ -0,0 +1,93 @@ +from state.base import State, INTEGER, FLOAT, STRING, BOOLEAN, TYPE, NODE, EDGE +from core.element import Element, String + + +class Bottom: + def __init__(self, state: State) -> None: + self.state = state + + def create_model(self, name: String) -> Element: + bottom = self.state.create_nodevalue(name.value) + self.state.create_dict(bottom, "Model", self.state.create_node()) + return Element(id=bottom) + + def add_node(self, model: Element, name: String): + model_root = self.state.read_dict(model.id, "Model") + # create model element + element = self.state.create_node() + # connect to model root + element_edge = self.state.create_edge(model_root, element) + # label edge with provided name + element_label = self.state.create_nodevalue(name.value) + if element_label is None: + print(f"Warning: Invalid name {name.value}, element not created.") + return + self.state.create_edge(element_edge, element_label) + # add ltm-bottom typing + element_type = self.state.create_nodevalue(NODE) + self.state.create_dict(element_label, TYPE, element_type) + + def add_value(self, model: Element, name: String, value: Element): + type_map = { + int: INTEGER, + float: FLOAT, + bool: BOOLEAN, + str: STRING + } + model_root = self.state.read_dict(model.id, "Model") + # create model element + element = self.state.create_nodevalue(value.value) + if element is None: + print("Warning: Invalid value, value node not created.") + return + # connect to model root + element_edge = self.state.create_edge(model_root, element) + # label edge with provided name + element_label = self.state.create_nodevalue(name.value) + if element_label is None: + print(f"Warning: Invalid name {name.value}, element not created.") + return + self.state.create_edge(element_edge, element_label) + # add ltm-bottom typing + element_type = self.state.create_nodevalue(type_map[type(value.value)]) + self.state.create_dict(element_label, TYPE, element_type) + + def add_edge(self, model: Element, name: String, source: String, target: String): + model_root = self.state.read_dict(model.id, "Model") + source_element = self.state.read_dict(model_root, source.value) + if source_element is None: + print(f"Warning: Unknown source element {source.value}, edge not created.") + return + target_element = self.state.read_dict(model_root, target.value) + if target_element is None: + print(f"Warning: Unknown target element {target.value}, edge not created.") + return + # create model element + element = self.state.create_edge(source_element, target_element) + # connect to model root + element_edge = self.state.create_edge(model_root, element) + # label edge with provided name + element_label = self.state.create_nodevalue(name.value) + if element_label is None: + print(f"Warning: Invalid name {name.value}, element not created.") + return + self.state.create_edge(element_edge, element_label) + # add ltm-bottom typing + element_type = self.state.create_nodevalue(EDGE) + self.state.create_dict(element_label, TYPE, element_type) + + def get_element(self, model: Element, name: String) -> Element: + model_root = self.state.read_dict(model.id, "Model") + element = self.state.read_dict(model_root, name.value) + if element is None: + print(f"Warning: Unknown element {name.value}.") + return Element() + else: + return Element(id=element, value=self.state.read_value(element)) + + def delete_element(self, model: Element, name: String): + model_root = self.state.read_dict(model.id, "Model") + element = self.state.read_dict(model_root, name.value) + # could be both a node or an edge + self.state.delete_node(element) + self.state.delete_edge(element) From 5a548d5c3e0a7a1dde0a564ae403a07a27824fac Mon Sep 17 00:00:00 2001 From: Andrei Bondarenko Date: Tue, 13 Jul 2021 19:51:24 +0200 Subject: [PATCH 17/46] Added type value constants --- state/base.py | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/state/base.py b/state/base.py index 42b92aa..6a5b28b 100644 --- a/state/base.py +++ b/state/base.py @@ -3,7 +3,14 @@ from typing import Any, List, Tuple, Optional, Union import uuid primitive_types = (int, float, str, bool) -type_values = ("Integer", "Float", "String", "Boolean", "Type") +INTEGER = {"type": "Integer"} +FLOAT = {"type": "Float"} +STRING = {"type": "String"} +BOOLEAN = {"type": "Boolean"} +TYPE = {"type": "Type"} +NODE = {"type": "Node"} +EDGE = {"type": "Edge"} +type_values = (INTEGER, FLOAT, STRING, BOOLEAN, TYPE, NODE, EDGE) Node = str @@ -37,7 +44,7 @@ class State(ABC): Returns: True if value type is supported, False otherwise. """ - if isinstance(value, dict) and value.get("type", None) in type_values: + if isinstance(value, dict) and value in type_values: return True if not isinstance(value, primitive_types): return False From 24119609b3c70627669a037d9533e4cb0b7661a2 Mon Sep 17 00:00:00 2001 From: Andrei Bondarenko Date: Tue, 13 Jul 2021 19:54:13 +0200 Subject: [PATCH 18/46] Updated tests --- state/test/test_create_nodevalue.py | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/state/test/test_create_nodevalue.py b/state/test/test_create_nodevalue.py index 9ee275f..76bd7b4 100644 --- a/state/test/test_create_nodevalue.py +++ b/state/test/test_create_nodevalue.py @@ -165,3 +165,27 @@ def test_create_nodevalue_string_type(state): v = state.read_value(id1) assert v == {"type": "String"} + + +@pytest.mark.usefixtures("state") +def test_create_nodevalue_node_type(state): + id1 = state.create_nodevalue({"type": "Node"}) + assert id1 is not None + + v = state.read_value(id1) + assert v == {"type": "Node"} + + +@pytest.mark.usefixtures("state") +def test_create_nodevalue_edge_type(state): + id1 = state.create_nodevalue({"type": "Edge"}) + assert id1 is not None + + v = state.read_value(id1) + assert v == {"type": "Edge"} + + +@pytest.mark.usefixtures("state") +def test_create_nodevalue_invalid_type(state): + id1 = state.create_nodevalue({"type": "Class"}) + assert id1 is None From 93b602a0eb4bfa292438c998f258369f060a9ced Mon Sep 17 00:00:00 2001 From: Andrei Bondarenko Date: Wed, 14 Jul 2021 08:41:08 +0200 Subject: [PATCH 19/46] Updated types --- state/base.py | 16 +++---- state/devstate.py | 4 +- state/test/test_create_nodevalue.py | 69 ++++------------------------- 3 files changed, 18 insertions(+), 71 deletions(-) diff --git a/state/base.py b/state/base.py index 6a5b28b..8502f12 100644 --- a/state/base.py +++ b/state/base.py @@ -3,13 +3,13 @@ from typing import Any, List, Tuple, Optional, Union import uuid primitive_types = (int, float, str, bool) -INTEGER = {"type": "Integer"} -FLOAT = {"type": "Float"} -STRING = {"type": "String"} -BOOLEAN = {"type": "Boolean"} -TYPE = {"type": "Type"} -NODE = {"type": "Node"} -EDGE = {"type": "Edge"} +INTEGER = ("Integer",) +FLOAT = ("Float",) +STRING = ("String",) +BOOLEAN = ("Boolean",) +TYPE = ("Type",) +NODE = ("Node",) +EDGE = ("Edge",) type_values = (INTEGER, FLOAT, STRING, BOOLEAN, TYPE, NODE, EDGE) @@ -44,7 +44,7 @@ class State(ABC): Returns: True if value type is supported, False otherwise. """ - if isinstance(value, dict) and value in type_values: + if isinstance(value, tuple) and value in type_values: return True if not isinstance(value, primitive_types): return False diff --git a/state/devstate.py b/state/devstate.py index 3fa3971..798d8d9 100644 --- a/state/devstate.py +++ b/state/devstate.py @@ -29,8 +29,8 @@ class DevState(PyState): for n in sorted(self.nodes): if n in self.values: x = self.values[n] - if isinstance(x, dict): - x = f"{x.get('type')}" + if isinstance(x, tuple): + x = f"{x[0]}" else: x = repr(x) f.write("\"a_%s\" [label=\"%s\"];\n" % ( diff --git a/state/test/test_create_nodevalue.py b/state/test/test_create_nodevalue.py index 76bd7b4..23e12b9 100644 --- a/state/test/test_create_nodevalue.py +++ b/state/test/test_create_nodevalue.py @@ -1,4 +1,5 @@ import pytest +from state.base import INTEGER, FLOAT, STRING, BOOLEAN, TYPE, NODE, EDGE @pytest.mark.usefixtures("state") @@ -123,69 +124,15 @@ def test_create_nodevalue_junk(state): @pytest.mark.usefixtures("state") -def test_create_nodevalue_type_type(state): - id1 = state.create_nodevalue({"type": "Type"}) - assert id1 is not None - - v = state.read_value(id1) - assert v == {"type": "Type"} - - -@pytest.mark.usefixtures("state") -def test_create_nodevalue_integer_type(state): - id1 = state.create_nodevalue({"type": "Integer"}) - assert id1 is not None - - v = state.read_value(id1) - assert v == {"type": "Integer"} - - -@pytest.mark.usefixtures("state") -def test_create_nodevalue_float_type(state): - id1 = state.create_nodevalue({"type": "Float"}) - assert id1 is not None - - v = state.read_value(id1) - assert v == {"type": "Float"} - - -@pytest.mark.usefixtures("state") -def test_create_nodevalue_boolean_type(state): - id1 = state.create_nodevalue({"type": "Boolean"}) - assert id1 is not None - - v = state.read_value(id1) - assert v == {"type": "Boolean"} - - -@pytest.mark.usefixtures("state") -def test_create_nodevalue_string_type(state): - id1 = state.create_nodevalue({"type": "String"}) - assert id1 is not None - - v = state.read_value(id1) - assert v == {"type": "String"} - - -@pytest.mark.usefixtures("state") -def test_create_nodevalue_node_type(state): - id1 = state.create_nodevalue({"type": "Node"}) - assert id1 is not None - - v = state.read_value(id1) - assert v == {"type": "Node"} - - -@pytest.mark.usefixtures("state") -def test_create_nodevalue_edge_type(state): - id1 = state.create_nodevalue({"type": "Edge"}) - assert id1 is not None - - v = state.read_value(id1) - assert v == {"type": "Edge"} +def test_create_nodevalue_type(state): + for t in {INTEGER, FLOAT, STRING, BOOLEAN, TYPE, NODE, EDGE}: + id1 = state.create_nodevalue(t) + assert id1 is not None + v = state.read_value(id1) + assert v == t @pytest.mark.usefixtures("state") def test_create_nodevalue_invalid_type(state): - id1 = state.create_nodevalue({"type": "Class"}) + id1 = state.create_nodevalue(("Class",)) assert id1 is None From dd9c48ac81b545b0c6f8ee35ffb3803d4d11cc60 Mon Sep 17 00:00:00 2001 From: Andrei Bondarenko Date: Wed, 14 Jul 2021 10:21:49 +0200 Subject: [PATCH 20/46] Reverted some changes to state base --- state/base.py | 4 +-- state/test/test_create_nodevalue.py | 49 ++++++++++++++++++++++++----- 2 files changed, 43 insertions(+), 10 deletions(-) diff --git a/state/base.py b/state/base.py index 8502f12..242c4b7 100644 --- a/state/base.py +++ b/state/base.py @@ -8,9 +8,7 @@ FLOAT = ("Float",) STRING = ("String",) BOOLEAN = ("Boolean",) TYPE = ("Type",) -NODE = ("Node",) -EDGE = ("Edge",) -type_values = (INTEGER, FLOAT, STRING, BOOLEAN, TYPE, NODE, EDGE) +type_values = (INTEGER, FLOAT, STRING, BOOLEAN, TYPE) Node = str diff --git a/state/test/test_create_nodevalue.py b/state/test/test_create_nodevalue.py index 23e12b9..7e3865a 100644 --- a/state/test/test_create_nodevalue.py +++ b/state/test/test_create_nodevalue.py @@ -1,5 +1,4 @@ import pytest -from state.base import INTEGER, FLOAT, STRING, BOOLEAN, TYPE, NODE, EDGE @pytest.mark.usefixtures("state") @@ -124,13 +123,49 @@ def test_create_nodevalue_junk(state): @pytest.mark.usefixtures("state") -def test_create_nodevalue_type(state): - for t in {INTEGER, FLOAT, STRING, BOOLEAN, TYPE, NODE, EDGE}: - id1 = state.create_nodevalue(t) - assert id1 is not None +def test_create_nodevalue_type_type(state): + id1 = state.create_nodevalue(("Type",)) + assert id1 is not None + + v = state.read_value(id1) + assert v == ("Type",) + + +@pytest.mark.usefixtures("state") +def test_create_nodevalue_integer_type(state): + id1 = state.create_nodevalue(("Integer",)) + assert id1 is not None + + v = state.read_value(id1) + assert v == ("Integer",) + + +@pytest.mark.usefixtures("state") +def test_create_nodevalue_float_type(state): + id1 = state.create_nodevalue(("Float",)) + assert id1 is not None + + v = state.read_value(id1) + assert v == ("Float",) + + +@pytest.mark.usefixtures("state") +def test_create_nodevalue_boolean_type(state): + id1 = state.create_nodevalue(("Boolean",)) + assert id1 is not None + + v = state.read_value(id1) + assert v == ("Boolean",) + + +@pytest.mark.usefixtures("state") +def test_create_nodevalue_string_type(state): + id1 = state.create_nodevalue(("String",)) + assert id1 is not None + + v = state.read_value(id1) + assert v == ("String",) - v = state.read_value(id1) - assert v == t @pytest.mark.usefixtures("state") def test_create_nodevalue_invalid_type(state): From a3cf1ef8c577b52c84b982f646be522e89e39ae3 Mon Sep 17 00:00:00 2001 From: Andrei Bondarenko Date: Fri, 16 Jul 2021 10:54:01 +0200 Subject: [PATCH 21/46] Simplified structures --- core/bottom.py | 50 +++++++++++++++++--------------------------------- 1 file changed, 17 insertions(+), 33 deletions(-) diff --git a/core/bottom.py b/core/bottom.py index 16fde5e..2d68216 100644 --- a/core/bottom.py +++ b/core/bottom.py @@ -1,9 +1,9 @@ -from state.base import State, INTEGER, FLOAT, STRING, BOOLEAN, TYPE, NODE, EDGE +from state.base import State from core.element import Element, String class Bottom: - def __init__(self, state: State) -> None: + def __init__(self, state: State): self.state = state def create_model(self, name: String) -> Element: @@ -16,24 +16,15 @@ class Bottom: # create model element element = self.state.create_node() # connect to model root - element_edge = self.state.create_edge(model_root, element) - # label edge with provided name - element_label = self.state.create_nodevalue(name.value) - if element_label is None: + self.state.create_dict(model_root, name.value, element) + # confirm that element has been added to the model + element = self.state.read_dict(model_root, name.value) + if element is None: + self.state.delete_node(element) print(f"Warning: Invalid name {name.value}, element not created.") return - self.state.create_edge(element_edge, element_label) - # add ltm-bottom typing - element_type = self.state.create_nodevalue(NODE) - self.state.create_dict(element_label, TYPE, element_type) def add_value(self, model: Element, name: String, value: Element): - type_map = { - int: INTEGER, - float: FLOAT, - bool: BOOLEAN, - str: STRING - } model_root = self.state.read_dict(model.id, "Model") # create model element element = self.state.create_nodevalue(value.value) @@ -41,16 +32,13 @@ class Bottom: print("Warning: Invalid value, value node not created.") return # connect to model root - element_edge = self.state.create_edge(model_root, element) - # label edge with provided name - element_label = self.state.create_nodevalue(name.value) - if element_label is None: + self.state.create_dict(model_root, name.value, element) + # confirm that element has been added to the model + element_found = self.state.read_dict(model_root, name.value) + if element_found is None: + self.state.delete_node(element) print(f"Warning: Invalid name {name.value}, element not created.") return - self.state.create_edge(element_edge, element_label) - # add ltm-bottom typing - element_type = self.state.create_nodevalue(type_map[type(value.value)]) - self.state.create_dict(element_label, TYPE, element_type) def add_edge(self, model: Element, name: String, source: String, target: String): model_root = self.state.read_dict(model.id, "Model") @@ -65,16 +53,12 @@ class Bottom: # create model element element = self.state.create_edge(source_element, target_element) # connect to model root - element_edge = self.state.create_edge(model_root, element) - # label edge with provided name - element_label = self.state.create_nodevalue(name.value) - if element_label is None: + self.state.create_dict(model_root, name.value, element) + # confirm that element has been added to the model + element = self.state.read_dict(model_root, name.value) + if element is None: + self.state.delete_edge(element) print(f"Warning: Invalid name {name.value}, element not created.") - return - self.state.create_edge(element_edge, element_label) - # add ltm-bottom typing - element_type = self.state.create_nodevalue(EDGE) - self.state.create_dict(element_label, TYPE, element_type) def get_element(self, model: Element, name: String) -> Element: model_root = self.state.read_dict(model.id, "Model") From b404f3450f9787b9429ab9ffbb79c03c4ef5ccd7 Mon Sep 17 00:00:00 2001 From: Andrei Bondarenko Date: Fri, 16 Jul 2021 11:28:25 +0200 Subject: [PATCH 22/46] Added bottom type to supported Element values --- core/element.py | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/core/element.py b/core/element.py index f428528..42284a2 100644 --- a/core/element.py +++ b/core/element.py @@ -1,12 +1,14 @@ -from dataclasses import dataclass, asdict, astuple -from typing import TypeVar, Generic, Optional +from dataclasses import dataclass +from typing import TypeVar, Generic, Optional, Literal +from state.base import INTEGER, FLOAT, BOOLEAN, STRING, TYPE # Some typing information for Python static type checkers -Literal = TypeVar('Literal', int, float, bool, str) # Must be int, float, bool or str +bottom_type = Literal[INTEGER, FLOAT, BOOLEAN, STRING, TYPE] +T = TypeVar('T', int, float, bool, str, bottom_type) @dataclass -class Element(Generic[Literal]): +class Element(Generic[T]): """ An Element can represent one of following two things, based on the value of its attributes: @@ -19,7 +21,7 @@ class Element(Generic[Literal]): dict that is used there. """ id: Optional[str] = None - value: Optional[Literal] = None + value: Optional[T] = None def is_none(self) -> bool: return self.id is None and self.value is None @@ -29,3 +31,4 @@ String = Element[str] Integer = Element[int] Float = Element[float] Boolean = Element[bool] +Type = Element[bottom_type] From 67f5f5bb7f6e4ac83aa6796ccb5af6cd45c0bba9 Mon Sep 17 00:00:00 2001 From: Andrei Bondarenko Date: Fri, 16 Jul 2021 18:18:51 +0200 Subject: [PATCH 23/46] Added element listing --- core/bottom.py | 47 +++++++++++++++++++++++++++++++++++++---------- 1 file changed, 37 insertions(+), 10 deletions(-) diff --git a/core/bottom.py b/core/bottom.py index 2d68216..31ccc66 100644 --- a/core/bottom.py +++ b/core/bottom.py @@ -1,4 +1,4 @@ -from state.base import State +from state.base import INTEGER, FLOAT, BOOLEAN, STRING, TYPE, State from core.element import Element, String @@ -60,18 +60,45 @@ class Bottom: self.state.delete_edge(element) print(f"Warning: Invalid name {name.value}, element not created.") - def get_element(self, model: Element, name: String) -> Element: - model_root = self.state.read_dict(model.id, "Model") - element = self.state.read_dict(model_root, name.value) - if element is None: - print(f"Warning: Unknown element {name.value}.") - return Element() - else: - return Element(id=element, value=self.state.read_value(element)) - def delete_element(self, model: Element, name: String): model_root = self.state.read_dict(model.id, "Model") element = self.state.read_dict(model_root, name.value) # could be both a node or an edge self.state.delete_node(element) self.state.delete_edge(element) + + def list_elements(self, model: Element): + def is_edge(elem: Element) -> bool: + edge = self.state.read_edge(elem.id) + return edge is not None + def value_type(value) -> str: + map = { + int: INTEGER, + float: FLOAT, + str: STRING, + bool: BOOLEAN, + tuple: TYPE + } + return map[type(value)][0] + + unsorted = [] + model_root = self.state.read_dict(model.id, "Model") + for elem_edge in self.state.read_outgoing(model_root): + # get element name + label_edge, = self.state.read_outgoing(elem_edge) + _, label_node = self.state.read_edge(label_edge) + label = self.state.read_value(label_node) + # find element bottom type + _, elem = self.state.read_edge(elem_edge) + if is_edge(elem): + bottom_type = "Edge" + else: + # is_node + elem_value = self.state.read_value(elem) + if elem_value is None: + bottom_type = "Node" + else: + bottom_type = value_type + unsorted.append(f"{label} : {bottom_type}") + for i in sorted(unsorted): + print(i) From 17df2d3383e0c92c23849290cd328e9071d11d0d Mon Sep 17 00:00:00 2001 From: Andrei Bondarenko Date: Sat, 17 Jul 2021 15:48:43 +0200 Subject: [PATCH 24/46] Added simple model manager --- core/manager.py | 65 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 65 insertions(+) create mode 100644 core/manager.py diff --git a/core/manager.py b/core/manager.py new file mode 100644 index 0000000..f57d794 --- /dev/null +++ b/core/manager.py @@ -0,0 +1,65 @@ +from state.base import State +from core.element import Element, String + + +class Manager: + def __init__(self, state: State): + self.state = state + + def new_model(self, name: String, type_model_name: String) -> Element: + root = self.state.read_root() + mm_bottom = self.state.read_dict(root, type_model_name.value) + if mm_bottom is None: + print(f"Error: Invalid type model name {type_model_name.value}. Model not created.") + return Element() + bottom = self.state.create_nodevalue(name.value) + if bottom is None: + print(f"Error: Invalid model name {name.value}. Model not created.") + return Element() + self.state.create_dict(root, name.value, bottom) + self.state.create_dict(bottom, "Model", self.state.create_node()) + self.state.create_dict(bottom, "Metamodel", mm_bottom) + return Element(id=bottom) + + def get_model(self, name: String) -> Element: + root = self.state.read_root() + model = self.state.read_dict(root, name.value) + if model is None: + print(f"Error: Cannot find model with name {name.value}.") + return Element() + return Element(id=model) + + def rename_model(self, name: String, new_name: String): + root = self.state.read_root() + name_used = self.state.read_dict_edge(root, new_name.value) + if name_used: + print(f"Error: Model with name {new_name.value} already exists. Please use another name.") + return + mode_edge = self.state.read_dict_edge(root, name.value) + if mode_edge is None: + print(f"Error: Cannot find model with name {name.value}.") + return + _, model_node = self.state.read_edge(mode_edge) + self.state.create_dict(root, new_name.value, model_node) + created = self.state.read_dict_edge(root, new_name.value) + if created is None: + print(f"Error: Invalid model name {new_name.value}. Model not renamed.") + else: + self.state.delete_edge(mode_edge) + + def delete_model(self, name: String): + root = self.state.read_root() + model = self.state.read_dict(root, name.value) + if model is None: + print(f"Error: No model found for name {name.value}.") + else: + self.state.delete_node(model) + + def list_models(self): + unsorted = [] + for edge in self.state.read_outgoing(self.state.read_root()): + _, model = self.state.read_edge(edge) + metamodel = self.state.read_dict(model, "Metamodel") + unsorted.append(f"{self.state.read_value(model)} : {self.state.read_value(metamodel)}") + for x in sorted(unsorted): + print(x) From 8382d7bfc4c5874d104c14f368bb2c2f031163d7 Mon Sep 17 00:00:00 2001 From: Andrei Bondarenko Date: Sat, 17 Jul 2021 15:55:23 +0200 Subject: [PATCH 25/46] Added context package --- core/context/__init__.py | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 core/context/__init__.py diff --git a/core/context/__init__.py b/core/context/__init__.py new file mode 100644 index 0000000..e69de29 From 249a07894a93595706812391d28c67269034379f Mon Sep 17 00:00:00 2001 From: Andrei Bondarenko Date: Sat, 17 Jul 2021 15:58:05 +0200 Subject: [PATCH 26/46] Simplified bottom context --- core/{ => context}/bottom.py | 51 +++++++++++++++++++----------------- 1 file changed, 27 insertions(+), 24 deletions(-) rename core/{ => context}/bottom.py (72%) diff --git a/core/bottom.py b/core/context/bottom.py similarity index 72% rename from core/bottom.py rename to core/context/bottom.py index 31ccc66..63aafb2 100644 --- a/core/bottom.py +++ b/core/context/bottom.py @@ -2,30 +2,32 @@ from state.base import INTEGER, FLOAT, BOOLEAN, STRING, TYPE, State from core.element import Element, String -class Bottom: - def __init__(self, state: State): +class BottomContext: + def __init__(self, state: State, model: Element): self.state = state + self.model = model - def create_model(self, name: String) -> Element: - bottom = self.state.create_nodevalue(name.value) - self.state.create_dict(bottom, "Model", self.state.create_node()) - return Element(id=bottom) + def __enter__(self): + return self - def add_node(self, model: Element, name: String): - model_root = self.state.read_dict(model.id, "Model") + def __exit__(self): + pass + + def add_node(self, name: String): + model_root = self.state.read_dict(self.model.id, "Model") # create model element element = self.state.create_node() # connect to model root self.state.create_dict(model_root, name.value, element) # confirm that element has been added to the model - element = self.state.read_dict(model_root, name.value) - if element is None: + element_found = self.state.read_dict(model_root, name.value) + if element_found is None: self.state.delete_node(element) print(f"Warning: Invalid name {name.value}, element not created.") return - def add_value(self, model: Element, name: String, value: Element): - model_root = self.state.read_dict(model.id, "Model") + def add_value(self, name: String, value: Element): + model_root = self.state.read_dict(self.model.id, "Model") # create model element element = self.state.create_nodevalue(value.value) if element is None: @@ -40,8 +42,8 @@ class Bottom: print(f"Warning: Invalid name {name.value}, element not created.") return - def add_edge(self, model: Element, name: String, source: String, target: String): - model_root = self.state.read_dict(model.id, "Model") + def add_edge(self, name: String, source: String, target: String): + model_root = self.state.read_dict(self.model.id, "Model") source_element = self.state.read_dict(model_root, source.value) if source_element is None: print(f"Warning: Unknown source element {source.value}, edge not created.") @@ -55,34 +57,35 @@ class Bottom: # connect to model root self.state.create_dict(model_root, name.value, element) # confirm that element has been added to the model - element = self.state.read_dict(model_root, name.value) - if element is None: + element_found = self.state.read_dict(model_root, name.value) + if element_found is None: self.state.delete_edge(element) print(f"Warning: Invalid name {name.value}, element not created.") - def delete_element(self, model: Element, name: String): - model_root = self.state.read_dict(model.id, "Model") + def delete_element(self, name: String): + model_root = self.state.read_dict(self.model.id, "Model") element = self.state.read_dict(model_root, name.value) # could be both a node or an edge self.state.delete_node(element) self.state.delete_edge(element) - def list_elements(self, model: Element): - def is_edge(elem: Element) -> bool: - edge = self.state.read_edge(elem.id) + def list_elements(self): + def is_edge(element: str) -> bool: + edge = self.state.read_edge(element) return edge is not None + def value_type(value) -> str: - map = { + mapping = { int: INTEGER, float: FLOAT, str: STRING, bool: BOOLEAN, tuple: TYPE } - return map[type(value)][0] + return mapping[type(value)][0] unsorted = [] - model_root = self.state.read_dict(model.id, "Model") + model_root = self.state.read_dict(self.model.id, "Model") for elem_edge in self.state.read_outgoing(model_root): # get element name label_edge, = self.state.read_outgoing(elem_edge) From 844586960ad6054179903988b90ee5c257a7a897 Mon Sep 17 00:00:00 2001 From: Andrei Bondarenko Date: Sat, 17 Jul 2021 15:59:36 +0200 Subject: [PATCH 27/46] Added Context abstract base class --- core/context/base.py | 59 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 59 insertions(+) create mode 100644 core/context/base.py diff --git a/core/context/base.py b/core/context/base.py new file mode 100644 index 0000000..627dc2e --- /dev/null +++ b/core/context/base.py @@ -0,0 +1,59 @@ +from abc import ABC, abstractmethod +from state.base import State +from core.element import Element, String + + +class Context(ABC): + def __init__(self, state: State, model: Element, metamodel: Element): + self.state = state + self.model = model + self.metamodel = metamodel + + @abstractmethod + def __enter__(self): + pass + + @abstractmethod + def __exit__(self): + pass + + @abstractmethod + def instantiate(self, type_name: String, instance_name: String): + pass + + @abstractmethod + def instantiate_value(self, type_name: String, instance_name: String, value: Element): + pass + + @abstractmethod + def instantiate_link(self, type_name: String, name: String, source: String, target: String): + pass + + @abstractmethod + def delete_element(self, name: String): + pass + + @abstractmethod + def verify(self): + pass + + @abstractmethod + def list_elements(self): + pass + + def list_types(self): + # can be implemented here since we assume that metamodel + # is always in graph fo, i.e. in the MV-state graph. + unsorted = [] + model_root = self.state.read_dict(self.metamodel.id, "Model") + for elem_edge in self.state.read_outgoing(model_root): + # get element name + label_edge, = self.state.read_outgoing(elem_edge) + _, label_node = self.state.read_edge(label_edge) + label = self.state.read_value(label_node) + # find element type + elem_type_node = self.state.read_dict(label_node, "Type") + elem_type = self.state.read_value(elem_type_node) + unsorted.append(f"{label} : {elem_type if elem_type is not None else '_'}") + for i in sorted(unsorted): + print(i) From be6aeb7d0ae5d4f5d9c644f391de312b87f57de0 Mon Sep 17 00:00:00 2001 From: Andrei Bondarenko Date: Sat, 17 Jul 2021 22:31:05 +0200 Subject: [PATCH 28/46] Moved model retyping to manager --- core/manager.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/core/manager.py b/core/manager.py index f57d794..4c82b45 100644 --- a/core/manager.py +++ b/core/manager.py @@ -63,3 +63,16 @@ class Manager: unsorted.append(f"{self.state.read_value(model)} : {self.state.read_value(metamodel)}") for x in sorted(unsorted): print(x) + + def retype_model(self, model_name: String, metamodel_name: String): + root = self.state.read_root() + model = self.state.read_dict(root, model_name.value) + if model is None: + print(f"Error: Cannot find model with name {model_name.value}.") + metamodel = self.state.read_dict(root, metamodel_name.value) + if metamodel is None: + print(f"Error: Cannot find model with name {metamodel_name.value}.") + metamodel_edge = self.state.read_dict_edge(model, "Metamodel") + if metamodel_edge is not None: + self.state.delete_edge(metamodel_edge) + self.state.create_dict(model, "Metamodel", metamodel) From f875f700cde688a8033cd943d09ade1499959475 Mon Sep 17 00:00:00 2001 From: Andrei Bondarenko Date: Sat, 17 Jul 2021 23:11:19 +0200 Subject: [PATCH 29/46] Implemented GenericContext --- core/context/generic.py | 82 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 82 insertions(+) create mode 100644 core/context/generic.py diff --git a/core/context/generic.py b/core/context/generic.py new file mode 100644 index 0000000..ab91ba4 --- /dev/null +++ b/core/context/generic.py @@ -0,0 +1,82 @@ +from core.element import Element, String, Boolean +from state.base import State +from core.context.base import Context +from core.context.bottom import BottomContext + + +class GenericContext(Context): + def __init__(self, state: State, model: Element, metamodel: Element): + super().__init__(state, model, metamodel) + self.bottom = BottomContext(state, model) + + def __enter__(self): + pass + + def __exit__(self): + pass + + def _type_exists(self, type_name: String, instantiate_link: bool) -> bool: + metamodel_root = self.state.read_dict(self.metamodel.id, "Model") + type_element = self.state.read_dict(metamodel_root, type_name.value) + if type_element is None: + return False + else: + element_is_edge = self.state.read_edge(type_element) is not None + return element_is_edge == instantiate_link + + def instantiate(self, type_name: String, name: String): + if not self._type_exists(type_name, instantiate_link=False): + print(f"Attempting to instantiate element with invalid type: {type_name.value}") + else: + self.bottom.add_node(name) + self.retype_element(name, type_name) + + def instantiate_value(self, type_name: String, name: String, value: Element): + if not self._type_exists(type_name, instantiate_link=False): + print(f"Attempting to instantiate element with invalid type: {type_name.value}") + else: + self.bottom.add_value(name, value.value) + self.retype_element(name, type_name) + + def instantiate_link(self, type_name: String, name: String, source: String, target: String): + if not self._type_exists(type_name, instantiate_link=True): + print(f"Attempting to instantiate link with invalid type: {type_name.value}") + else: + self.bottom.add_edge(name, source, target) + self.retype_element(name, type_name) + + def delete_element(self, name: String): + self.bottom.delete_element(name) + + def verify(self): + pass # TODO: implement conformance check + + def list_elements(self): + model_root = self.state.read_dict(self.model.id, "Model") + unsorted = [] + for elem_edge in self.state.read_outgoing(model_root): + # get element name + label_edge, = self.state.read_outgoing(elem_edge) + _, label_node = self.state.read_edge(label_edge) + label = self.state.read_value(label_node) + type_node = self.state.read_dict(label_node, "Type") + type_name = self.state.read_value(type_node) + unsorted.append(f"{label} : {type_name}") + for i in sorted(unsorted): + print(i) + + def retype_element(self, name: String, type_name: String): + model_root = self.state.read_dict(self.model.id, "Model") + element_edge = self.state.read_dict_edge(model_root, name.value) + label_node_edge, = self.state.read_outgoing(element_edge) + _, label_node = self.state.read_edge(label_node_edge) + # create type name node + type_name_node = self.state.create_nodevalue(type_name.value) + if type_name_node is None: + print("Warning: Invalid type name, element not retyped.") + # remove any existing type node + existing = self.state.read_dict(label_node, "Type") + if existing is not None: + self.state.delete_node(existing) + # create new type node + self.state.create_dict(label_node, "Type", type_name_node) From add00d6f3f2bdcc892764bef32c16b2704c8d7bd Mon Sep 17 00:00:00 2001 From: Andrei Bondarenko Date: Sun, 18 Jul 2021 10:58:28 +0200 Subject: [PATCH 30/46] Added existence check --- core/context/generic.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/core/context/generic.py b/core/context/generic.py index ab91ba4..35d4ed9 100644 --- a/core/context/generic.py +++ b/core/context/generic.py @@ -68,12 +68,15 @@ class GenericContext(Context): def retype_element(self, name: String, type_name: String): model_root = self.state.read_dict(self.model.id, "Model") element_edge = self.state.read_dict_edge(model_root, name.value) + if element_edge is None: + print(f"Error: Element with name {name.value} not found.") + return label_node_edge, = self.state.read_outgoing(element_edge) _, label_node = self.state.read_edge(label_node_edge) # create type name node type_name_node = self.state.create_nodevalue(type_name.value) if type_name_node is None: - print("Warning: Invalid type name, element not retyped.") + print("Error: Invalid type name, element not retyped.") # remove any existing type node existing = self.state.read_dict(label_node, "Type") if existing is not None: From 8449ec95623f582b43437e634da18ef2380eb6e2 Mon Sep 17 00:00:00 2001 From: Andrei Bondarenko Date: Sun, 18 Jul 2021 10:58:56 +0200 Subject: [PATCH 31/46] Added type name to custom context mapping --- core/context/__init__.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/core/context/__init__.py b/core/context/__init__.py index e69de29..820a79d 100644 --- a/core/context/__init__.py +++ b/core/context/__init__.py @@ -0,0 +1,5 @@ +from core.context.simple_class_diagrams import SCDContext + +custom_contexts = { + "SimpleClassDiagrams": SCDContext +} From 4300783416027dad4a96569ac63bf88bc13dca95 Mon Sep 17 00:00:00 2001 From: Andrei Bondarenko Date: Sun, 18 Jul 2021 10:59:26 +0200 Subject: [PATCH 32/46] Moved SCD bootstrap to SCDContext --- core/context/simple_class_diagrams.py | 192 ++++++++++++++++++++++++++ 1 file changed, 192 insertions(+) create mode 100644 core/context/simple_class_diagrams.py diff --git a/core/context/simple_class_diagrams.py b/core/context/simple_class_diagrams.py new file mode 100644 index 0000000..fcfc41f --- /dev/null +++ b/core/context/simple_class_diagrams.py @@ -0,0 +1,192 @@ +from core.element import Element +from state.base import State, STRING, INTEGER, BOOLEAN, TYPE +from core.context.generic import GenericContext + + +class SCDContext(GenericContext): + def __init__(self, state: State, model: Element, metamodel: Element): + super().__init__(state, model, metamodel) + + def _bootstrap_scd(self) -> Element: + + scd = self.state.create_nodevalue("SimpleClassDiagrams") + self.state.create_dict(self.state.read_root(), "SimpleClassDiagrams", scd) + self.state.create_dict(scd, "Model", self.state.create_node()) + self.state.create_dict(scd, "Metamodel", scd) + super().__init__(self.state, Element(id=scd), Element(id=scd)) + + # Classes --> elements that will be typed by Class + self.bottom.add_node(Element(value="Element")) + self.bottom.add_node(Element(value="Class")) + self.bottom.add_value(Element(value="Attribute"), Element(value=TYPE)) + + # Associations --> elements that will be typed by Association + self.bottom.add_edge(Element(value="Association"), Element(value="Class"), Element(value="Class")) + self.bottom.add_edge(Element(value="Inheritance"), Element(value="Element"), Element(value="Element")) + self.bottom.add_edge(Element(value="AttributeLink"), Element(value="Element"), Element(value="Attribute")) + + # Attributes --> elements that will be typed by Attribute + self.bottom.add_value(Element(value="Class_lower_cardinality"), Element(value=INTEGER)) + self.bottom.add_value(Element(value="Class_upper_cardinality"), Element(value=INTEGER)) + + self.bottom.add_value(Element(value="Association_source_lower_cardinality"), Element(value=INTEGER)) + self.bottom.add_value(Element(value="Association_source_upper_cardinality"), Element(value=INTEGER)) + self.bottom.add_value(Element(value="Association_target_lower_cardinality"), Element(value=INTEGER)) + self.bottom.add_value(Element(value="Association_target_upper_cardinality"), Element(value=INTEGER)) + + self.bottom.add_value(Element(value="Attribute_name"), Element(value=STRING)) + self.bottom.add_value(Element(value="Attribute_optional"), Element(value=BOOLEAN)) + + # Attribute instances --> elements that will be typed by one of the Attributes defined above + self.bottom.add_value(Element(value="Attribute_name.name"), Element(value="name")) + self.bottom.add_value(Element(value="Attribute_name.optional"), Element(value=False)) + self.bottom.add_value(Element(value="Attribute_optional.name"), Element(value="optional")) + self.bottom.add_value(Element(value="Attribute_optional.optional"), Element(value=False)) + + self.bottom.add_value(Element(value="Class_lower_cardinality.name"), Element(value="lower_cardinality")) + self.bottom.add_value(Element(value="Class_lower_cardinality.optional"), Element(value=True)) + self.bottom.add_value(Element(value="Class_upper_cardinality.name"), Element(value="upper_cardinality")) + self.bottom.add_value(Element(value="Class_upper_cardinality.optional"), Element(value=True)) + + self.bottom.add_value(Element(value="Association_source_lower_cardinality.name"), Element(value="source_lower_cardinality")) + self.bottom.add_value(Element(value="Association_source_lower_cardinality.optional"), Element(value=True)) + self.bottom.add_value(Element(value="Association_source_upper_cardinality.name"), Element(value="source_upper_cardinality")) + self.bottom.add_value(Element(value="Association_source_upper_cardinality.optional"), Element(value=True)) + + self.bottom.add_value(Element(value="Association_target_lower_cardinality.name"), Element(value="target_lower_cardinality")) + self.bottom.add_value(Element(value="Association_target_lower_cardinality.optional"), Element(value=True)) + self.bottom.add_value(Element(value="Association_target_upper_cardinality.name"), Element(value="target_upper_cardinality")) + self.bottom.add_value(Element(value="Association_target_upper_cardinality.optional"), Element(value=True)) + + # Inheritance instances --> elements that will be typed by Inheritance + self.bottom.add_edge(Element(value="class_inh_element"), Element(value="Class"), Element(value="Element")) + self.bottom.add_edge(Element(value="attribute_inh_element"), Element(value="Attribute"), Element(value="Element")) + self.bottom.add_edge(Element(value="association_inh_element"), Element(value="Association"), Element(value="Element")) + self.bottom.add_edge(Element(value="attributelink_inh_element"), Element(value="AttributeLink"), Element(value="Element")) + + # AttributeLinks --> elements that will be typed by AttributeLink + self.bottom.add_edge(Element(value="Class_attr01"), Element(value="Class"), Element(value="Class_lower_cardinality")) + self.bottom.add_edge(Element(value="Class_attr02"), Element(value="Class"), Element(value="Class_upper_cardinality")) + + self.bottom.add_edge(Element(value="Association_attr01"), Element(value="Association"), Element(value="Association_source_lower_cardinality")) + self.bottom.add_edge(Element(value="Association_attr02"), Element(value="Association"), Element(value="Association_source_upper_cardinality")) + self.bottom.add_edge(Element(value="Association_attr03"), Element(value="Association"), Element(value="Association_target_lower_cardinality")) + self.bottom.add_edge(Element(value="Association_attr04"), Element(value="Association"), Element(value="Association_target_upper_cardinality")) + + self.bottom.add_edge(Element(value="Attribute_name_link"), Element(value="Attribute"), Element(value="Attribute_name")) + self.bottom.add_edge(Element(value="Attribute_optional_link"), Element(value="Attribute"), Element(value="Attribute_optional")) + + # AttributeLink instances --> elements that will be typed by one of the AttributeLink defined above + self.bottom.add_edge(Element(value="Attribute_name_link_01"), Element(value="Attribute_name"), Element(value="Attribute_name.name")) + self.bottom.add_edge(Element(value="Attribute_optional_link_01"), Element(value="Attribute_name"), Element(value="Attribute_name.optional")) + self.bottom.add_edge(Element(value="Attribute_name_link_02"), Element(value="Attribute_optional"), Element(value="Attribute_optional.name")) + self.bottom.add_edge(Element(value="Attribute_optional_link_02"), Element(value="Attribute_optional"), Element(value="Attribute_optional.optional")) + + self.bottom.add_edge(Element(value="Attribute_name_link_03"), Element(value="Class_lower_cardinality"), Element(value="Class_lower_cardinality.name")) + self.bottom.add_edge(Element(value="Attribute_optional_link_03"), Element(value="Class_lower_cardinality"), Element(value="Class_lower_cardinality.optional")) + self.bottom.add_edge(Element(value="Attribute_name_link_04"), Element(value="Class_upper_cardinality"), Element(value="Class_upper_cardinality.name")) + self.bottom.add_edge(Element(value="Attribute_optional_link_04"), Element(value="Class_upper_cardinality"), Element(value="Class_upper_cardinality.optional")) + + self.bottom.add_edge(Element(value="Attribute_name_link_05"), Element(value="Association_source_lower_cardinality"), Element(value="Association_source_lower_cardinality.name")) + self.bottom.add_edge(Element(value="Attribute_optional_link_05"), Element(value="Association_source_lower_cardinality"), Element(value="Association_source_lower_cardinality.optional")) + self.bottom.add_edge(Element(value="Attribute_name_link_06"), Element(value="Association_source_upper_cardinality"), Element(value="Association_source_upper_cardinality.name")) + self.bottom.add_edge(Element(value="Attribute_optional_link_06"), Element(value="Association_source_upper_cardinality"), Element(value="Association_source_upper_cardinality.optional")) + + self.bottom.add_edge(Element(value="Attribute_name_link_07"), Element(value="Association_target_lower_cardinality"), Element(value="Association_target_lower_cardinality.name")) + self.bottom.add_edge(Element(value="Attribute_optional_link_07"), Element(value="Association_target_lower_cardinality"), Element(value="Association_target_lower_cardinality.optional")) + self.bottom.add_edge(Element(value="Attribute_name_link_08"), Element(value="Association_target_upper_cardinality"), Element(value="Association_target_upper_cardinality.name")) + self.bottom.add_edge(Element(value="Attribute_optional_link_08"), Element(value="Association_target_upper_cardinality"), Element(value="Association_target_upper_cardinality.optional")) + + """ + Retype the elements of the model. + This way we make the model "metacircular". + """ + self.retype_element(Element(value="Element"), Element(value="Class")) + self.retype_element(Element(value="Class"), Element(value="Class")) + self.retype_element(Element(value="Attribute"), Element(value="Class")) + + self.retype_element(Element(value="Association"), Element(value="Association")) + self.retype_element(Element(value="Inheritance"), Element(value="Association")) + self.retype_element(Element(value="AttributeLink"), Element(value="Association")) + + self.retype_element(Element(value="Class_lower_cardinality"), Element(value="Attribute")) + self.retype_element(Element(value="Class_upper_cardinality"), Element(value="Attribute")) + self.retype_element(Element(value="Association_source_lower_cardinality"), Element(value="Attribute")) + self.retype_element(Element(value="Association_source_upper_cardinality"), Element(value="Attribute")) + self.retype_element(Element(value="Association_target_lower_cardinality"), Element(value="Attribute")) + self.retype_element(Element(value="Association_target_upper_cardinality"), Element(value="Attribute")) + self.retype_element(Element(value="Attribute_name"), Element(value="Attribute")) + self.retype_element(Element(value="Attribute_optional"), Element(value="Attribute")) + + self.retype_element(Element(value="Class_attr01"), Element(value="AttributeLink")) + self.retype_element(Element(value="Class_attr02"), Element(value="AttributeLink")) + self.retype_element(Element(value="Association_attr01"), Element(value="AttributeLink")) + self.retype_element(Element(value="Association_attr02"), Element(value="AttributeLink")) + self.retype_element(Element(value="Association_attr03"), Element(value="AttributeLink")) + self.retype_element(Element(value="Association_attr04"), Element(value="AttributeLink")) + self.retype_element(Element(value="Attribute_name_link"), Element(value="AttributeLink")) + self.retype_element(Element(value="Attribute_optional_link"), Element(value="AttributeLink")) + + self.retype_element(Element(value="class_inh_element"), Element(value="Inheritance")) + self.retype_element(Element(value="attribute_inh_element"), Element(value="Inheritance")) + self.retype_element(Element(value="association_inh_element"), Element(value="Inheritance")) + self.retype_element(Element(value="attributelink_inh_element"), Element(value="Inheritance")) + + self.retype_element(Element(value="Attribute_name.name"), Element(value="Attribute_name")) + self.retype_element(Element(value="Attribute_name.optional"),Element(value="Attribute_optional")) + self.retype_element(Element(value="Attribute_optional.name"), Element(value="Attribute_name")) + self.retype_element(Element(value="Attribute_optional.optional"), Element(value="Attribute_optional")) + self.retype_element(Element(value="Class_lower_cardinality.name"), Element(value="Attribute_name")) + self.retype_element(Element(value="Class_lower_cardinality.optional"), Element(value="Attribute_optional")) + self.retype_element(Element(value="Class_upper_cardinality.name"), Element(value="Attribute_name")) + self.retype_element(Element(value="Class_upper_cardinality.optional"), Element(value="Attribute_optional")) + self.retype_element(Element(value="Association_source_lower_cardinality.name"), Element(value="Attribute_name")) + self.retype_element(Element(value="Association_source_lower_cardinality.optional"), Element(value="Attribute_optional")) + self.retype_element(Element(value="Association_source_upper_cardinality.name"), Element(value="Attribute_name")) + self.retype_element(Element(value="Association_source_upper_cardinality.optional"), Element(value="Attribute_optional")) + self.retype_element(Element(value="Association_target_lower_cardinality.name"), Element(value="Attribute_name")) + self.retype_element(Element(value="Association_target_lower_cardinality.optional"), Element(value="Attribute_optional")) + self.retype_element(Element(value="Association_target_upper_cardinality.name"), Element(value="Attribute_name")) + self.retype_element(Element(value="Association_target_upper_cardinality.optional"), Element(value="Attribute_optional")) + + self.retype_element(Element(value="Attribute_name_link_01"), Element(value="Attribute_name_link")) + self.retype_element(Element(value="Attribute_optional_link_01"), Element(value="Attribute_optional_link")) + self.retype_element(Element(value="Attribute_name_link_02"), Element(value="Attribute_name_link")) + self.retype_element(Element(value="Attribute_optional_link_02"), Element(value="Attribute_optional_link")) + self.retype_element(Element(value="Attribute_name_link_03"), Element(value="Attribute_name_link")) + self.retype_element(Element(value="Attribute_optional_link_03"), Element(value="Attribute_optional_link")) + self.retype_element(Element(value="Attribute_name_link_04"), Element(value="Attribute_name_link")) + self.retype_element(Element(value="Attribute_optional_link_04"), Element(value="Attribute_optional_link")) + self.retype_element(Element(value="Attribute_name_link_05"), Element(value="Attribute_name_link")) + self.retype_element(Element(value="Attribute_optional_link_05"), Element(value="Attribute_optional_link")) + self.retype_element(Element(value="Attribute_name_link_06"), Element(value="Attribute_name_link")) + self.retype_element(Element(value="Attribute_optional_link_06"), Element(value="Attribute_optional_link")) + self.retype_element(Element(value="Attribute_name_link_07"), Element(value="Attribute_name_link")) + self.retype_element(Element(value="Attribute_optional_link_07"), Element(value="Attribute_optional_link")) + self.retype_element(Element(value="Attribute_name_link_08"), Element(value="Attribute_name_link")) + self.retype_element(Element(value="Attribute_optional_link_08"), Element(value="Attribute_optional_link")) + + return Element(id=scd) + + +def main(): + from state.devstate import DevState + + s = DevState() + scd = SCDContext(s, Element(), Element()) + bootstrap = scd._bootstrap_scd() + model = s.read_dict(bootstrap.id, "Model") + x = [] + for e in s.read_outgoing(model): + label_node_edge, = s.read_outgoing(e) + _, label_node = s.read_edge(label_node_edge) + type_node = s.read_dict(label_node, "Type") + x.append(f"{s.read_value(label_node)} : {s.read_value(type_node)}") + for t in sorted(x): + print(t) + + # s.dump("out/scd.dot", "out/scd.png") + + +if __name__ == '__main__': + main() From 65886dc694fbbd135dfec8d3d3ea81f0e77038a8 Mon Sep 17 00:00:00 2001 From: Andrei Bondarenko Date: Sun, 18 Jul 2021 12:00:37 +0200 Subject: [PATCH 33/46] Moved SCD bootstrap to separate file --- bootstrap/__init__.py | 0 bootstrap/simple_class_diagrams.py | 204 ++++++++++++++++++++++++++ core/context/simple_class_diagrams.py | 163 +------------------- 3 files changed, 205 insertions(+), 162 deletions(-) create mode 100644 bootstrap/__init__.py create mode 100644 bootstrap/simple_class_diagrams.py diff --git a/bootstrap/__init__.py b/bootstrap/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/bootstrap/simple_class_diagrams.py b/bootstrap/simple_class_diagrams.py new file mode 100644 index 0000000..bfe9289 --- /dev/null +++ b/bootstrap/simple_class_diagrams.py @@ -0,0 +1,204 @@ +from core.element import Element +from state.base import State, STRING, INTEGER, BOOLEAN, TYPE +from core.context.generic import GenericContext + + +def bootstrap_scd(state: State) -> Element: + ctx = GenericContext(state, Element(), Element()) + + scd = state.create_nodevalue("SimpleClassDiagrams") + state.create_dict(state.read_root(), "SimpleClassDiagrams", scd) + state.create_dict(scd, "Model", state.create_node()) + state.create_dict(scd, "Metamodel", scd) + ctx.__init__(state, Element(id=scd), Element(id=scd)) + + # Classes --> elements that will be typed by Class + ctx.bottom.add_node(Element(value="Element")) + ctx.bottom.add_node(Element(value="Class")) + ctx.bottom.add_value(Element(value="Attribute"), Element(value=TYPE)) + + # Associations --> elements that will be typed by Association + ctx.bottom.add_edge(Element(value="Association"), Element(value="Class"), Element(value="Class")) + ctx.bottom.add_edge(Element(value="Inheritance"), Element(value="Element"), Element(value="Element")) + ctx.bottom.add_edge(Element(value="AttributeLink"), Element(value="Element"), Element(value="Attribute")) + + # Attributes --> elements that will be typed by Attribute + ctx.bottom.add_value(Element(value="Class_lower_cardinality"), Element(value=INTEGER)) + ctx.bottom.add_value(Element(value="Class_upper_cardinality"), Element(value=INTEGER)) + + ctx.bottom.add_value(Element(value="Association_source_lower_cardinality"), Element(value=INTEGER)) + ctx.bottom.add_value(Element(value="Association_source_upper_cardinality"), Element(value=INTEGER)) + ctx.bottom.add_value(Element(value="Association_target_lower_cardinality"), Element(value=INTEGER)) + ctx.bottom.add_value(Element(value="Association_target_upper_cardinality"), Element(value=INTEGER)) + + ctx.bottom.add_value(Element(value="Attribute_name"), Element(value=STRING)) + ctx.bottom.add_value(Element(value="Attribute_optional"), Element(value=BOOLEAN)) + + # Attribute instances --> elements that will be typed by one of the Attributes defined above + ctx.bottom.add_value(Element(value="Attribute_name.name"), Element(value="name")) + ctx.bottom.add_value(Element(value="Attribute_name.optional"), Element(value=False)) + ctx.bottom.add_value(Element(value="Attribute_optional.name"), Element(value="optional")) + ctx.bottom.add_value(Element(value="Attribute_optional.optional"), Element(value=False)) + + ctx.bottom.add_value(Element(value="Class_lower_cardinality.name"), Element(value="lower_cardinality")) + ctx.bottom.add_value(Element(value="Class_lower_cardinality.optional"), Element(value=True)) + ctx.bottom.add_value(Element(value="Class_upper_cardinality.name"), Element(value="upper_cardinality")) + ctx.bottom.add_value(Element(value="Class_upper_cardinality.optional"), Element(value=True)) + + ctx.bottom.add_value(Element(value="Association_source_lower_cardinality.name"), + Element(value="source_lower_cardinality")) + ctx.bottom.add_value(Element(value="Association_source_lower_cardinality.optional"), Element(value=True)) + ctx.bottom.add_value(Element(value="Association_source_upper_cardinality.name"), + Element(value="source_upper_cardinality")) + ctx.bottom.add_value(Element(value="Association_source_upper_cardinality.optional"), Element(value=True)) + + ctx.bottom.add_value(Element(value="Association_target_lower_cardinality.name"), + Element(value="target_lower_cardinality")) + ctx.bottom.add_value(Element(value="Association_target_lower_cardinality.optional"), Element(value=True)) + ctx.bottom.add_value(Element(value="Association_target_upper_cardinality.name"), + Element(value="target_upper_cardinality")) + ctx.bottom.add_value(Element(value="Association_target_upper_cardinality.optional"), Element(value=True)) + + # Inheritance instances --> elements that will be typed by Inheritance + ctx.bottom.add_edge(Element(value="class_inh_element"), Element(value="Class"), Element(value="Element")) + ctx.bottom.add_edge(Element(value="attribute_inh_element"), Element(value="Attribute"), Element(value="Element")) + ctx.bottom.add_edge(Element(value="association_inh_element"), Element(value="Association"), + Element(value="Element")) + ctx.bottom.add_edge(Element(value="attributelink_inh_element"), Element(value="AttributeLink"), + Element(value="Element")) + + # AttributeLinks --> elements that will be typed by AttributeLink + ctx.bottom.add_edge(Element(value="Class_attr01"), Element(value="Class"), + Element(value="Class_lower_cardinality")) + ctx.bottom.add_edge(Element(value="Class_attr02"), Element(value="Class"), + Element(value="Class_upper_cardinality")) + + ctx.bottom.add_edge(Element(value="Association_attr01"), Element(value="Association"), + Element(value="Association_source_lower_cardinality")) + ctx.bottom.add_edge(Element(value="Association_attr02"), Element(value="Association"), + Element(value="Association_source_upper_cardinality")) + ctx.bottom.add_edge(Element(value="Association_attr03"), Element(value="Association"), + Element(value="Association_target_lower_cardinality")) + ctx.bottom.add_edge(Element(value="Association_attr04"), Element(value="Association"), + Element(value="Association_target_upper_cardinality")) + + ctx.bottom.add_edge(Element(value="Attribute_name_link"), Element(value="Attribute"), + Element(value="Attribute_name")) + ctx.bottom.add_edge(Element(value="Attribute_optional_link"), Element(value="Attribute"), + Element(value="Attribute_optional")) + + # AttributeLink instances --> elements that will be typed by one of the AttributeLink defined above + ctx.bottom.add_edge(Element(value="Attribute_name_link_01"), Element(value="Attribute_name"), + Element(value="Attribute_name.name")) + ctx.bottom.add_edge(Element(value="Attribute_optional_link_01"), Element(value="Attribute_name"), + Element(value="Attribute_name.optional")) + ctx.bottom.add_edge(Element(value="Attribute_name_link_02"), Element(value="Attribute_optional"), + Element(value="Attribute_optional.name")) + ctx.bottom.add_edge(Element(value="Attribute_optional_link_02"), Element(value="Attribute_optional"), + Element(value="Attribute_optional.optional")) + + ctx.bottom.add_edge(Element(value="Attribute_name_link_03"), Element(value="Class_lower_cardinality"), + Element(value="Class_lower_cardinality.name")) + ctx.bottom.add_edge(Element(value="Attribute_optional_link_03"), Element(value="Class_lower_cardinality"), + Element(value="Class_lower_cardinality.optional")) + ctx.bottom.add_edge(Element(value="Attribute_name_link_04"), Element(value="Class_upper_cardinality"), + Element(value="Class_upper_cardinality.name")) + ctx.bottom.add_edge(Element(value="Attribute_optional_link_04"), Element(value="Class_upper_cardinality"), + Element(value="Class_upper_cardinality.optional")) + + ctx.bottom.add_edge(Element(value="Attribute_name_link_05"), Element(value="Association_source_lower_cardinality"), + Element(value="Association_source_lower_cardinality.name")) + ctx.bottom.add_edge(Element(value="Attribute_optional_link_05"), + Element(value="Association_source_lower_cardinality"), + Element(value="Association_source_lower_cardinality.optional")) + ctx.bottom.add_edge(Element(value="Attribute_name_link_06"), Element(value="Association_source_upper_cardinality"), + Element(value="Association_source_upper_cardinality.name")) + ctx.bottom.add_edge(Element(value="Attribute_optional_link_06"), + Element(value="Association_source_upper_cardinality"), + Element(value="Association_source_upper_cardinality.optional")) + + ctx.bottom.add_edge(Element(value="Attribute_name_link_07"), Element(value="Association_target_lower_cardinality"), + Element(value="Association_target_lower_cardinality.name")) + ctx.bottom.add_edge(Element(value="Attribute_optional_link_07"), + Element(value="Association_target_lower_cardinality"), + Element(value="Association_target_lower_cardinality.optional")) + ctx.bottom.add_edge(Element(value="Attribute_name_link_08"), Element(value="Association_target_upper_cardinality"), + Element(value="Association_target_upper_cardinality.name")) + ctx.bottom.add_edge(Element(value="Attribute_optional_link_08"), + Element(value="Association_target_upper_cardinality"), + Element(value="Association_target_upper_cardinality.optional")) + + """ + Retype the elements of the model. + This way we make the model "metacircular". + """ + ctx.retype_element(Element(value="Element"), Element(value="Class")) + ctx.retype_element(Element(value="Class"), Element(value="Class")) + ctx.retype_element(Element(value="Attribute"), Element(value="Class")) + + ctx.retype_element(Element(value="Association"), Element(value="Association")) + ctx.retype_element(Element(value="Inheritance"), Element(value="Association")) + ctx.retype_element(Element(value="AttributeLink"), Element(value="Association")) + + ctx.retype_element(Element(value="Class_lower_cardinality"), Element(value="Attribute")) + ctx.retype_element(Element(value="Class_upper_cardinality"), Element(value="Attribute")) + ctx.retype_element(Element(value="Association_source_lower_cardinality"), Element(value="Attribute")) + ctx.retype_element(Element(value="Association_source_upper_cardinality"), Element(value="Attribute")) + ctx.retype_element(Element(value="Association_target_lower_cardinality"), Element(value="Attribute")) + ctx.retype_element(Element(value="Association_target_upper_cardinality"), Element(value="Attribute")) + ctx.retype_element(Element(value="Attribute_name"), Element(value="Attribute")) + ctx.retype_element(Element(value="Attribute_optional"), Element(value="Attribute")) + + ctx.retype_element(Element(value="Class_attr01"), Element(value="AttributeLink")) + ctx.retype_element(Element(value="Class_attr02"), Element(value="AttributeLink")) + ctx.retype_element(Element(value="Association_attr01"), Element(value="AttributeLink")) + ctx.retype_element(Element(value="Association_attr02"), Element(value="AttributeLink")) + ctx.retype_element(Element(value="Association_attr03"), Element(value="AttributeLink")) + ctx.retype_element(Element(value="Association_attr04"), Element(value="AttributeLink")) + ctx.retype_element(Element(value="Attribute_name_link"), Element(value="AttributeLink")) + ctx.retype_element(Element(value="Attribute_optional_link"), Element(value="AttributeLink")) + + ctx.retype_element(Element(value="class_inh_element"), Element(value="Inheritance")) + ctx.retype_element(Element(value="attribute_inh_element"), Element(value="Inheritance")) + ctx.retype_element(Element(value="association_inh_element"), Element(value="Inheritance")) + ctx.retype_element(Element(value="attributelink_inh_element"), Element(value="Inheritance")) + + ctx.retype_element(Element(value="Attribute_name.name"), Element(value="Attribute_name")) + ctx.retype_element(Element(value="Attribute_name.optional"), Element(value="Attribute_optional")) + ctx.retype_element(Element(value="Attribute_optional.name"), Element(value="Attribute_name")) + ctx.retype_element(Element(value="Attribute_optional.optional"), Element(value="Attribute_optional")) + ctx.retype_element(Element(value="Class_lower_cardinality.name"), Element(value="Attribute_name")) + ctx.retype_element(Element(value="Class_lower_cardinality.optional"), Element(value="Attribute_optional")) + ctx.retype_element(Element(value="Class_upper_cardinality.name"), Element(value="Attribute_name")) + ctx.retype_element(Element(value="Class_upper_cardinality.optional"), Element(value="Attribute_optional")) + ctx.retype_element(Element(value="Association_source_lower_cardinality.name"), Element(value="Attribute_name")) + ctx.retype_element(Element(value="Association_source_lower_cardinality.optional"), + Element(value="Attribute_optional")) + ctx.retype_element(Element(value="Association_source_upper_cardinality.name"), Element(value="Attribute_name")) + ctx.retype_element(Element(value="Association_source_upper_cardinality.optional"), + Element(value="Attribute_optional")) + ctx.retype_element(Element(value="Association_target_lower_cardinality.name"), Element(value="Attribute_name")) + ctx.retype_element(Element(value="Association_target_lower_cardinality.optional"), + Element(value="Attribute_optional")) + ctx.retype_element(Element(value="Association_target_upper_cardinality.name"), Element(value="Attribute_name")) + ctx.retype_element(Element(value="Association_target_upper_cardinality.optional"), + Element(value="Attribute_optional")) + + ctx.retype_element(Element(value="Attribute_name_link_01"), Element(value="Attribute_name_link")) + ctx.retype_element(Element(value="Attribute_optional_link_01"), Element(value="Attribute_optional_link")) + ctx.retype_element(Element(value="Attribute_name_link_02"), Element(value="Attribute_name_link")) + ctx.retype_element(Element(value="Attribute_optional_link_02"), Element(value="Attribute_optional_link")) + ctx.retype_element(Element(value="Attribute_name_link_03"), Element(value="Attribute_name_link")) + ctx.retype_element(Element(value="Attribute_optional_link_03"), Element(value="Attribute_optional_link")) + ctx.retype_element(Element(value="Attribute_name_link_04"), Element(value="Attribute_name_link")) + ctx.retype_element(Element(value="Attribute_optional_link_04"), Element(value="Attribute_optional_link")) + ctx.retype_element(Element(value="Attribute_name_link_05"), Element(value="Attribute_name_link")) + ctx.retype_element(Element(value="Attribute_optional_link_05"), Element(value="Attribute_optional_link")) + ctx.retype_element(Element(value="Attribute_name_link_06"), Element(value="Attribute_name_link")) + ctx.retype_element(Element(value="Attribute_optional_link_06"), Element(value="Attribute_optional_link")) + ctx.retype_element(Element(value="Attribute_name_link_07"), Element(value="Attribute_name_link")) + ctx.retype_element(Element(value="Attribute_optional_link_07"), Element(value="Attribute_optional_link")) + ctx.retype_element(Element(value="Attribute_name_link_08"), Element(value="Attribute_name_link")) + ctx.retype_element(Element(value="Attribute_optional_link_08"), Element(value="Attribute_optional_link")) + + return Element(id=scd) diff --git a/core/context/simple_class_diagrams.py b/core/context/simple_class_diagrams.py index fcfc41f..c013862 100644 --- a/core/context/simple_class_diagrams.py +++ b/core/context/simple_class_diagrams.py @@ -1,5 +1,5 @@ from core.element import Element -from state.base import State, STRING, INTEGER, BOOLEAN, TYPE +from state.base import State from core.context.generic import GenericContext @@ -7,167 +7,6 @@ class SCDContext(GenericContext): def __init__(self, state: State, model: Element, metamodel: Element): super().__init__(state, model, metamodel) - def _bootstrap_scd(self) -> Element: - - scd = self.state.create_nodevalue("SimpleClassDiagrams") - self.state.create_dict(self.state.read_root(), "SimpleClassDiagrams", scd) - self.state.create_dict(scd, "Model", self.state.create_node()) - self.state.create_dict(scd, "Metamodel", scd) - super().__init__(self.state, Element(id=scd), Element(id=scd)) - - # Classes --> elements that will be typed by Class - self.bottom.add_node(Element(value="Element")) - self.bottom.add_node(Element(value="Class")) - self.bottom.add_value(Element(value="Attribute"), Element(value=TYPE)) - - # Associations --> elements that will be typed by Association - self.bottom.add_edge(Element(value="Association"), Element(value="Class"), Element(value="Class")) - self.bottom.add_edge(Element(value="Inheritance"), Element(value="Element"), Element(value="Element")) - self.bottom.add_edge(Element(value="AttributeLink"), Element(value="Element"), Element(value="Attribute")) - - # Attributes --> elements that will be typed by Attribute - self.bottom.add_value(Element(value="Class_lower_cardinality"), Element(value=INTEGER)) - self.bottom.add_value(Element(value="Class_upper_cardinality"), Element(value=INTEGER)) - - self.bottom.add_value(Element(value="Association_source_lower_cardinality"), Element(value=INTEGER)) - self.bottom.add_value(Element(value="Association_source_upper_cardinality"), Element(value=INTEGER)) - self.bottom.add_value(Element(value="Association_target_lower_cardinality"), Element(value=INTEGER)) - self.bottom.add_value(Element(value="Association_target_upper_cardinality"), Element(value=INTEGER)) - - self.bottom.add_value(Element(value="Attribute_name"), Element(value=STRING)) - self.bottom.add_value(Element(value="Attribute_optional"), Element(value=BOOLEAN)) - - # Attribute instances --> elements that will be typed by one of the Attributes defined above - self.bottom.add_value(Element(value="Attribute_name.name"), Element(value="name")) - self.bottom.add_value(Element(value="Attribute_name.optional"), Element(value=False)) - self.bottom.add_value(Element(value="Attribute_optional.name"), Element(value="optional")) - self.bottom.add_value(Element(value="Attribute_optional.optional"), Element(value=False)) - - self.bottom.add_value(Element(value="Class_lower_cardinality.name"), Element(value="lower_cardinality")) - self.bottom.add_value(Element(value="Class_lower_cardinality.optional"), Element(value=True)) - self.bottom.add_value(Element(value="Class_upper_cardinality.name"), Element(value="upper_cardinality")) - self.bottom.add_value(Element(value="Class_upper_cardinality.optional"), Element(value=True)) - - self.bottom.add_value(Element(value="Association_source_lower_cardinality.name"), Element(value="source_lower_cardinality")) - self.bottom.add_value(Element(value="Association_source_lower_cardinality.optional"), Element(value=True)) - self.bottom.add_value(Element(value="Association_source_upper_cardinality.name"), Element(value="source_upper_cardinality")) - self.bottom.add_value(Element(value="Association_source_upper_cardinality.optional"), Element(value=True)) - - self.bottom.add_value(Element(value="Association_target_lower_cardinality.name"), Element(value="target_lower_cardinality")) - self.bottom.add_value(Element(value="Association_target_lower_cardinality.optional"), Element(value=True)) - self.bottom.add_value(Element(value="Association_target_upper_cardinality.name"), Element(value="target_upper_cardinality")) - self.bottom.add_value(Element(value="Association_target_upper_cardinality.optional"), Element(value=True)) - - # Inheritance instances --> elements that will be typed by Inheritance - self.bottom.add_edge(Element(value="class_inh_element"), Element(value="Class"), Element(value="Element")) - self.bottom.add_edge(Element(value="attribute_inh_element"), Element(value="Attribute"), Element(value="Element")) - self.bottom.add_edge(Element(value="association_inh_element"), Element(value="Association"), Element(value="Element")) - self.bottom.add_edge(Element(value="attributelink_inh_element"), Element(value="AttributeLink"), Element(value="Element")) - - # AttributeLinks --> elements that will be typed by AttributeLink - self.bottom.add_edge(Element(value="Class_attr01"), Element(value="Class"), Element(value="Class_lower_cardinality")) - self.bottom.add_edge(Element(value="Class_attr02"), Element(value="Class"), Element(value="Class_upper_cardinality")) - - self.bottom.add_edge(Element(value="Association_attr01"), Element(value="Association"), Element(value="Association_source_lower_cardinality")) - self.bottom.add_edge(Element(value="Association_attr02"), Element(value="Association"), Element(value="Association_source_upper_cardinality")) - self.bottom.add_edge(Element(value="Association_attr03"), Element(value="Association"), Element(value="Association_target_lower_cardinality")) - self.bottom.add_edge(Element(value="Association_attr04"), Element(value="Association"), Element(value="Association_target_upper_cardinality")) - - self.bottom.add_edge(Element(value="Attribute_name_link"), Element(value="Attribute"), Element(value="Attribute_name")) - self.bottom.add_edge(Element(value="Attribute_optional_link"), Element(value="Attribute"), Element(value="Attribute_optional")) - - # AttributeLink instances --> elements that will be typed by one of the AttributeLink defined above - self.bottom.add_edge(Element(value="Attribute_name_link_01"), Element(value="Attribute_name"), Element(value="Attribute_name.name")) - self.bottom.add_edge(Element(value="Attribute_optional_link_01"), Element(value="Attribute_name"), Element(value="Attribute_name.optional")) - self.bottom.add_edge(Element(value="Attribute_name_link_02"), Element(value="Attribute_optional"), Element(value="Attribute_optional.name")) - self.bottom.add_edge(Element(value="Attribute_optional_link_02"), Element(value="Attribute_optional"), Element(value="Attribute_optional.optional")) - - self.bottom.add_edge(Element(value="Attribute_name_link_03"), Element(value="Class_lower_cardinality"), Element(value="Class_lower_cardinality.name")) - self.bottom.add_edge(Element(value="Attribute_optional_link_03"), Element(value="Class_lower_cardinality"), Element(value="Class_lower_cardinality.optional")) - self.bottom.add_edge(Element(value="Attribute_name_link_04"), Element(value="Class_upper_cardinality"), Element(value="Class_upper_cardinality.name")) - self.bottom.add_edge(Element(value="Attribute_optional_link_04"), Element(value="Class_upper_cardinality"), Element(value="Class_upper_cardinality.optional")) - - self.bottom.add_edge(Element(value="Attribute_name_link_05"), Element(value="Association_source_lower_cardinality"), Element(value="Association_source_lower_cardinality.name")) - self.bottom.add_edge(Element(value="Attribute_optional_link_05"), Element(value="Association_source_lower_cardinality"), Element(value="Association_source_lower_cardinality.optional")) - self.bottom.add_edge(Element(value="Attribute_name_link_06"), Element(value="Association_source_upper_cardinality"), Element(value="Association_source_upper_cardinality.name")) - self.bottom.add_edge(Element(value="Attribute_optional_link_06"), Element(value="Association_source_upper_cardinality"), Element(value="Association_source_upper_cardinality.optional")) - - self.bottom.add_edge(Element(value="Attribute_name_link_07"), Element(value="Association_target_lower_cardinality"), Element(value="Association_target_lower_cardinality.name")) - self.bottom.add_edge(Element(value="Attribute_optional_link_07"), Element(value="Association_target_lower_cardinality"), Element(value="Association_target_lower_cardinality.optional")) - self.bottom.add_edge(Element(value="Attribute_name_link_08"), Element(value="Association_target_upper_cardinality"), Element(value="Association_target_upper_cardinality.name")) - self.bottom.add_edge(Element(value="Attribute_optional_link_08"), Element(value="Association_target_upper_cardinality"), Element(value="Association_target_upper_cardinality.optional")) - - """ - Retype the elements of the model. - This way we make the model "metacircular". - """ - self.retype_element(Element(value="Element"), Element(value="Class")) - self.retype_element(Element(value="Class"), Element(value="Class")) - self.retype_element(Element(value="Attribute"), Element(value="Class")) - - self.retype_element(Element(value="Association"), Element(value="Association")) - self.retype_element(Element(value="Inheritance"), Element(value="Association")) - self.retype_element(Element(value="AttributeLink"), Element(value="Association")) - - self.retype_element(Element(value="Class_lower_cardinality"), Element(value="Attribute")) - self.retype_element(Element(value="Class_upper_cardinality"), Element(value="Attribute")) - self.retype_element(Element(value="Association_source_lower_cardinality"), Element(value="Attribute")) - self.retype_element(Element(value="Association_source_upper_cardinality"), Element(value="Attribute")) - self.retype_element(Element(value="Association_target_lower_cardinality"), Element(value="Attribute")) - self.retype_element(Element(value="Association_target_upper_cardinality"), Element(value="Attribute")) - self.retype_element(Element(value="Attribute_name"), Element(value="Attribute")) - self.retype_element(Element(value="Attribute_optional"), Element(value="Attribute")) - - self.retype_element(Element(value="Class_attr01"), Element(value="AttributeLink")) - self.retype_element(Element(value="Class_attr02"), Element(value="AttributeLink")) - self.retype_element(Element(value="Association_attr01"), Element(value="AttributeLink")) - self.retype_element(Element(value="Association_attr02"), Element(value="AttributeLink")) - self.retype_element(Element(value="Association_attr03"), Element(value="AttributeLink")) - self.retype_element(Element(value="Association_attr04"), Element(value="AttributeLink")) - self.retype_element(Element(value="Attribute_name_link"), Element(value="AttributeLink")) - self.retype_element(Element(value="Attribute_optional_link"), Element(value="AttributeLink")) - - self.retype_element(Element(value="class_inh_element"), Element(value="Inheritance")) - self.retype_element(Element(value="attribute_inh_element"), Element(value="Inheritance")) - self.retype_element(Element(value="association_inh_element"), Element(value="Inheritance")) - self.retype_element(Element(value="attributelink_inh_element"), Element(value="Inheritance")) - - self.retype_element(Element(value="Attribute_name.name"), Element(value="Attribute_name")) - self.retype_element(Element(value="Attribute_name.optional"),Element(value="Attribute_optional")) - self.retype_element(Element(value="Attribute_optional.name"), Element(value="Attribute_name")) - self.retype_element(Element(value="Attribute_optional.optional"), Element(value="Attribute_optional")) - self.retype_element(Element(value="Class_lower_cardinality.name"), Element(value="Attribute_name")) - self.retype_element(Element(value="Class_lower_cardinality.optional"), Element(value="Attribute_optional")) - self.retype_element(Element(value="Class_upper_cardinality.name"), Element(value="Attribute_name")) - self.retype_element(Element(value="Class_upper_cardinality.optional"), Element(value="Attribute_optional")) - self.retype_element(Element(value="Association_source_lower_cardinality.name"), Element(value="Attribute_name")) - self.retype_element(Element(value="Association_source_lower_cardinality.optional"), Element(value="Attribute_optional")) - self.retype_element(Element(value="Association_source_upper_cardinality.name"), Element(value="Attribute_name")) - self.retype_element(Element(value="Association_source_upper_cardinality.optional"), Element(value="Attribute_optional")) - self.retype_element(Element(value="Association_target_lower_cardinality.name"), Element(value="Attribute_name")) - self.retype_element(Element(value="Association_target_lower_cardinality.optional"), Element(value="Attribute_optional")) - self.retype_element(Element(value="Association_target_upper_cardinality.name"), Element(value="Attribute_name")) - self.retype_element(Element(value="Association_target_upper_cardinality.optional"), Element(value="Attribute_optional")) - - self.retype_element(Element(value="Attribute_name_link_01"), Element(value="Attribute_name_link")) - self.retype_element(Element(value="Attribute_optional_link_01"), Element(value="Attribute_optional_link")) - self.retype_element(Element(value="Attribute_name_link_02"), Element(value="Attribute_name_link")) - self.retype_element(Element(value="Attribute_optional_link_02"), Element(value="Attribute_optional_link")) - self.retype_element(Element(value="Attribute_name_link_03"), Element(value="Attribute_name_link")) - self.retype_element(Element(value="Attribute_optional_link_03"), Element(value="Attribute_optional_link")) - self.retype_element(Element(value="Attribute_name_link_04"), Element(value="Attribute_name_link")) - self.retype_element(Element(value="Attribute_optional_link_04"), Element(value="Attribute_optional_link")) - self.retype_element(Element(value="Attribute_name_link_05"), Element(value="Attribute_name_link")) - self.retype_element(Element(value="Attribute_optional_link_05"), Element(value="Attribute_optional_link")) - self.retype_element(Element(value="Attribute_name_link_06"), Element(value="Attribute_name_link")) - self.retype_element(Element(value="Attribute_optional_link_06"), Element(value="Attribute_optional_link")) - self.retype_element(Element(value="Attribute_name_link_07"), Element(value="Attribute_name_link")) - self.retype_element(Element(value="Attribute_optional_link_07"), Element(value="Attribute_optional_link")) - self.retype_element(Element(value="Attribute_name_link_08"), Element(value="Attribute_name_link")) - self.retype_element(Element(value="Attribute_optional_link_08"), Element(value="Attribute_optional_link")) - - return Element(id=scd) - def main(): from state.devstate import DevState From d7ba998cd72e3c8d60b5f34a813aa462fc6b3650 Mon Sep 17 00:00:00 2001 From: Andrei Bondarenko Date: Mon, 19 Jul 2021 07:09:39 +0200 Subject: [PATCH 34/46] Simple prompt implemented --- core/context/base.py | 4 + core/context/generic.py | 14 ++- core/context/simple_class_diagrams.py | 39 +++++---- core/manager.py | 10 +++ main.py | 121 ++++++++++++++++++++++++++ 5 files changed, 168 insertions(+), 20 deletions(-) create mode 100644 main.py diff --git a/core/context/base.py b/core/context/base.py index 627dc2e..9a13cc9 100644 --- a/core/context/base.py +++ b/core/context/base.py @@ -17,6 +17,10 @@ class Context(ABC): def __exit__(self): pass + @abstractmethod + def exposed_methods(self): + pass + @abstractmethod def instantiate(self, type_name: String, instance_name: String): pass diff --git a/core/context/generic.py b/core/context/generic.py index 35d4ed9..bc97ce2 100644 --- a/core/context/generic.py +++ b/core/context/generic.py @@ -5,6 +5,7 @@ from core.context.bottom import BottomContext class GenericContext(Context): + def __init__(self, state: State, model: Element, metamodel: Element): super().__init__(state, model, metamodel) self.bottom = BottomContext(state, model) @@ -15,13 +16,24 @@ class GenericContext(Context): def __exit__(self): pass + def exposed_methods(self): + yield from [ + self.instantiate, + self.instantiate_value, + self.instantiate_link, + self.retype_element, + self.list_elements, + self.delete_element, + self.verify, + ] + def _type_exists(self, type_name: String, instantiate_link: bool) -> bool: metamodel_root = self.state.read_dict(self.metamodel.id, "Model") type_element = self.state.read_dict(metamodel_root, type_name.value) if type_element is None: return False else: - element_is_edge = self.state.read_edge(type_element) is not None + element_is_edge = self.state.read_edge(type_element) != (None, None) return element_is_edge == instantiate_link def instantiate(self, type_name: String, name: String): diff --git a/core/context/simple_class_diagrams.py b/core/context/simple_class_diagrams.py index c013862..97c826d 100644 --- a/core/context/simple_class_diagrams.py +++ b/core/context/simple_class_diagrams.py @@ -1,4 +1,4 @@ -from core.element import Element +from core.element import Element, String, Integer, Boolean from state.base import State from core.context.generic import GenericContext @@ -7,25 +7,26 @@ class SCDContext(GenericContext): def __init__(self, state: State, model: Element, metamodel: Element): super().__init__(state, model, metamodel) + def exposed_methods(self): + yield from super().exposed_methods() + yield from [ + self.create_class, + self.create_class_attribute, + self.create_association, + self.create_inheritance + ] -def main(): - from state.devstate import DevState + def create_class(self, name: String, lower_cardinality: Integer = Element(), upper_cardinality: Integer = Element()): + pass - s = DevState() - scd = SCDContext(s, Element(), Element()) - bootstrap = scd._bootstrap_scd() - model = s.read_dict(bootstrap.id, "Model") - x = [] - for e in s.read_outgoing(model): - label_node_edge, = s.read_outgoing(e) - _, label_node = s.read_edge(label_node_edge) - type_node = s.read_dict(label_node, "Type") - x.append(f"{s.read_value(label_node)} : {s.read_value(type_node)}") - for t in sorted(x): - print(t) + def create_class_attribute(self, class_name: String, name: String, optional: Boolean = Element(value=False)): + pass - # s.dump("out/scd.dot", "out/scd.png") + def create_association(self, source_class_name: String, target_class_name: String, name: String, + source_lower_cardinality: Integer = Element(), target_lower_cardinality: Integer = Element(), + source_upper_cardinality: Integer = Element(), target_upper_cardinality: Integer = Element() + ): + pass - -if __name__ == '__main__': - main() + def create_inheritance(self, parent_class_name: String, child_class_name: String): + pass diff --git a/core/manager.py b/core/manager.py index 4c82b45..2df048c 100644 --- a/core/manager.py +++ b/core/manager.py @@ -6,6 +6,16 @@ class Manager: def __init__(self, state: State): self.state = state + def exposed_methods(self): + yield from [ + self.new_model, + self.get_model, + self.rename_model, + self.retype_model, + self.delete_model, + self.list_models, + ] + def new_model(self, name: String, type_model_name: String) -> Element: root = self.state.read_root() mm_bottom = self.state.read_dict(root, type_model_name.value) diff --git a/main.py b/main.py new file mode 100644 index 0000000..918f51d --- /dev/null +++ b/main.py @@ -0,0 +1,121 @@ +from core.element import Element +from core.manager import Manager +from core.context.bottom import BottomContext +from core.context.generic import GenericContext +from core.context import custom_contexts +from state.devstate import DevState +from bootstrap.simple_class_diagrams import bootstrap_scd + +import inspect +from ast import literal_eval +import sys +import signal +signal.signal(signal.SIGINT, signal.default_int_handler) + + +def params(method): + annotations = { + "core.element.Element[str]": "String", + "core.element.Element[int]": "Integer", + "core.element.Element[float]": "Float", + "core.element.Element[bool]": "Boolean" + } + sign = inspect.signature(method) + for param in sign.parameters.values(): + param_type = annotations.get(str(param.annotation), "Element") + if param.default is param.empty: + yield param.name, param_type + else: + yield param.name, param_type, param.default + + +def input_args(method): + args = [] + for param_tuple in params(method): + if len(param_tuple) == 3: + param_name, param_type, param_default = param_tuple + arg = input(f"{param_type} {param_name} (default = {param_default.value})? ") + if arg == '': + arg = param_default.value + else: + param_name, param_type = param_tuple + while True: + arg = input(f"{param_type} {param_name}? ") + if arg == '': + print("This arguments is non-optional, please provide a value.") + else: + break + if arg is None: + arg = Element() + elif param_type in {"Boolean", "Integer", "Float"}: + arg = Element(value=literal_eval(arg)) + else: + arg = Element(value=arg) + args.append(arg) + return args + + +def model_management_loop(manager: Manager): + try: + print("\nCurrently no model is loaded. Following model management operations are available:") + for m in manager.exposed_methods(): + print(f"\t* {m.__name__}") + while True: + command = input("\nPlease enter a command: ") + try: + method = next(m for m in manager.exposed_methods() if m.__name__ == command) + user_args = input_args(method) + model = method(*user_args) + if model is not None and not model.is_none(): + metamodel = Element(id=manager.state.read_dict(model.id, "Metamodel")) + modelling_loop(manager, model, metamodel) + break + except StopIteration: + print("Invalid command, please try again.") + except KeyboardInterrupt: + print("\nBye!") + sys.exit() + + +def modelling_loop(manager: Manager, model: Element, metamodel: Element): + model_name = manager.state.read_value(model.id) + metamodel_name = manager.state.read_value(metamodel.id) + if metamodel.is_none(): + context = BottomContext(manager.state, model) + print(f"No metamodel found. Model {model_name} has been loaded in context Bottom.") + else: + context = custom_contexts.get(metamodel_name, GenericContext)(manager.state, model, metamodel) + print(type(context)) + print(f"Model {model_name} has been loaded in context {metamodel_name}.") + print(f"To return to the model management interface press CTRL + C.") + print(f"The following operations are available in this context:") + for m in context.exposed_methods(): + print(f"\t* {m.__name__}") + try: + while True: + command = input("\nPlease enter a command: ") + try: + method = next(m for m in context.exposed_methods() if m.__name__ == command) + user_args = input_args(method) + model = method(*user_args) + if model is not None and not model.is_none(): + metamodel = Element(id=manager.state.read_dict(model.id, "Metamodel")) + modelling_loop(manager, model, metamodel) + break + except StopIteration: + print("Invalid command, please try again.") + except KeyboardInterrupt: + pass + + +if __name__ == '__main__': + state = DevState() + bootstrap_scd(state) + + man = Manager(state) + print("Welcome to MV2!") + while True: + model_management_loop(man) + + + From 2e3576a1a87993c5915e945f3bbb22ef3a2ae12b Mon Sep 17 00:00:00 2001 From: Andrei Bondarenko Date: Mon, 19 Jul 2021 09:08:10 +0200 Subject: [PATCH 35/46] SCDContext should be done --- bootstrap/simple_class_diagrams.py | 102 +++++++++++++++----------- core/context/generic.py | 5 +- core/context/simple_class_diagrams.py | 97 +++++++++++++++++++++++- main.py | 3 + 4 files changed, 157 insertions(+), 50 deletions(-) diff --git a/bootstrap/simple_class_diagrams.py b/bootstrap/simple_class_diagrams.py index bfe9289..a2278cd 100644 --- a/bootstrap/simple_class_diagrams.py +++ b/bootstrap/simple_class_diagrams.py @@ -68,18 +68,18 @@ def bootstrap_scd(state: State) -> Element: Element(value="Element")) # AttributeLinks --> elements that will be typed by AttributeLink - ctx.bottom.add_edge(Element(value="Class_attr01"), Element(value="Class"), + ctx.bottom.add_edge(Element(value="Class_lower_cardinality_link"), Element(value="Class"), Element(value="Class_lower_cardinality")) - ctx.bottom.add_edge(Element(value="Class_attr02"), Element(value="Class"), + ctx.bottom.add_edge(Element(value="Class_upper_cardinality_link"), Element(value="Class"), Element(value="Class_upper_cardinality")) - ctx.bottom.add_edge(Element(value="Association_attr01"), Element(value="Association"), + ctx.bottom.add_edge(Element(value="Association_source_lower_cardinality_link"), Element(value="Association"), Element(value="Association_source_lower_cardinality")) - ctx.bottom.add_edge(Element(value="Association_attr02"), Element(value="Association"), + ctx.bottom.add_edge(Element(value="Association_source_upper_cardinality_link"), Element(value="Association"), Element(value="Association_source_upper_cardinality")) - ctx.bottom.add_edge(Element(value="Association_attr03"), Element(value="Association"), + ctx.bottom.add_edge(Element(value="Association_target_lower_cardinality_link"), Element(value="Association"), Element(value="Association_target_lower_cardinality")) - ctx.bottom.add_edge(Element(value="Association_attr04"), Element(value="Association"), + ctx.bottom.add_edge(Element(value="Association_target_upper_cardinality_link"), Element(value="Association"), Element(value="Association_target_upper_cardinality")) ctx.bottom.add_edge(Element(value="Attribute_name_link"), Element(value="Attribute"), @@ -88,43 +88,49 @@ def bootstrap_scd(state: State) -> Element: Element(value="Attribute_optional")) # AttributeLink instances --> elements that will be typed by one of the AttributeLink defined above - ctx.bottom.add_edge(Element(value="Attribute_name_link_01"), Element(value="Attribute_name"), + ctx.bottom.add_edge(Element(value="Attribute_name.name_link"), Element(value="Attribute_name"), Element(value="Attribute_name.name")) - ctx.bottom.add_edge(Element(value="Attribute_optional_link_01"), Element(value="Attribute_name"), + ctx.bottom.add_edge(Element(value="Attribute_name.optional_link"), Element(value="Attribute_name"), Element(value="Attribute_name.optional")) - ctx.bottom.add_edge(Element(value="Attribute_name_link_02"), Element(value="Attribute_optional"), + ctx.bottom.add_edge(Element(value="Attribute_optional.name_link"), Element(value="Attribute_optional"), Element(value="Attribute_optional.name")) - ctx.bottom.add_edge(Element(value="Attribute_optional_link_02"), Element(value="Attribute_optional"), + ctx.bottom.add_edge(Element(value="Attribute_optional.optional_link"), Element(value="Attribute_optional"), Element(value="Attribute_optional.optional")) - ctx.bottom.add_edge(Element(value="Attribute_name_link_03"), Element(value="Class_lower_cardinality"), + ctx.bottom.add_edge(Element(value="Class_lower_cardinality.name_link"), Element(value="Class_lower_cardinality"), Element(value="Class_lower_cardinality.name")) - ctx.bottom.add_edge(Element(value="Attribute_optional_link_03"), Element(value="Class_lower_cardinality"), + ctx.bottom.add_edge(Element(value="Class_lower_cardinality.optional_link"), + Element(value="Class_lower_cardinality"), Element(value="Class_lower_cardinality.optional")) - ctx.bottom.add_edge(Element(value="Attribute_name_link_04"), Element(value="Class_upper_cardinality"), + ctx.bottom.add_edge(Element(value="Class_upper_cardinality.name_link"), Element(value="Class_upper_cardinality"), Element(value="Class_upper_cardinality.name")) - ctx.bottom.add_edge(Element(value="Attribute_optional_link_04"), Element(value="Class_upper_cardinality"), + ctx.bottom.add_edge(Element(value="Class_upper_cardinality.optional_link"), + Element(value="Class_upper_cardinality"), Element(value="Class_upper_cardinality.optional")) - ctx.bottom.add_edge(Element(value="Attribute_name_link_05"), Element(value="Association_source_lower_cardinality"), + ctx.bottom.add_edge(Element(value="Association_source_lower_cardinality.name_link"), + Element(value="Association_source_lower_cardinality"), Element(value="Association_source_lower_cardinality.name")) - ctx.bottom.add_edge(Element(value="Attribute_optional_link_05"), + ctx.bottom.add_edge(Element(value="Association_source_lower_cardinality.optional_link"), Element(value="Association_source_lower_cardinality"), Element(value="Association_source_lower_cardinality.optional")) - ctx.bottom.add_edge(Element(value="Attribute_name_link_06"), Element(value="Association_source_upper_cardinality"), + ctx.bottom.add_edge(Element(value="Association_source_upper_cardinality.name_link"), + Element(value="Association_source_upper_cardinality"), Element(value="Association_source_upper_cardinality.name")) - ctx.bottom.add_edge(Element(value="Attribute_optional_link_06"), + ctx.bottom.add_edge(Element(value="Association_source_upper_cardinality.optional_link"), Element(value="Association_source_upper_cardinality"), Element(value="Association_source_upper_cardinality.optional")) - ctx.bottom.add_edge(Element(value="Attribute_name_link_07"), Element(value="Association_target_lower_cardinality"), + ctx.bottom.add_edge(Element(value="Association_target_lower_cardinality.name_link"), + Element(value="Association_target_lower_cardinality"), Element(value="Association_target_lower_cardinality.name")) - ctx.bottom.add_edge(Element(value="Attribute_optional_link_07"), + ctx.bottom.add_edge(Element(value="Association_target_lower_cardinality.optional_link"), Element(value="Association_target_lower_cardinality"), Element(value="Association_target_lower_cardinality.optional")) - ctx.bottom.add_edge(Element(value="Attribute_name_link_08"), Element(value="Association_target_upper_cardinality"), + ctx.bottom.add_edge(Element(value="Association_target_upper_cardinality.name_link"), + Element(value="Association_target_upper_cardinality"), Element(value="Association_target_upper_cardinality.name")) - ctx.bottom.add_edge(Element(value="Attribute_optional_link_08"), + ctx.bottom.add_edge(Element(value="Association_target_upper_cardinality.optional_link"), Element(value="Association_target_upper_cardinality"), Element(value="Association_target_upper_cardinality.optional")) @@ -149,12 +155,12 @@ def bootstrap_scd(state: State) -> Element: ctx.retype_element(Element(value="Attribute_name"), Element(value="Attribute")) ctx.retype_element(Element(value="Attribute_optional"), Element(value="Attribute")) - ctx.retype_element(Element(value="Class_attr01"), Element(value="AttributeLink")) - ctx.retype_element(Element(value="Class_attr02"), Element(value="AttributeLink")) - ctx.retype_element(Element(value="Association_attr01"), Element(value="AttributeLink")) - ctx.retype_element(Element(value="Association_attr02"), Element(value="AttributeLink")) - ctx.retype_element(Element(value="Association_attr03"), Element(value="AttributeLink")) - ctx.retype_element(Element(value="Association_attr04"), Element(value="AttributeLink")) + ctx.retype_element(Element(value="Class_lower_cardinality_link"), Element(value="AttributeLink")) + ctx.retype_element(Element(value="Class_upper_cardinality_link"), Element(value="AttributeLink")) + ctx.retype_element(Element(value="Association_source_lower_cardinality_link"), Element(value="AttributeLink")) + ctx.retype_element(Element(value="Association_source_upper_cardinality_link"), Element(value="AttributeLink")) + ctx.retype_element(Element(value="Association_target_lower_cardinality_link"), Element(value="AttributeLink")) + ctx.retype_element(Element(value="Association_target_upper_cardinality_link"), Element(value="AttributeLink")) ctx.retype_element(Element(value="Attribute_name_link"), Element(value="AttributeLink")) ctx.retype_element(Element(value="Attribute_optional_link"), Element(value="AttributeLink")) @@ -184,21 +190,29 @@ def bootstrap_scd(state: State) -> Element: ctx.retype_element(Element(value="Association_target_upper_cardinality.optional"), Element(value="Attribute_optional")) - ctx.retype_element(Element(value="Attribute_name_link_01"), Element(value="Attribute_name_link")) - ctx.retype_element(Element(value="Attribute_optional_link_01"), Element(value="Attribute_optional_link")) - ctx.retype_element(Element(value="Attribute_name_link_02"), Element(value="Attribute_name_link")) - ctx.retype_element(Element(value="Attribute_optional_link_02"), Element(value="Attribute_optional_link")) - ctx.retype_element(Element(value="Attribute_name_link_03"), Element(value="Attribute_name_link")) - ctx.retype_element(Element(value="Attribute_optional_link_03"), Element(value="Attribute_optional_link")) - ctx.retype_element(Element(value="Attribute_name_link_04"), Element(value="Attribute_name_link")) - ctx.retype_element(Element(value="Attribute_optional_link_04"), Element(value="Attribute_optional_link")) - ctx.retype_element(Element(value="Attribute_name_link_05"), Element(value="Attribute_name_link")) - ctx.retype_element(Element(value="Attribute_optional_link_05"), Element(value="Attribute_optional_link")) - ctx.retype_element(Element(value="Attribute_name_link_06"), Element(value="Attribute_name_link")) - ctx.retype_element(Element(value="Attribute_optional_link_06"), Element(value="Attribute_optional_link")) - ctx.retype_element(Element(value="Attribute_name_link_07"), Element(value="Attribute_name_link")) - ctx.retype_element(Element(value="Attribute_optional_link_07"), Element(value="Attribute_optional_link")) - ctx.retype_element(Element(value="Attribute_name_link_08"), Element(value="Attribute_name_link")) - ctx.retype_element(Element(value="Attribute_optional_link_08"), Element(value="Attribute_optional_link")) + ctx.retype_element(Element(value="Attribute_name.name_link"), Element(value="Attribute_name_link")) + ctx.retype_element(Element(value="Attribute_name.optional_link"), Element(value="Attribute_optional_link")) + ctx.retype_element(Element(value="Attribute_optional.name_link"), Element(value="Attribute_name_link")) + ctx.retype_element(Element(value="Attribute_optional.optional_link"), Element(value="Attribute_optional_link")) + ctx.retype_element(Element(value="Class_lower_cardinality.name_link"), Element(value="Attribute_name_link")) + ctx.retype_element(Element(value="Class_lower_cardinality.optional_link"), Element(value="Attribute_optional_link")) + ctx.retype_element(Element(value="Class_upper_cardinality.name_link"), Element(value="Attribute_name_link")) + ctx.retype_element(Element(value="Class_upper_cardinality.optional_link"), Element(value="Attribute_optional_link")) + ctx.retype_element(Element(value="Association_source_lower_cardinality.name_link"), + Element(value="Attribute_name_link")) + ctx.retype_element(Element(value="Association_source_lower_cardinality.optional_link"), + Element(value="Attribute_optional_link")) + ctx.retype_element(Element(value="Association_source_upper_cardinality.name_link"), + Element(value="Attribute_name_link")) + ctx.retype_element(Element(value="Association_source_upper_cardinality.optional_link"), + Element(value="Attribute_optional_link")) + ctx.retype_element(Element(value="Association_target_lower_cardinality.name_link"), + Element(value="Attribute_name_link")) + ctx.retype_element(Element(value="Association_target_lower_cardinality.optional_link"), + Element(value="Attribute_optional_link")) + ctx.retype_element(Element(value="Association_target_upper_cardinality.name_link"), + Element(value="Attribute_name_link")) + ctx.retype_element(Element(value="Association_target_upper_cardinality.optional_link"), + Element(value="Attribute_optional_link")) return Element(id=scd) diff --git a/core/context/generic.py b/core/context/generic.py index bc97ce2..717c8f9 100644 --- a/core/context/generic.py +++ b/core/context/generic.py @@ -22,8 +22,9 @@ class GenericContext(Context): self.instantiate_value, self.instantiate_link, self.retype_element, - self.list_elements, self.delete_element, + self.list_elements, + self.list_types, self.verify, ] @@ -47,7 +48,7 @@ class GenericContext(Context): if not self._type_exists(type_name, instantiate_link=False): print(f"Attempting to instantiate element with invalid type: {type_name.value}") else: - self.bottom.add_value(name, value.value) + self.bottom.add_value(name, value) self.retype_element(name, type_name) def instantiate_link(self, type_name: String, name: String, source: String, target: String): diff --git a/core/context/simple_class_diagrams.py b/core/context/simple_class_diagrams.py index 97c826d..753c97e 100644 --- a/core/context/simple_class_diagrams.py +++ b/core/context/simple_class_diagrams.py @@ -17,16 +17,105 @@ class SCDContext(GenericContext): ] def create_class(self, name: String, lower_cardinality: Integer = Element(), upper_cardinality: Integer = Element()): - pass + self.instantiate(Element(value="Class"), name) + if lower_cardinality.value is not None: + self.instantiate_value( + Element(value="Class_lower_cardinality"), + Element(value=name.value + ".lower_cardinality"), + lower_cardinality + ) + self.instantiate_link( + Element(value="Class_lower_cardinality_link"), + Element(value=name.value + ".lower_cardinality_link"), + name, + Element(value=name.value + ".lower_cardinality") + ) + if upper_cardinality.value is not None: + self.instantiate_value( + Element(value="Class_upper_cardinality"), + Element(value=name.value + ".upper_cardinality"), + upper_cardinality + ) + self.instantiate_link( + Element(value="Class_upper_cardinality_link"), + Element(value=name.value + ".upper_cardinality_link"), + name, + Element(value=name.value + ".upper_cardinality") + ) def create_class_attribute(self, class_name: String, name: String, optional: Boolean = Element(value=False)): - pass + # create attribute + element_name = Element(value=f"{class_name.value}_{name.value}") + element_link_name = Element(value=f"{class_name.value}_{name.value}_link") + self.instantiate(Element(value="Attribute"), element_name) + self.instantiate_link(Element(value="AttributeLink"), element_link_name, class_name, element_name) + # set attribute's attributes + attr_name_name = Element(value=f"{class_name.value}_{name.value}.name") + attr_optional_name = Element(value=f"{class_name.value}_{name.value}.optional") + attr_name_link_name = Element(value=f"{class_name.value}_{name.value}.name_link") + attr_optional_link_name = Element(value=f"{class_name.value}_{name.value}.optional_link") + self.instantiate_value(Element(value="Attribute_name"), attr_name_name, name) + self.instantiate_value(Element(value="Attribute_optional"), attr_optional_name, optional) + self.instantiate_link(Element(value="Attribute_name_link"), attr_name_link_name, element_name, attr_name_name) + self.instantiate_link(Element(value="Attribute_optional_link"), attr_optional_link_name, element_name, attr_optional_name) def create_association(self, source_class_name: String, target_class_name: String, name: String, source_lower_cardinality: Integer = Element(), target_lower_cardinality: Integer = Element(), source_upper_cardinality: Integer = Element(), target_upper_cardinality: Integer = Element() ): - pass + self.instantiate_link(Element(value="Association"), name, source_class_name, target_class_name) + if source_lower_cardinality.value is not None: + self.instantiate_value( + Element(value="Association_source_lower_cardinality"), + Element(value=name.value + ".source_lower_cardinality"), + source_lower_cardinality + ) + self.instantiate_link( + Element(value="Association_source_lower_cardinality_link"), + Element(value=name.value + ".source_lower_cardinality_link"), + name, + Element(value=name.value + ".source_lower_cardinality") + ) + if source_upper_cardinality.value is not None: + self.instantiate_value( + Element(value="Association_source_upper_cardinality"), + Element(value=name.value + ".source_upper_cardinality"), + source_upper_cardinality + ) + self.instantiate_link( + Element(value="Association_source_upper_cardinality_link"), + Element(value=name.value + ".source_upper_cardinality_link"), + name, + Element(value=name.value + ".source_upper_cardinality") + ) + if target_lower_cardinality.value is not None: + self.instantiate_value( + Element(value="Association_target_lower_cardinality"), + Element(value=name.value + ".target_lower_cardinality"), + target_lower_cardinality + ) + self.instantiate_link( + Element(value="Association_target_lower_cardinality_link"), + Element(value=name.value + ".target_lower_cardinality_link"), + name, + Element(value=name.value + ".target_lower_cardinality") + ) + if target_upper_cardinality.value is not None: + self.instantiate_value( + Element(value="Association_target_upper_cardinality"), + Element(value=name.value + ".target_upper_cardinality"), + target_upper_cardinality + ) + self.instantiate_link( + Element(value="Association_target_upper_cardinality_link"), + Element(value=name.value + ".target_upper_cardinality_link"), + name, + Element(value=name.value + ".target_upper_cardinality") + ) def create_inheritance(self, parent_class_name: String, child_class_name: String): - pass + self.instantiate_link( + Element(value="Inheritance"), + Element(value=f"{child_class_name.value}_inherits_from_{parent_class_name.value}"), + child_class_name, + parent_class_name) diff --git a/main.py b/main.py index 918f51d..32cce95 100644 --- a/main.py +++ b/main.py @@ -37,6 +37,9 @@ def input_args(method): arg = input(f"{param_type} {param_name} (default = {param_default.value})? ") if arg == '': arg = param_default.value + arg = Element(value=arg) + args.append(arg) + continue else: param_name, param_type = param_tuple while True: From c64e348bf26eb4823518ae0a8402ea0ce823db9f Mon Sep 17 00:00:00 2001 From: Andrei Bondarenko Date: Sat, 24 Jul 2021 20:52:36 +0200 Subject: [PATCH 36/46] Clean slate --- bootstrap/__init__.py | 0 bootstrap/simple_class_diagrams.py | 218 -------------------------- core/__init__.py | 0 core/context/__init__.py | 5 - core/context/base.py | 63 -------- core/context/bottom.py | 107 ------------- core/context/generic.py | 98 ------------ core/context/simple_class_diagrams.py | 121 -------------- core/element.py | 34 ---- core/manager.py | 88 ----------- main.py | 124 --------------- requirements_dev.txt | 2 - 12 files changed, 860 deletions(-) delete mode 100644 bootstrap/__init__.py delete mode 100644 bootstrap/simple_class_diagrams.py delete mode 100644 core/__init__.py delete mode 100644 core/context/__init__.py delete mode 100644 core/context/base.py delete mode 100644 core/context/bottom.py delete mode 100644 core/context/generic.py delete mode 100644 core/context/simple_class_diagrams.py delete mode 100644 core/element.py delete mode 100644 core/manager.py delete mode 100644 main.py delete mode 100644 requirements_dev.txt diff --git a/bootstrap/__init__.py b/bootstrap/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/bootstrap/simple_class_diagrams.py b/bootstrap/simple_class_diagrams.py deleted file mode 100644 index a2278cd..0000000 --- a/bootstrap/simple_class_diagrams.py +++ /dev/null @@ -1,218 +0,0 @@ -from core.element import Element -from state.base import State, STRING, INTEGER, BOOLEAN, TYPE -from core.context.generic import GenericContext - - -def bootstrap_scd(state: State) -> Element: - ctx = GenericContext(state, Element(), Element()) - - scd = state.create_nodevalue("SimpleClassDiagrams") - state.create_dict(state.read_root(), "SimpleClassDiagrams", scd) - state.create_dict(scd, "Model", state.create_node()) - state.create_dict(scd, "Metamodel", scd) - ctx.__init__(state, Element(id=scd), Element(id=scd)) - - # Classes --> elements that will be typed by Class - ctx.bottom.add_node(Element(value="Element")) - ctx.bottom.add_node(Element(value="Class")) - ctx.bottom.add_value(Element(value="Attribute"), Element(value=TYPE)) - - # Associations --> elements that will be typed by Association - ctx.bottom.add_edge(Element(value="Association"), Element(value="Class"), Element(value="Class")) - ctx.bottom.add_edge(Element(value="Inheritance"), Element(value="Element"), Element(value="Element")) - ctx.bottom.add_edge(Element(value="AttributeLink"), Element(value="Element"), Element(value="Attribute")) - - # Attributes --> elements that will be typed by Attribute - ctx.bottom.add_value(Element(value="Class_lower_cardinality"), Element(value=INTEGER)) - ctx.bottom.add_value(Element(value="Class_upper_cardinality"), Element(value=INTEGER)) - - ctx.bottom.add_value(Element(value="Association_source_lower_cardinality"), Element(value=INTEGER)) - ctx.bottom.add_value(Element(value="Association_source_upper_cardinality"), Element(value=INTEGER)) - ctx.bottom.add_value(Element(value="Association_target_lower_cardinality"), Element(value=INTEGER)) - ctx.bottom.add_value(Element(value="Association_target_upper_cardinality"), Element(value=INTEGER)) - - ctx.bottom.add_value(Element(value="Attribute_name"), Element(value=STRING)) - ctx.bottom.add_value(Element(value="Attribute_optional"), Element(value=BOOLEAN)) - - # Attribute instances --> elements that will be typed by one of the Attributes defined above - ctx.bottom.add_value(Element(value="Attribute_name.name"), Element(value="name")) - ctx.bottom.add_value(Element(value="Attribute_name.optional"), Element(value=False)) - ctx.bottom.add_value(Element(value="Attribute_optional.name"), Element(value="optional")) - ctx.bottom.add_value(Element(value="Attribute_optional.optional"), Element(value=False)) - - ctx.bottom.add_value(Element(value="Class_lower_cardinality.name"), Element(value="lower_cardinality")) - ctx.bottom.add_value(Element(value="Class_lower_cardinality.optional"), Element(value=True)) - ctx.bottom.add_value(Element(value="Class_upper_cardinality.name"), Element(value="upper_cardinality")) - ctx.bottom.add_value(Element(value="Class_upper_cardinality.optional"), Element(value=True)) - - ctx.bottom.add_value(Element(value="Association_source_lower_cardinality.name"), - Element(value="source_lower_cardinality")) - ctx.bottom.add_value(Element(value="Association_source_lower_cardinality.optional"), Element(value=True)) - ctx.bottom.add_value(Element(value="Association_source_upper_cardinality.name"), - Element(value="source_upper_cardinality")) - ctx.bottom.add_value(Element(value="Association_source_upper_cardinality.optional"), Element(value=True)) - - ctx.bottom.add_value(Element(value="Association_target_lower_cardinality.name"), - Element(value="target_lower_cardinality")) - ctx.bottom.add_value(Element(value="Association_target_lower_cardinality.optional"), Element(value=True)) - ctx.bottom.add_value(Element(value="Association_target_upper_cardinality.name"), - Element(value="target_upper_cardinality")) - ctx.bottom.add_value(Element(value="Association_target_upper_cardinality.optional"), Element(value=True)) - - # Inheritance instances --> elements that will be typed by Inheritance - ctx.bottom.add_edge(Element(value="class_inh_element"), Element(value="Class"), Element(value="Element")) - ctx.bottom.add_edge(Element(value="attribute_inh_element"), Element(value="Attribute"), Element(value="Element")) - ctx.bottom.add_edge(Element(value="association_inh_element"), Element(value="Association"), - Element(value="Element")) - ctx.bottom.add_edge(Element(value="attributelink_inh_element"), Element(value="AttributeLink"), - Element(value="Element")) - - # AttributeLinks --> elements that will be typed by AttributeLink - ctx.bottom.add_edge(Element(value="Class_lower_cardinality_link"), Element(value="Class"), - Element(value="Class_lower_cardinality")) - ctx.bottom.add_edge(Element(value="Class_upper_cardinality_link"), Element(value="Class"), - Element(value="Class_upper_cardinality")) - - ctx.bottom.add_edge(Element(value="Association_source_lower_cardinality_link"), Element(value="Association"), - Element(value="Association_source_lower_cardinality")) - ctx.bottom.add_edge(Element(value="Association_source_upper_cardinality_link"), Element(value="Association"), - Element(value="Association_source_upper_cardinality")) - ctx.bottom.add_edge(Element(value="Association_target_lower_cardinality_link"), Element(value="Association"), - Element(value="Association_target_lower_cardinality")) - ctx.bottom.add_edge(Element(value="Association_target_upper_cardinality_link"), Element(value="Association"), - Element(value="Association_target_upper_cardinality")) - - ctx.bottom.add_edge(Element(value="Attribute_name_link"), Element(value="Attribute"), - Element(value="Attribute_name")) - ctx.bottom.add_edge(Element(value="Attribute_optional_link"), Element(value="Attribute"), - Element(value="Attribute_optional")) - - # AttributeLink instances --> elements that will be typed by one of the AttributeLink defined above - ctx.bottom.add_edge(Element(value="Attribute_name.name_link"), Element(value="Attribute_name"), - Element(value="Attribute_name.name")) - ctx.bottom.add_edge(Element(value="Attribute_name.optional_link"), Element(value="Attribute_name"), - Element(value="Attribute_name.optional")) - ctx.bottom.add_edge(Element(value="Attribute_optional.name_link"), Element(value="Attribute_optional"), - Element(value="Attribute_optional.name")) - ctx.bottom.add_edge(Element(value="Attribute_optional.optional_link"), Element(value="Attribute_optional"), - Element(value="Attribute_optional.optional")) - - ctx.bottom.add_edge(Element(value="Class_lower_cardinality.name_link"), Element(value="Class_lower_cardinality"), - Element(value="Class_lower_cardinality.name")) - ctx.bottom.add_edge(Element(value="Class_lower_cardinality.optional_link"), - Element(value="Class_lower_cardinality"), - Element(value="Class_lower_cardinality.optional")) - ctx.bottom.add_edge(Element(value="Class_upper_cardinality.name_link"), Element(value="Class_upper_cardinality"), - Element(value="Class_upper_cardinality.name")) - ctx.bottom.add_edge(Element(value="Class_upper_cardinality.optional_link"), - Element(value="Class_upper_cardinality"), - Element(value="Class_upper_cardinality.optional")) - - ctx.bottom.add_edge(Element(value="Association_source_lower_cardinality.name_link"), - Element(value="Association_source_lower_cardinality"), - Element(value="Association_source_lower_cardinality.name")) - ctx.bottom.add_edge(Element(value="Association_source_lower_cardinality.optional_link"), - Element(value="Association_source_lower_cardinality"), - Element(value="Association_source_lower_cardinality.optional")) - ctx.bottom.add_edge(Element(value="Association_source_upper_cardinality.name_link"), - Element(value="Association_source_upper_cardinality"), - Element(value="Association_source_upper_cardinality.name")) - ctx.bottom.add_edge(Element(value="Association_source_upper_cardinality.optional_link"), - Element(value="Association_source_upper_cardinality"), - Element(value="Association_source_upper_cardinality.optional")) - - ctx.bottom.add_edge(Element(value="Association_target_lower_cardinality.name_link"), - Element(value="Association_target_lower_cardinality"), - Element(value="Association_target_lower_cardinality.name")) - ctx.bottom.add_edge(Element(value="Association_target_lower_cardinality.optional_link"), - Element(value="Association_target_lower_cardinality"), - Element(value="Association_target_lower_cardinality.optional")) - ctx.bottom.add_edge(Element(value="Association_target_upper_cardinality.name_link"), - Element(value="Association_target_upper_cardinality"), - Element(value="Association_target_upper_cardinality.name")) - ctx.bottom.add_edge(Element(value="Association_target_upper_cardinality.optional_link"), - Element(value="Association_target_upper_cardinality"), - Element(value="Association_target_upper_cardinality.optional")) - - """ - Retype the elements of the model. - This way we make the model "metacircular". - """ - ctx.retype_element(Element(value="Element"), Element(value="Class")) - ctx.retype_element(Element(value="Class"), Element(value="Class")) - ctx.retype_element(Element(value="Attribute"), Element(value="Class")) - - ctx.retype_element(Element(value="Association"), Element(value="Association")) - ctx.retype_element(Element(value="Inheritance"), Element(value="Association")) - ctx.retype_element(Element(value="AttributeLink"), Element(value="Association")) - - ctx.retype_element(Element(value="Class_lower_cardinality"), Element(value="Attribute")) - ctx.retype_element(Element(value="Class_upper_cardinality"), Element(value="Attribute")) - ctx.retype_element(Element(value="Association_source_lower_cardinality"), Element(value="Attribute")) - ctx.retype_element(Element(value="Association_source_upper_cardinality"), Element(value="Attribute")) - ctx.retype_element(Element(value="Association_target_lower_cardinality"), Element(value="Attribute")) - ctx.retype_element(Element(value="Association_target_upper_cardinality"), Element(value="Attribute")) - ctx.retype_element(Element(value="Attribute_name"), Element(value="Attribute")) - ctx.retype_element(Element(value="Attribute_optional"), Element(value="Attribute")) - - ctx.retype_element(Element(value="Class_lower_cardinality_link"), Element(value="AttributeLink")) - ctx.retype_element(Element(value="Class_upper_cardinality_link"), Element(value="AttributeLink")) - ctx.retype_element(Element(value="Association_source_lower_cardinality_link"), Element(value="AttributeLink")) - ctx.retype_element(Element(value="Association_source_upper_cardinality_link"), Element(value="AttributeLink")) - ctx.retype_element(Element(value="Association_target_lower_cardinality_link"), Element(value="AttributeLink")) - ctx.retype_element(Element(value="Association_target_upper_cardinality_link"), Element(value="AttributeLink")) - ctx.retype_element(Element(value="Attribute_name_link"), Element(value="AttributeLink")) - ctx.retype_element(Element(value="Attribute_optional_link"), Element(value="AttributeLink")) - - ctx.retype_element(Element(value="class_inh_element"), Element(value="Inheritance")) - ctx.retype_element(Element(value="attribute_inh_element"), Element(value="Inheritance")) - ctx.retype_element(Element(value="association_inh_element"), Element(value="Inheritance")) - ctx.retype_element(Element(value="attributelink_inh_element"), Element(value="Inheritance")) - - ctx.retype_element(Element(value="Attribute_name.name"), Element(value="Attribute_name")) - ctx.retype_element(Element(value="Attribute_name.optional"), Element(value="Attribute_optional")) - ctx.retype_element(Element(value="Attribute_optional.name"), Element(value="Attribute_name")) - ctx.retype_element(Element(value="Attribute_optional.optional"), Element(value="Attribute_optional")) - ctx.retype_element(Element(value="Class_lower_cardinality.name"), Element(value="Attribute_name")) - ctx.retype_element(Element(value="Class_lower_cardinality.optional"), Element(value="Attribute_optional")) - ctx.retype_element(Element(value="Class_upper_cardinality.name"), Element(value="Attribute_name")) - ctx.retype_element(Element(value="Class_upper_cardinality.optional"), Element(value="Attribute_optional")) - ctx.retype_element(Element(value="Association_source_lower_cardinality.name"), Element(value="Attribute_name")) - ctx.retype_element(Element(value="Association_source_lower_cardinality.optional"), - Element(value="Attribute_optional")) - ctx.retype_element(Element(value="Association_source_upper_cardinality.name"), Element(value="Attribute_name")) - ctx.retype_element(Element(value="Association_source_upper_cardinality.optional"), - Element(value="Attribute_optional")) - ctx.retype_element(Element(value="Association_target_lower_cardinality.name"), Element(value="Attribute_name")) - ctx.retype_element(Element(value="Association_target_lower_cardinality.optional"), - Element(value="Attribute_optional")) - ctx.retype_element(Element(value="Association_target_upper_cardinality.name"), Element(value="Attribute_name")) - ctx.retype_element(Element(value="Association_target_upper_cardinality.optional"), - Element(value="Attribute_optional")) - - ctx.retype_element(Element(value="Attribute_name.name_link"), Element(value="Attribute_name_link")) - ctx.retype_element(Element(value="Attribute_name.optional_link"), Element(value="Attribute_optional_link")) - ctx.retype_element(Element(value="Attribute_optional.name_link"), Element(value="Attribute_name_link")) - ctx.retype_element(Element(value="Attribute_optional.optional_link"), Element(value="Attribute_optional_link")) - ctx.retype_element(Element(value="Class_lower_cardinality.name_link"), Element(value="Attribute_name_link")) - ctx.retype_element(Element(value="Class_lower_cardinality.optional_link"), Element(value="Attribute_optional_link")) - ctx.retype_element(Element(value="Class_upper_cardinality.name_link"), Element(value="Attribute_name_link")) - ctx.retype_element(Element(value="Class_upper_cardinality.optional_link"), Element(value="Attribute_optional_link")) - ctx.retype_element(Element(value="Association_source_lower_cardinality.name_link"), - Element(value="Attribute_name_link")) - ctx.retype_element(Element(value="Association_source_lower_cardinality.optional_link"), - Element(value="Attribute_optional_link")) - ctx.retype_element(Element(value="Association_source_upper_cardinality.name_link"), - Element(value="Attribute_name_link")) - ctx.retype_element(Element(value="Association_source_upper_cardinality.optional_link"), - Element(value="Attribute_optional_link")) - ctx.retype_element(Element(value="Association_target_lower_cardinality.name_link"), - Element(value="Attribute_name_link")) - ctx.retype_element(Element(value="Association_target_lower_cardinality.optional_link"), - Element(value="Attribute_optional_link")) - ctx.retype_element(Element(value="Association_target_upper_cardinality.name_link"), - Element(value="Attribute_name_link")) - ctx.retype_element(Element(value="Association_target_upper_cardinality.optional_link"), - Element(value="Attribute_optional_link")) - - return Element(id=scd) diff --git a/core/__init__.py b/core/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/core/context/__init__.py b/core/context/__init__.py deleted file mode 100644 index 820a79d..0000000 --- a/core/context/__init__.py +++ /dev/null @@ -1,5 +0,0 @@ -from core.context.simple_class_diagrams import SCDContext - -custom_contexts = { - "SimpleClassDiagrams": SCDContext -} diff --git a/core/context/base.py b/core/context/base.py deleted file mode 100644 index 9a13cc9..0000000 --- a/core/context/base.py +++ /dev/null @@ -1,63 +0,0 @@ -from abc import ABC, abstractmethod -from state.base import State -from core.element import Element, String - - -class Context(ABC): - def __init__(self, state: State, model: Element, metamodel: Element): - self.state = state - self.model = model - self.metamodel = metamodel - - @abstractmethod - def __enter__(self): - pass - - @abstractmethod - def __exit__(self): - pass - - @abstractmethod - def exposed_methods(self): - pass - - @abstractmethod - def instantiate(self, type_name: String, instance_name: String): - pass - - @abstractmethod - def instantiate_value(self, type_name: String, instance_name: String, value: Element): - pass - - @abstractmethod - def instantiate_link(self, type_name: String, name: String, source: String, target: String): - pass - - @abstractmethod - def delete_element(self, name: String): - pass - - @abstractmethod - def verify(self): - pass - - @abstractmethod - def list_elements(self): - pass - - def list_types(self): - # can be implemented here since we assume that metamodel - # is always in graph fo, i.e. in the MV-state graph. - unsorted = [] - model_root = self.state.read_dict(self.metamodel.id, "Model") - for elem_edge in self.state.read_outgoing(model_root): - # get element name - label_edge, = self.state.read_outgoing(elem_edge) - _, label_node = self.state.read_edge(label_edge) - label = self.state.read_value(label_node) - # find element type - elem_type_node = self.state.read_dict(label_node, "Type") - elem_type = self.state.read_value(elem_type_node) - unsorted.append(f"{label} : {elem_type if elem_type is not None else '_'}") - for i in sorted(unsorted): - print(i) diff --git a/core/context/bottom.py b/core/context/bottom.py deleted file mode 100644 index 63aafb2..0000000 --- a/core/context/bottom.py +++ /dev/null @@ -1,107 +0,0 @@ -from state.base import INTEGER, FLOAT, BOOLEAN, STRING, TYPE, State -from core.element import Element, String - - -class BottomContext: - def __init__(self, state: State, model: Element): - self.state = state - self.model = model - - def __enter__(self): - return self - - def __exit__(self): - pass - - def add_node(self, name: String): - model_root = self.state.read_dict(self.model.id, "Model") - # create model element - element = self.state.create_node() - # connect to model root - self.state.create_dict(model_root, name.value, element) - # confirm that element has been added to the model - element_found = self.state.read_dict(model_root, name.value) - if element_found is None: - self.state.delete_node(element) - print(f"Warning: Invalid name {name.value}, element not created.") - return - - def add_value(self, name: String, value: Element): - model_root = self.state.read_dict(self.model.id, "Model") - # create model element - element = self.state.create_nodevalue(value.value) - if element is None: - print("Warning: Invalid value, value node not created.") - return - # connect to model root - self.state.create_dict(model_root, name.value, element) - # confirm that element has been added to the model - element_found = self.state.read_dict(model_root, name.value) - if element_found is None: - self.state.delete_node(element) - print(f"Warning: Invalid name {name.value}, element not created.") - return - - def add_edge(self, name: String, source: String, target: String): - model_root = self.state.read_dict(self.model.id, "Model") - source_element = self.state.read_dict(model_root, source.value) - if source_element is None: - print(f"Warning: Unknown source element {source.value}, edge not created.") - return - target_element = self.state.read_dict(model_root, target.value) - if target_element is None: - print(f"Warning: Unknown target element {target.value}, edge not created.") - return - # create model element - element = self.state.create_edge(source_element, target_element) - # connect to model root - self.state.create_dict(model_root, name.value, element) - # confirm that element has been added to the model - element_found = self.state.read_dict(model_root, name.value) - if element_found is None: - self.state.delete_edge(element) - print(f"Warning: Invalid name {name.value}, element not created.") - - def delete_element(self, name: String): - model_root = self.state.read_dict(self.model.id, "Model") - element = self.state.read_dict(model_root, name.value) - # could be both a node or an edge - self.state.delete_node(element) - self.state.delete_edge(element) - - def list_elements(self): - def is_edge(element: str) -> bool: - edge = self.state.read_edge(element) - return edge is not None - - def value_type(value) -> str: - mapping = { - int: INTEGER, - float: FLOAT, - str: STRING, - bool: BOOLEAN, - tuple: TYPE - } - return mapping[type(value)][0] - - unsorted = [] - model_root = self.state.read_dict(self.model.id, "Model") - for elem_edge in self.state.read_outgoing(model_root): - # get element name - label_edge, = self.state.read_outgoing(elem_edge) - _, label_node = self.state.read_edge(label_edge) - label = self.state.read_value(label_node) - # find element bottom type - _, elem = self.state.read_edge(elem_edge) - if is_edge(elem): - bottom_type = "Edge" - else: - # is_node - elem_value = self.state.read_value(elem) - if elem_value is None: - bottom_type = "Node" - else: - bottom_type = value_type - unsorted.append(f"{label} : {bottom_type}") - for i in sorted(unsorted): - print(i) diff --git a/core/context/generic.py b/core/context/generic.py deleted file mode 100644 index 717c8f9..0000000 --- a/core/context/generic.py +++ /dev/null @@ -1,98 +0,0 @@ -from core.element import Element, String, Boolean -from state.base import State -from core.context.base import Context -from core.context.bottom import BottomContext - - -class GenericContext(Context): - - def __init__(self, state: State, model: Element, metamodel: Element): - super().__init__(state, model, metamodel) - self.bottom = BottomContext(state, model) - - def __enter__(self): - pass - - def __exit__(self): - pass - - def exposed_methods(self): - yield from [ - self.instantiate, - self.instantiate_value, - self.instantiate_link, - self.retype_element, - self.delete_element, - self.list_elements, - self.list_types, - self.verify, - ] - - def _type_exists(self, type_name: String, instantiate_link: bool) -> bool: - metamodel_root = self.state.read_dict(self.metamodel.id, "Model") - type_element = self.state.read_dict(metamodel_root, type_name.value) - if type_element is None: - return False - else: - element_is_edge = self.state.read_edge(type_element) != (None, None) - return element_is_edge == instantiate_link - - def instantiate(self, type_name: String, name: String): - if not self._type_exists(type_name, instantiate_link=False): - print(f"Attempting to instantiate element with invalid type: {type_name.value}") - else: - self.bottom.add_node(name) - self.retype_element(name, type_name) - - def instantiate_value(self, type_name: String, name: String, value: Element): - if not self._type_exists(type_name, instantiate_link=False): - print(f"Attempting to instantiate element with invalid type: {type_name.value}") - else: - self.bottom.add_value(name, value) - self.retype_element(name, type_name) - - def instantiate_link(self, type_name: String, name: String, source: String, target: String): - if not self._type_exists(type_name, instantiate_link=True): - print(f"Attempting to instantiate link with invalid type: {type_name.value}") - else: - self.bottom.add_edge(name, source, target) - self.retype_element(name, type_name) - - def delete_element(self, name: String): - self.bottom.delete_element(name) - - def verify(self): - pass # TODO: implement conformance check - - def list_elements(self): - model_root = self.state.read_dict(self.model.id, "Model") - unsorted = [] - for elem_edge in self.state.read_outgoing(model_root): - # get element name - label_edge, = self.state.read_outgoing(elem_edge) - _, label_node = self.state.read_edge(label_edge) - label = self.state.read_value(label_node) - type_node = self.state.read_dict(label_node, "Type") - type_name = self.state.read_value(type_node) - unsorted.append(f"{label} : {type_name}") - for i in sorted(unsorted): - print(i) - - def retype_element(self, name: String, type_name: String): - model_root = self.state.read_dict(self.model.id, "Model") - element_edge = self.state.read_dict_edge(model_root, name.value) - if element_edge is None: - print(f"Error: Element with name {name.value} not found.") - return - label_node_edge, = self.state.read_outgoing(element_edge) - _, label_node = self.state.read_edge(label_node_edge) - # create type name node - type_name_node = self.state.create_nodevalue(type_name.value) - if type_name_node is None: - print("Error: Invalid type name, element not retyped.") - # remove any existing type node - existing = self.state.read_dict(label_node, "Type") - if existing is not None: - self.state.delete_node(existing) - # create new type node - self.state.create_dict(label_node, "Type", type_name_node) diff --git a/core/context/simple_class_diagrams.py b/core/context/simple_class_diagrams.py deleted file mode 100644 index 753c97e..0000000 --- a/core/context/simple_class_diagrams.py +++ /dev/null @@ -1,121 +0,0 @@ -from core.element import Element, String, Integer, Boolean -from state.base import State -from core.context.generic import GenericContext - - -class SCDContext(GenericContext): - def __init__(self, state: State, model: Element, metamodel: Element): - super().__init__(state, model, metamodel) - - def exposed_methods(self): - yield from super().exposed_methods() - yield from [ - self.create_class, - self.create_class_attribute, - self.create_association, - self.create_inheritance - ] - - def create_class(self, name: String, lower_cardinality: Integer = Element(), upper_cardinality: Integer = Element()): - self.instantiate(Element(value="Class"), name) - if lower_cardinality.value is not None: - self.instantiate_value( - Element(value="Class_lower_cardinality"), - Element(value=name.value + ".lower_cardinality"), - lower_cardinality - ) - self.instantiate_link( - Element(value="Class_lower_cardinality_link"), - Element(value=name.value + ".lower_cardinality_link"), - name, - Element(value=name.value + ".lower_cardinality") - ) - if upper_cardinality.value is not None: - self.instantiate_value( - Element(value="Class_upper_cardinality"), - Element(value=name.value + ".upper_cardinality"), - upper_cardinality - ) - self.instantiate_link( - Element(value="Class_upper_cardinality_link"), - Element(value=name.value + ".upper_cardinality_link"), - name, - Element(value=name.value + ".upper_cardinality") - ) - - def create_class_attribute(self, class_name: String, name: String, optional: Boolean = Element(value=False)): - # create attribute - element_name = Element(value=f"{class_name.value}_{name.value}") - element_link_name = Element(value=f"{class_name.value}_{name.value}_link") - self.instantiate(Element(value="Attribute"), element_name) - self.instantiate_link(Element(value="AttributeLink"), element_link_name, class_name, element_name) - # set attribute's attributes - attr_name_name = Element(value=f"{class_name.value}_{name.value}.name") - attr_optional_name = Element(value=f"{class_name.value}_{name.value}.optional") - attr_name_link_name = Element(value=f"{class_name.value}_{name.value}.name_link") - attr_optional_link_name = Element(value=f"{class_name.value}_{name.value}.optional_link") - self.instantiate_value(Element(value="Attribute_name"), attr_name_name, name) - self.instantiate_value(Element(value="Attribute_optional"), attr_optional_name, optional) - self.instantiate_link(Element(value="Attribute_name_link"), attr_name_link_name, element_name, attr_name_name) - self.instantiate_link(Element(value="Attribute_optional_link"), attr_optional_link_name, element_name, attr_optional_name) - - def create_association(self, source_class_name: String, target_class_name: String, name: String, - source_lower_cardinality: Integer = Element(), target_lower_cardinality: Integer = Element(), - source_upper_cardinality: Integer = Element(), target_upper_cardinality: Integer = Element() - ): - self.instantiate_link(Element(value="Association"), name, source_class_name, target_class_name) - if source_lower_cardinality.value is not None: - self.instantiate_value( - Element(value="Association_source_lower_cardinality"), - Element(value=name.value + ".source_lower_cardinality"), - source_lower_cardinality - ) - self.instantiate_link( - Element(value="Association_source_lower_cardinality_link"), - Element(value=name.value + ".source_lower_cardinality_link"), - name, - Element(value=name.value + ".source_lower_cardinality") - ) - if source_upper_cardinality.value is not None: - self.instantiate_value( - Element(value="Association_source_upper_cardinality"), - Element(value=name.value + ".source_upper_cardinality"), - source_upper_cardinality - ) - self.instantiate_link( - Element(value="Association_source_upper_cardinality_link"), - Element(value=name.value + ".source_upper_cardinality_link"), - name, - Element(value=name.value + ".source_upper_cardinality") - ) - if target_lower_cardinality.value is not None: - self.instantiate_value( - Element(value="Association_target_lower_cardinality"), - Element(value=name.value + ".target_lower_cardinality"), - target_lower_cardinality - ) - self.instantiate_link( - Element(value="Association_target_lower_cardinality_link"), - Element(value=name.value + ".target_lower_cardinality_link"), - name, - Element(value=name.value + ".target_lower_cardinality") - ) - if target_upper_cardinality.value is not None: - self.instantiate_value( - Element(value="Association_target_upper_cardinality"), - Element(value=name.value + ".target_upper_cardinality"), - target_upper_cardinality - ) - self.instantiate_link( - Element(value="Association_target_upper_cardinality_link"), - Element(value=name.value + ".target_upper_cardinality_link"), - name, - Element(value=name.value + ".target_upper_cardinality") - ) - - def create_inheritance(self, parent_class_name: String, child_class_name: String): - self.instantiate_link( - Element(value="Inheritance"), - Element(value=f"{child_class_name.value}_inherits_from_{parent_class_name.value}"), - child_class_name, - parent_class_name) diff --git a/core/element.py b/core/element.py deleted file mode 100644 index 42284a2..0000000 --- a/core/element.py +++ /dev/null @@ -1,34 +0,0 @@ -from dataclasses import dataclass -from typing import TypeVar, Generic, Optional, Literal -from state.base import INTEGER, FLOAT, BOOLEAN, STRING, TYPE - -# Some typing information for Python static type checkers -bottom_type = Literal[INTEGER, FLOAT, BOOLEAN, STRING, TYPE] -T = TypeVar('T', int, float, bool, str, bottom_type) - - -@dataclass -class Element(Generic[T]): - """ - An Element can represent one of following two things, based on the value of its attributes: - - * An element (node or edge) in the State (id is not None). In this case the value can be None because it hasn't - yet been read OR because the element doesn't have a value. - * A value for which a node has not yet been materialized in the State (id is None, value is not None). - - - If you are familiar with the Modelverse Kernel, this class serves a function similar to the {id: ..., value: ...} - dict that is used there. - """ - id: Optional[str] = None - value: Optional[T] = None - - def is_none(self) -> bool: - return self.id is None and self.value is None - - -String = Element[str] -Integer = Element[int] -Float = Element[float] -Boolean = Element[bool] -Type = Element[bottom_type] diff --git a/core/manager.py b/core/manager.py deleted file mode 100644 index 2df048c..0000000 --- a/core/manager.py +++ /dev/null @@ -1,88 +0,0 @@ -from state.base import State -from core.element import Element, String - - -class Manager: - def __init__(self, state: State): - self.state = state - - def exposed_methods(self): - yield from [ - self.new_model, - self.get_model, - self.rename_model, - self.retype_model, - self.delete_model, - self.list_models, - ] - - def new_model(self, name: String, type_model_name: String) -> Element: - root = self.state.read_root() - mm_bottom = self.state.read_dict(root, type_model_name.value) - if mm_bottom is None: - print(f"Error: Invalid type model name {type_model_name.value}. Model not created.") - return Element() - bottom = self.state.create_nodevalue(name.value) - if bottom is None: - print(f"Error: Invalid model name {name.value}. Model not created.") - return Element() - self.state.create_dict(root, name.value, bottom) - self.state.create_dict(bottom, "Model", self.state.create_node()) - self.state.create_dict(bottom, "Metamodel", mm_bottom) - return Element(id=bottom) - - def get_model(self, name: String) -> Element: - root = self.state.read_root() - model = self.state.read_dict(root, name.value) - if model is None: - print(f"Error: Cannot find model with name {name.value}.") - return Element() - return Element(id=model) - - def rename_model(self, name: String, new_name: String): - root = self.state.read_root() - name_used = self.state.read_dict_edge(root, new_name.value) - if name_used: - print(f"Error: Model with name {new_name.value} already exists. Please use another name.") - return - mode_edge = self.state.read_dict_edge(root, name.value) - if mode_edge is None: - print(f"Error: Cannot find model with name {name.value}.") - return - _, model_node = self.state.read_edge(mode_edge) - self.state.create_dict(root, new_name.value, model_node) - created = self.state.read_dict_edge(root, new_name.value) - if created is None: - print(f"Error: Invalid model name {new_name.value}. Model not renamed.") - else: - self.state.delete_edge(mode_edge) - - def delete_model(self, name: String): - root = self.state.read_root() - model = self.state.read_dict(root, name.value) - if model is None: - print(f"Error: No model found for name {name.value}.") - else: - self.state.delete_node(model) - - def list_models(self): - unsorted = [] - for edge in self.state.read_outgoing(self.state.read_root()): - _, model = self.state.read_edge(edge) - metamodel = self.state.read_dict(model, "Metamodel") - unsorted.append(f"{self.state.read_value(model)} : {self.state.read_value(metamodel)}") - for x in sorted(unsorted): - print(x) - - def retype_model(self, model_name: String, metamodel_name: String): - root = self.state.read_root() - model = self.state.read_dict(root, model_name.value) - if model is None: - print(f"Error: Cannot find model with name {model_name.value}.") - metamodel = self.state.read_dict(root, metamodel_name.value) - if metamodel is None: - print(f"Error: Cannot find model with name {metamodel_name.value}.") - metamodel_edge = self.state.read_dict_edge(model, "Metamodel") - if metamodel_edge is not None: - self.state.delete_edge(metamodel_edge) - self.state.create_dict(model, "Metamodel", metamodel) diff --git a/main.py b/main.py deleted file mode 100644 index 32cce95..0000000 --- a/main.py +++ /dev/null @@ -1,124 +0,0 @@ -from core.element import Element -from core.manager import Manager -from core.context.bottom import BottomContext -from core.context.generic import GenericContext -from core.context import custom_contexts -from state.devstate import DevState -from bootstrap.simple_class_diagrams import bootstrap_scd - -import inspect -from ast import literal_eval -import sys -import signal -signal.signal(signal.SIGINT, signal.default_int_handler) - - -def params(method): - annotations = { - "core.element.Element[str]": "String", - "core.element.Element[int]": "Integer", - "core.element.Element[float]": "Float", - "core.element.Element[bool]": "Boolean" - } - sign = inspect.signature(method) - for param in sign.parameters.values(): - param_type = annotations.get(str(param.annotation), "Element") - if param.default is param.empty: - yield param.name, param_type - else: - yield param.name, param_type, param.default - - -def input_args(method): - args = [] - for param_tuple in params(method): - if len(param_tuple) == 3: - param_name, param_type, param_default = param_tuple - arg = input(f"{param_type} {param_name} (default = {param_default.value})? ") - if arg == '': - arg = param_default.value - arg = Element(value=arg) - args.append(arg) - continue - else: - param_name, param_type = param_tuple - while True: - arg = input(f"{param_type} {param_name}? ") - if arg == '': - print("This arguments is non-optional, please provide a value.") - else: - break - if arg is None: - arg = Element() - elif param_type in {"Boolean", "Integer", "Float"}: - arg = Element(value=literal_eval(arg)) - else: - arg = Element(value=arg) - args.append(arg) - return args - - -def model_management_loop(manager: Manager): - try: - print("\nCurrently no model is loaded. Following model management operations are available:") - for m in manager.exposed_methods(): - print(f"\t* {m.__name__}") - while True: - command = input("\nPlease enter a command: ") - try: - method = next(m for m in manager.exposed_methods() if m.__name__ == command) - user_args = input_args(method) - model = method(*user_args) - if model is not None and not model.is_none(): - metamodel = Element(id=manager.state.read_dict(model.id, "Metamodel")) - modelling_loop(manager, model, metamodel) - break - except StopIteration: - print("Invalid command, please try again.") - except KeyboardInterrupt: - print("\nBye!") - sys.exit() - - -def modelling_loop(manager: Manager, model: Element, metamodel: Element): - model_name = manager.state.read_value(model.id) - metamodel_name = manager.state.read_value(metamodel.id) - if metamodel.is_none(): - context = BottomContext(manager.state, model) - print(f"No metamodel found. Model {model_name} has been loaded in context Bottom.") - else: - context = custom_contexts.get(metamodel_name, GenericContext)(manager.state, model, metamodel) - print(type(context)) - print(f"Model {model_name} has been loaded in context {metamodel_name}.") - print(f"To return to the model management interface press CTRL + C.") - print(f"The following operations are available in this context:") - for m in context.exposed_methods(): - print(f"\t* {m.__name__}") - try: - while True: - command = input("\nPlease enter a command: ") - try: - method = next(m for m in context.exposed_methods() if m.__name__ == command) - user_args = input_args(method) - model = method(*user_args) - if model is not None and not model.is_none(): - metamodel = Element(id=manager.state.read_dict(model.id, "Metamodel")) - modelling_loop(manager, model, metamodel) - break - except StopIteration: - print("Invalid command, please try again.") - except KeyboardInterrupt: - pass - - -if __name__ == '__main__': - state = DevState() - bootstrap_scd(state) - - man = Manager(state) - print("Welcome to MV2!") - while True: - model_management_loop(man) - - - diff --git a/requirements_dev.txt b/requirements_dev.txt deleted file mode 100644 index dcf816b..0000000 --- a/requirements_dev.txt +++ /dev/null @@ -1,2 +0,0 @@ -autopep8==1.5.7 -mypy==0.910 \ No newline at end of file From f44c888acee6fd306c1a8c76c57872f88c0130be Mon Sep 17 00:00:00 2001 From: Andrei Bondarenko Date: Sat, 24 Jul 2021 20:59:38 +0200 Subject: [PATCH 37/46] Reorganization --- {state => core}/__init__.py | 0 state/test/__init__.py => core/manager.py | 0 {state/test/fixtures => services}/__init__.py | 0 services/bottom/__init__.py | 0 {state => services/bottom}/base.py | 0 {state => services/bottom}/devstate.py | 2 +- {state => services/bottom}/pystate.py | 2 +- services/bottom/test/__init__.py | 0 {state => services/bottom}/test/conftest.py | 0 services/bottom/test/fixtures/__init__.py | 0 {state => services/bottom}/test/fixtures/state.py | 2 +- {state => services/bottom}/test/test_create_dict.py | 0 {state => services/bottom}/test/test_create_edge.py | 0 {state => services/bottom}/test/test_create_node.py | 0 {state => services/bottom}/test/test_create_nodevalue.py | 0 {state => services/bottom}/test/test_delete_edge.py | 0 {state => services/bottom}/test/test_delete_node.py | 0 {state => services/bottom}/test/test_read_dict.py | 0 {state => services/bottom}/test/test_read_dict_edge.py | 0 {state => services/bottom}/test/test_read_dict_keys.py | 0 {state => services/bottom}/test/test_read_dict_node.py | 0 {state => services/bottom}/test/test_read_edge.py | 0 {state => services/bottom}/test/test_read_incoming.py | 0 {state => services/bottom}/test/test_read_outgoing.py | 0 {state => services/bottom}/test/test_read_reverse_dict.py | 0 {state => services/bottom}/test/test_read_value.py | 0 26 files changed, 3 insertions(+), 3 deletions(-) rename {state => core}/__init__.py (100%) rename state/test/__init__.py => core/manager.py (100%) rename {state/test/fixtures => services}/__init__.py (100%) create mode 100644 services/bottom/__init__.py rename {state => services/bottom}/base.py (100%) rename {state => services/bottom}/devstate.py (97%) rename {state => services/bottom}/pystate.py (99%) create mode 100644 services/bottom/test/__init__.py rename {state => services/bottom}/test/conftest.py (100%) create mode 100644 services/bottom/test/fixtures/__init__.py rename {state => services/bottom}/test/fixtures/state.py (90%) rename {state => services/bottom}/test/test_create_dict.py (100%) rename {state => services/bottom}/test/test_create_edge.py (100%) rename {state => services/bottom}/test/test_create_node.py (100%) rename {state => services/bottom}/test/test_create_nodevalue.py (100%) rename {state => services/bottom}/test/test_delete_edge.py (100%) rename {state => services/bottom}/test/test_delete_node.py (100%) rename {state => services/bottom}/test/test_read_dict.py (100%) rename {state => services/bottom}/test/test_read_dict_edge.py (100%) rename {state => services/bottom}/test/test_read_dict_keys.py (100%) rename {state => services/bottom}/test/test_read_dict_node.py (100%) rename {state => services/bottom}/test/test_read_edge.py (100%) rename {state => services/bottom}/test/test_read_incoming.py (100%) rename {state => services/bottom}/test/test_read_outgoing.py (100%) rename {state => services/bottom}/test/test_read_reverse_dict.py (100%) rename {state => services/bottom}/test/test_read_value.py (100%) diff --git a/state/__init__.py b/core/__init__.py similarity index 100% rename from state/__init__.py rename to core/__init__.py diff --git a/state/test/__init__.py b/core/manager.py similarity index 100% rename from state/test/__init__.py rename to core/manager.py diff --git a/state/test/fixtures/__init__.py b/services/__init__.py similarity index 100% rename from state/test/fixtures/__init__.py rename to services/__init__.py diff --git a/services/bottom/__init__.py b/services/bottom/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/state/base.py b/services/bottom/base.py similarity index 100% rename from state/base.py rename to services/bottom/base.py diff --git a/state/devstate.py b/services/bottom/devstate.py similarity index 97% rename from state/devstate.py rename to services/bottom/devstate.py index 798d8d9..65d4357 100644 --- a/state/devstate.py +++ b/services/bottom/devstate.py @@ -1,4 +1,4 @@ -from state.pystate import PyState +from services.bottom.pystate import PyState class DevState(PyState): diff --git a/state/pystate.py b/services/bottom/pystate.py similarity index 99% rename from state/pystate.py rename to services/bottom/pystate.py index b88a358..8f306ec 100644 --- a/state/pystate.py +++ b/services/bottom/pystate.py @@ -1,6 +1,6 @@ from typing import Any, List, Tuple, Optional -from state.base import State, Node, Edge, Element +from services.bottom.base import State, Node, Edge, Element class PyState(State): diff --git a/services/bottom/test/__init__.py b/services/bottom/test/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/state/test/conftest.py b/services/bottom/test/conftest.py similarity index 100% rename from state/test/conftest.py rename to services/bottom/test/conftest.py diff --git a/services/bottom/test/fixtures/__init__.py b/services/bottom/test/fixtures/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/state/test/fixtures/state.py b/services/bottom/test/fixtures/state.py similarity index 90% rename from state/test/fixtures/state.py rename to services/bottom/test/fixtures/state.py index ab7eb87..6dcb16e 100644 --- a/state/test/fixtures/state.py +++ b/services/bottom/test/fixtures/state.py @@ -1,5 +1,5 @@ import pytest -from state.pystate import PyState +from services.bottom.pystate import PyState # from state.rdfstate import RDFState # from state.neo4jstate import Neo4jState diff --git a/state/test/test_create_dict.py b/services/bottom/test/test_create_dict.py similarity index 100% rename from state/test/test_create_dict.py rename to services/bottom/test/test_create_dict.py diff --git a/state/test/test_create_edge.py b/services/bottom/test/test_create_edge.py similarity index 100% rename from state/test/test_create_edge.py rename to services/bottom/test/test_create_edge.py diff --git a/state/test/test_create_node.py b/services/bottom/test/test_create_node.py similarity index 100% rename from state/test/test_create_node.py rename to services/bottom/test/test_create_node.py diff --git a/state/test/test_create_nodevalue.py b/services/bottom/test/test_create_nodevalue.py similarity index 100% rename from state/test/test_create_nodevalue.py rename to services/bottom/test/test_create_nodevalue.py diff --git a/state/test/test_delete_edge.py b/services/bottom/test/test_delete_edge.py similarity index 100% rename from state/test/test_delete_edge.py rename to services/bottom/test/test_delete_edge.py diff --git a/state/test/test_delete_node.py b/services/bottom/test/test_delete_node.py similarity index 100% rename from state/test/test_delete_node.py rename to services/bottom/test/test_delete_node.py diff --git a/state/test/test_read_dict.py b/services/bottom/test/test_read_dict.py similarity index 100% rename from state/test/test_read_dict.py rename to services/bottom/test/test_read_dict.py diff --git a/state/test/test_read_dict_edge.py b/services/bottom/test/test_read_dict_edge.py similarity index 100% rename from state/test/test_read_dict_edge.py rename to services/bottom/test/test_read_dict_edge.py diff --git a/state/test/test_read_dict_keys.py b/services/bottom/test/test_read_dict_keys.py similarity index 100% rename from state/test/test_read_dict_keys.py rename to services/bottom/test/test_read_dict_keys.py diff --git a/state/test/test_read_dict_node.py b/services/bottom/test/test_read_dict_node.py similarity index 100% rename from state/test/test_read_dict_node.py rename to services/bottom/test/test_read_dict_node.py diff --git a/state/test/test_read_edge.py b/services/bottom/test/test_read_edge.py similarity index 100% rename from state/test/test_read_edge.py rename to services/bottom/test/test_read_edge.py diff --git a/state/test/test_read_incoming.py b/services/bottom/test/test_read_incoming.py similarity index 100% rename from state/test/test_read_incoming.py rename to services/bottom/test/test_read_incoming.py diff --git a/state/test/test_read_outgoing.py b/services/bottom/test/test_read_outgoing.py similarity index 100% rename from state/test/test_read_outgoing.py rename to services/bottom/test/test_read_outgoing.py diff --git a/state/test/test_read_reverse_dict.py b/services/bottom/test/test_read_reverse_dict.py similarity index 100% rename from state/test/test_read_reverse_dict.py rename to services/bottom/test/test_read_reverse_dict.py diff --git a/state/test/test_read_value.py b/services/bottom/test/test_read_value.py similarity index 100% rename from state/test/test_read_value.py rename to services/bottom/test/test_read_value.py From 1931020bb1b5d1c1dc100db0c0daccd395ef0792 Mon Sep 17 00:00:00 2001 From: Andrei Bondarenko Date: Sat, 24 Jul 2021 21:50:12 +0200 Subject: [PATCH 38/46] Revert "Reorganization" This reverts commit f44c888a --- services/bottom/test/__init__.py | 0 services/bottom/test/fixtures/__init__.py | 0 {core => state}/__init__.py | 0 {services/bottom => state}/base.py | 0 {services/bottom => state}/devstate.py | 2 +- {services/bottom => state}/pystate.py | 2 +- {services => state/test}/__init__.py | 0 {services/bottom => state}/test/conftest.py | 0 {services/bottom => state/test/fixtures}/__init__.py | 0 {services/bottom => state}/test/fixtures/state.py | 2 +- {services/bottom => state}/test/test_create_dict.py | 0 {services/bottom => state}/test/test_create_edge.py | 0 {services/bottom => state}/test/test_create_node.py | 0 {services/bottom => state}/test/test_create_nodevalue.py | 0 {services/bottom => state}/test/test_delete_edge.py | 0 {services/bottom => state}/test/test_delete_node.py | 0 {services/bottom => state}/test/test_read_dict.py | 0 {services/bottom => state}/test/test_read_dict_edge.py | 0 {services/bottom => state}/test/test_read_dict_keys.py | 0 {services/bottom => state}/test/test_read_dict_node.py | 0 {services/bottom => state}/test/test_read_edge.py | 0 {services/bottom => state}/test/test_read_incoming.py | 0 {services/bottom => state}/test/test_read_outgoing.py | 0 {services/bottom => state}/test/test_read_reverse_dict.py | 0 {services/bottom => state}/test/test_read_value.py | 0 25 files changed, 3 insertions(+), 3 deletions(-) delete mode 100644 services/bottom/test/__init__.py delete mode 100644 services/bottom/test/fixtures/__init__.py rename {core => state}/__init__.py (100%) rename {services/bottom => state}/base.py (100%) rename {services/bottom => state}/devstate.py (97%) rename {services/bottom => state}/pystate.py (99%) rename {services => state/test}/__init__.py (100%) rename {services/bottom => state}/test/conftest.py (100%) rename {services/bottom => state/test/fixtures}/__init__.py (100%) rename {services/bottom => state}/test/fixtures/state.py (90%) rename {services/bottom => state}/test/test_create_dict.py (100%) rename {services/bottom => state}/test/test_create_edge.py (100%) rename {services/bottom => state}/test/test_create_node.py (100%) rename {services/bottom => state}/test/test_create_nodevalue.py (100%) rename {services/bottom => state}/test/test_delete_edge.py (100%) rename {services/bottom => state}/test/test_delete_node.py (100%) rename {services/bottom => state}/test/test_read_dict.py (100%) rename {services/bottom => state}/test/test_read_dict_edge.py (100%) rename {services/bottom => state}/test/test_read_dict_keys.py (100%) rename {services/bottom => state}/test/test_read_dict_node.py (100%) rename {services/bottom => state}/test/test_read_edge.py (100%) rename {services/bottom => state}/test/test_read_incoming.py (100%) rename {services/bottom => state}/test/test_read_outgoing.py (100%) rename {services/bottom => state}/test/test_read_reverse_dict.py (100%) rename {services/bottom => state}/test/test_read_value.py (100%) diff --git a/services/bottom/test/__init__.py b/services/bottom/test/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/services/bottom/test/fixtures/__init__.py b/services/bottom/test/fixtures/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/core/__init__.py b/state/__init__.py similarity index 100% rename from core/__init__.py rename to state/__init__.py diff --git a/services/bottom/base.py b/state/base.py similarity index 100% rename from services/bottom/base.py rename to state/base.py diff --git a/services/bottom/devstate.py b/state/devstate.py similarity index 97% rename from services/bottom/devstate.py rename to state/devstate.py index 65d4357..798d8d9 100644 --- a/services/bottom/devstate.py +++ b/state/devstate.py @@ -1,4 +1,4 @@ -from services.bottom.pystate import PyState +from state.pystate import PyState class DevState(PyState): diff --git a/services/bottom/pystate.py b/state/pystate.py similarity index 99% rename from services/bottom/pystate.py rename to state/pystate.py index 8f306ec..b88a358 100644 --- a/services/bottom/pystate.py +++ b/state/pystate.py @@ -1,6 +1,6 @@ from typing import Any, List, Tuple, Optional -from services.bottom.base import State, Node, Edge, Element +from state.base import State, Node, Edge, Element class PyState(State): diff --git a/services/__init__.py b/state/test/__init__.py similarity index 100% rename from services/__init__.py rename to state/test/__init__.py diff --git a/services/bottom/test/conftest.py b/state/test/conftest.py similarity index 100% rename from services/bottom/test/conftest.py rename to state/test/conftest.py diff --git a/services/bottom/__init__.py b/state/test/fixtures/__init__.py similarity index 100% rename from services/bottom/__init__.py rename to state/test/fixtures/__init__.py diff --git a/services/bottom/test/fixtures/state.py b/state/test/fixtures/state.py similarity index 90% rename from services/bottom/test/fixtures/state.py rename to state/test/fixtures/state.py index 6dcb16e..ab7eb87 100644 --- a/services/bottom/test/fixtures/state.py +++ b/state/test/fixtures/state.py @@ -1,5 +1,5 @@ import pytest -from services.bottom.pystate import PyState +from state.pystate import PyState # from state.rdfstate import RDFState # from state.neo4jstate import Neo4jState diff --git a/services/bottom/test/test_create_dict.py b/state/test/test_create_dict.py similarity index 100% rename from services/bottom/test/test_create_dict.py rename to state/test/test_create_dict.py diff --git a/services/bottom/test/test_create_edge.py b/state/test/test_create_edge.py similarity index 100% rename from services/bottom/test/test_create_edge.py rename to state/test/test_create_edge.py diff --git a/services/bottom/test/test_create_node.py b/state/test/test_create_node.py similarity index 100% rename from services/bottom/test/test_create_node.py rename to state/test/test_create_node.py diff --git a/services/bottom/test/test_create_nodevalue.py b/state/test/test_create_nodevalue.py similarity index 100% rename from services/bottom/test/test_create_nodevalue.py rename to state/test/test_create_nodevalue.py diff --git a/services/bottom/test/test_delete_edge.py b/state/test/test_delete_edge.py similarity index 100% rename from services/bottom/test/test_delete_edge.py rename to state/test/test_delete_edge.py diff --git a/services/bottom/test/test_delete_node.py b/state/test/test_delete_node.py similarity index 100% rename from services/bottom/test/test_delete_node.py rename to state/test/test_delete_node.py diff --git a/services/bottom/test/test_read_dict.py b/state/test/test_read_dict.py similarity index 100% rename from services/bottom/test/test_read_dict.py rename to state/test/test_read_dict.py diff --git a/services/bottom/test/test_read_dict_edge.py b/state/test/test_read_dict_edge.py similarity index 100% rename from services/bottom/test/test_read_dict_edge.py rename to state/test/test_read_dict_edge.py diff --git a/services/bottom/test/test_read_dict_keys.py b/state/test/test_read_dict_keys.py similarity index 100% rename from services/bottom/test/test_read_dict_keys.py rename to state/test/test_read_dict_keys.py diff --git a/services/bottom/test/test_read_dict_node.py b/state/test/test_read_dict_node.py similarity index 100% rename from services/bottom/test/test_read_dict_node.py rename to state/test/test_read_dict_node.py diff --git a/services/bottom/test/test_read_edge.py b/state/test/test_read_edge.py similarity index 100% rename from services/bottom/test/test_read_edge.py rename to state/test/test_read_edge.py diff --git a/services/bottom/test/test_read_incoming.py b/state/test/test_read_incoming.py similarity index 100% rename from services/bottom/test/test_read_incoming.py rename to state/test/test_read_incoming.py diff --git a/services/bottom/test/test_read_outgoing.py b/state/test/test_read_outgoing.py similarity index 100% rename from services/bottom/test/test_read_outgoing.py rename to state/test/test_read_outgoing.py diff --git a/services/bottom/test/test_read_reverse_dict.py b/state/test/test_read_reverse_dict.py similarity index 100% rename from services/bottom/test/test_read_reverse_dict.py rename to state/test/test_read_reverse_dict.py diff --git a/services/bottom/test/test_read_value.py b/state/test/test_read_value.py similarity index 100% rename from services/bottom/test/test_read_value.py rename to state/test/test_read_value.py From 597ee31c54ef519a09b117b59f1a76051be13f71 Mon Sep 17 00:00:00 2001 From: Andrei Bondarenko Date: Sat, 24 Jul 2021 21:59:48 +0200 Subject: [PATCH 39/46] state now properly uses and it typed with UUID --- core/manager.py | 0 state/base.py | 10 +++++----- state/devstate.py | 13 +++++++------ 3 files changed, 12 insertions(+), 11 deletions(-) delete mode 100644 core/manager.py diff --git a/core/manager.py b/core/manager.py deleted file mode 100644 index e69de29..0000000 diff --git a/state/base.py b/state/base.py index 242c4b7..06e792f 100644 --- a/state/base.py +++ b/state/base.py @@ -1,6 +1,6 @@ from abc import ABC, abstractmethod from typing import Any, List, Tuple, Optional, Union -import uuid +from uuid import UUID, uuid4 primitive_types = (int, float, str, bool) INTEGER = ("Integer",) @@ -11,8 +11,8 @@ TYPE = ("Type",) type_values = (INTEGER, FLOAT, STRING, BOOLEAN, TYPE) -Node = str -Edge = str +Node = UUID +Edge = UUID Element = Union[Node, Edge] @@ -25,11 +25,11 @@ class State(ABC): """ @staticmethod - def new_id() -> str: + def new_id() -> UUID: """ Generates a new UUID """ - return str(uuid.uuid4()) + return uuid4() @staticmethod def is_valid_datavalue(value: Any) -> bool: diff --git a/state/devstate.py b/state/devstate.py index 798d8d9..6c7f853 100644 --- a/state/devstate.py +++ b/state/devstate.py @@ -1,4 +1,5 @@ from state.pystate import PyState +from uuid import UUID class DevState(PyState): @@ -13,9 +14,9 @@ class DevState(PyState): super().__init__() @staticmethod - def new_id() -> str: + def new_id() -> UUID: DevState.free_id += 1 - return str(DevState.free_id - 1) + return UUID(int=DevState.free_id - 1) def dump(self, path: str, png_path: str = None): """Dumps the whole MV graph to a graphviz .dot-file @@ -34,13 +35,13 @@ class DevState(PyState): else: x = repr(x) f.write("\"a_%s\" [label=\"%s\"];\n" % ( - n, x.replace('"', '\\"'))) + n.int, x.replace('"', '\\"'))) else: f.write("\"a_%s\" [label=\"\"];\n" % n) for i, e in sorted(list(self.edges.items())): - f.write("\"a_%s\" [label=\"e_%s\" shape=point];\n" % (i, i)) - f.write("\"a_%s\" -> \"a_%s\" [arrowhead=none];\n" % (e[0], i)) - f.write("\"a_%s\" -> \"a_%s\";\n" % (i, e[1])) + f.write("\"a_%s\" [label=\"e_%s\" shape=point];\n" % (i.int, i.int)) + f.write("\"a_%s\" -> \"a_%s\" [arrowhead=none];\n" % (e[0].int, i.int)) + f.write("\"a_%s\" -> \"a_%s\";\n" % (i.int, e[1].int)) f.write("}") if png_path is not None: From cf2379c5ec48ec01cc497431e1fb2f4a78e4ffbc Mon Sep 17 00:00:00 2001 From: Andrei Bondarenko Date: Sat, 24 Jul 2021 22:46:59 +0200 Subject: [PATCH 40/46] Working on manager --- core/__init__.py | 0 core/manager.py | 32 ++++++++++++++++++++++++++++++++ services/__init__.py | 0 3 files changed, 32 insertions(+) create mode 100644 core/__init__.py create mode 100644 core/manager.py create mode 100644 services/__init__.py diff --git a/core/__init__.py b/core/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/core/manager.py b/core/manager.py new file mode 100644 index 0000000..ba90ea1 --- /dev/null +++ b/core/manager.py @@ -0,0 +1,32 @@ +from state.base import State +from uuid import UUID + + +class Manager: + def __init__(self, state: State): + self._state = state + bottom = self._state.create_node() + bottom_id = self._state.create_nodevalue(str(bottom)) + bottom_label = self._state.create_nodevalue("Bottom") + to_uuid = self._state.create_edge(self._state.read_root(), bottom_id) + to_label = self._state.create_edge(self._state.read_root(), bottom_label) + self._state.create_edge(to_label, bottom_id) + self._state.create_edge(to_uuid, bottom_label) + + @property + def models(self): + root = self._state.read_root() + for key in self._state.read_dict_keys(root): + try: + UUID(key) # checks whether the key is a valid UUID + name_node = self._state.read_dict(root, key) + name = self._state.read_value(name_node) + yield key, name + except ValueError: + pass + + +if __name__ == '__main__': + from state.devstate import DevState + m = Manager(DevState()) + print(list(m.models)) diff --git a/services/__init__.py b/services/__init__.py new file mode 100644 index 0000000..e69de29 From 93ff82c2bb282d3a63178bd02c6aa68c2f1e9d4e Mon Sep 17 00:00:00 2001 From: Andrei Bondarenko Date: Sun, 25 Jul 2021 10:33:21 +0200 Subject: [PATCH 41/46] Corrected interface --- state/base.py | 2 +- state/pystate.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/state/base.py b/state/base.py index 06e792f..614ae9e 100644 --- a/state/base.py +++ b/state/base.py @@ -191,7 +191,7 @@ class State(ABC): pass @abstractmethod - def read_dict_keys(self, elem: Element) -> Optional[List[Any]]: + def read_dict_keys(self, elem: Element) -> Optional[List[Element]]: """ Reads labels of outgoing edges starting in given node. diff --git a/state/pystate.py b/state/pystate.py index b88a358..0cc7f7a 100644 --- a/state/pystate.py +++ b/state/pystate.py @@ -112,7 +112,7 @@ class PyState(State): else: return self.edges[e][1] - def read_dict_keys(self, elem: Element) -> Optional[List[Any]]: + def read_dict_keys(self, elem: Element) -> Optional[List[Element]]: if elem not in self.nodes and elem not in self.edges: return None From bc57d9f5c15130b2b1cde0d1d4fdc92323bb2c9c Mon Sep 17 00:00:00 2001 From: Andrei Bondarenko Date: Wed, 11 Aug 2021 00:31:14 +0200 Subject: [PATCH 42/46] Some cleanup --- core/__init__.py | 0 core/manager.py | 32 -------------------------------- services/__init__.py | 0 3 files changed, 32 deletions(-) delete mode 100644 core/__init__.py delete mode 100644 core/manager.py delete mode 100644 services/__init__.py diff --git a/core/__init__.py b/core/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/core/manager.py b/core/manager.py deleted file mode 100644 index ba90ea1..0000000 --- a/core/manager.py +++ /dev/null @@ -1,32 +0,0 @@ -from state.base import State -from uuid import UUID - - -class Manager: - def __init__(self, state: State): - self._state = state - bottom = self._state.create_node() - bottom_id = self._state.create_nodevalue(str(bottom)) - bottom_label = self._state.create_nodevalue("Bottom") - to_uuid = self._state.create_edge(self._state.read_root(), bottom_id) - to_label = self._state.create_edge(self._state.read_root(), bottom_label) - self._state.create_edge(to_label, bottom_id) - self._state.create_edge(to_uuid, bottom_label) - - @property - def models(self): - root = self._state.read_root() - for key in self._state.read_dict_keys(root): - try: - UUID(key) # checks whether the key is a valid UUID - name_node = self._state.read_dict(root, key) - name = self._state.read_value(name_node) - yield key, name - except ValueError: - pass - - -if __name__ == '__main__': - from state.devstate import DevState - m = Manager(DevState()) - print(list(m.models)) diff --git a/services/__init__.py b/services/__init__.py deleted file mode 100644 index e69de29..0000000 From 633aa84f39a255050832056822eaf942355bf3f8 Mon Sep 17 00:00:00 2001 From: Andrei Bondarenko Date: Wed, 11 Aug 2021 11:46:41 +0200 Subject: [PATCH 43/46] Added back RDF and Neo4j implementations --- requirements.txt | 4 +- state/neo4jstate.py | 301 +++++++++++++++++++++++++++++++++++ state/rdfstate.py | 276 ++++++++++++++++++++++++++++++++ state/test/fixtures/state.py | 12 +- 4 files changed, 586 insertions(+), 7 deletions(-) create mode 100644 state/neo4jstate.py create mode 100644 state/rdfstate.py diff --git a/requirements.txt b/requirements.txt index 11b890f..b49798e 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1 +1,3 @@ -pytest==6.2.4 \ No newline at end of file +pytest==6.2.4 +neo4j==4.3.4 +rdflib==6.0.0 diff --git a/state/neo4jstate.py b/state/neo4jstate.py new file mode 100644 index 0000000..14704fa --- /dev/null +++ b/state/neo4jstate.py @@ -0,0 +1,301 @@ +from typing import Any, Optional, List, Tuple, Callable, Generator +from neo4j import GraphDatabase +from ast import literal_eval + +from .base import State, Edge, Node, Element, UUID + + + +class Neo4jState(State): + def __init__(self, uri="bolt://localhost:7687", user="neo4j", password="tests"): + self.driver = GraphDatabase.driver(uri, auth=(user, password)) + self.root = self.create_node() + + def close(self, *, clear=False): + if clear: + self._run_and_return(self._clear) + self.driver.close() + + def _run_and_return(self, query: Callable, **kwargs): + with self.driver.session() as session: + result = session.write_transaction(query, **kwargs) + return result + + @staticmethod + def _clear(tx): + tx.run("MATCH (n) " + "DETACH DELETE n") + + @staticmethod + def _existence_check(tx, eid, label="Element"): + result = tx.run(f"MATCH (elem:{label}) " + "WHERE elem.id = $eid " + "RETURN elem.id", + eid=eid) + try: + return result.single()[0] + except TypeError: + # No node found for nid + # ergo, no edge created + return None + + def create_node(self) -> Node: + def query(tx, nid): + result = tx.run("CREATE (n:Element:Node) " + "SET n.id = $nid " + "RETURN n.id", + nid=nid) + return result.single()[0] + + node = self._run_and_return(query, nid=str(self.new_id())) + return UUID(node) if node is not None else None + + def create_edge(self, source: Element, target: Element) -> Optional[Edge]: + def query(tx, eid, sid, tid): + result = tx.run("MATCH (source), (target) " + "WHERE source.id = $sid AND target.id = $tid " + "CREATE (source) -[:Source]-> (e:Element:Edge) -[:Target]-> (target) " + "SET e.id = $eid " + "RETURN e.id", + eid=eid, sid=sid, tid=tid) + try: + return result.single()[0] + except TypeError: + # No node found for sid and/or tid + # ergo, no edge created + return None + + edge = self._run_and_return(query, eid=str(self.new_id()), sid=str(source), tid=str(target)) + return UUID(edge) if edge is not None else None + + def create_nodevalue(self, value: Any) -> Optional[Node]: + def query(tx, nid, val): + result = tx.run("CREATE (n:Element:Node) " + "SET n.id = $nid, n.value = $val " + "RETURN n.id", + nid=nid, val=val) + return result.single()[0] + + if not self.is_valid_datavalue(value): + return None + + node = self._run_and_return(query, nid=str(self.new_id()), val=repr(value)) + return UUID(node) if node is not None else None + + def create_dict(self, source: Element, value: Any, target: Element) -> Optional[Tuple[Edge, Edge, Node]]: + if not self.is_valid_datavalue(value): + return None + + edge_node = self.create_edge(source, target) + val_node = self.create_nodevalue(value) + if edge_node is not None and val_node is not None: + self.create_edge(edge_node, val_node) + + def read_root(self) -> Node: + return self.root + + def read_value(self, node: Node) -> Optional[Any]: + def query(tx, nid): + result = tx.run("MATCH (n:Node) " + "WHERE n.id = $nid " + "RETURN n.value", + nid=nid) + try: + return result.single()[0] + except TypeError: + # No node found for nid + return None + + value = self._run_and_return(query, nid=str(node)) + return literal_eval(value) if value is not None else None + + def read_outgoing(self, elem: Element) -> Optional[List[Edge]]: + def query(tx, eid): + result = tx.run("MATCH (elem:Element) -[:Source]-> (e:Edge) " + "WHERE elem.id = $eid " + "RETURN e.id", + eid=eid) + return result.value() + + source_exists = self._run_and_return(self._existence_check, eid=str(elem)) is not None + if source_exists: + result = self._run_and_return(query, eid=str(elem)) + return [UUID(x) for x in result] if result is not None else None + + def read_incoming(self, elem: Element) -> Optional[List[Edge]]: + def query(tx, eid): + result = tx.run("MATCH (elem:Element) <-[:Target]- (e:Edge) " + "WHERE elem.id = $eid " + "RETURN e.id", + eid=eid) + return result.value() + + target_exists = self._run_and_return(self._existence_check, eid=str(elem)) is not None + if target_exists: + result = self._run_and_return(query, eid=str(elem)) + return [UUID(x) for x in result] if result is not None else None + + def read_edge(self, edge: Edge) -> Tuple[Optional[Node], Optional[Node]]: + def query(tx, eid): + result = tx.run("MATCH (src) -[:Source]-> (e:Edge) -[:Target]-> (tgt)" + "WHERE e.id = $eid " + "RETURN src.id, tgt.id", + eid=eid) + return result.single() + + edge_exists = self._run_and_return(self._existence_check, eid=str(edge), label="Edge") is not None + if edge_exists: + try: + src, tgt = self._run_and_return(query, eid=str(edge)) + return UUID(src), UUID(tgt) + except TypeError: + return None, None + else: + return None, None + + def read_dict(self, elem: Element, value: Any) -> Optional[Element]: + def query(tx, eid, label_value): + result = tx.run("MATCH (src) -[:Source]-> (e:Edge) -[:Target]-> (tgt), " + "(e) -[:Source]-> (:Edge) -[:Target]-> (label)" + "WHERE src.id = $eid " + "AND label.value = $val " + "RETURN tgt.id", + eid=eid, val=label_value) + try: + return result.single()[0] + except TypeError: + # No edge found with given label + return None + + elem_exists = self._run_and_return(self._existence_check, eid=str(elem)) is not None + if elem_exists: + if isinstance(value, UUID): + return None + result = self._run_and_return(query, eid=str(elem), label_value=repr(value)) + return UUID(result) if result is not None else None + + def read_dict_keys(self, elem: Element) -> Optional[List[Any]]: + def query(tx, eid): + result = tx.run("MATCH (src) -[:Source]-> (e:Edge) -[:Target]-> (), " + "(e) -[:Source]-> (:Edge) -[:Target]-> (label)" + "WHERE src.id = $eid " + "RETURN label.id", + eid=eid) + try: + return result.value() + except TypeError: + # No edge found with given label + return None + + elem_exists = self._run_and_return(self._existence_check, eid=str(elem)) is not None + if elem_exists: + result = self._run_and_return(query, eid=str(elem)) + return [UUID(x) for x in result if x is not None] + + def read_dict_edge(self, elem: Element, value: Any) -> Optional[Edge]: + def query(tx, eid, label_value): + result = tx.run("MATCH (src) -[:Source]-> (e:Edge) -[:Target]-> (), " + "(e) -[:Source]-> (:Edge) -[:Target]-> (label)" + "WHERE src.id = $eid " + "AND label.value = $val " + "RETURN e.id", + eid=eid, val=label_value) + try: + return result.single()[0] + except TypeError: + # No edge found with given label + return None + + elem_exists = self._run_and_return(self._existence_check, eid=str(elem)) is not None + if elem_exists: + result = self._run_and_return(query, eid=str(elem), label_value=repr(value)) + return UUID(result) if result is not None else None + + def read_dict_node(self, elem: Element, value_node: Node) -> Optional[Element]: + def query(tx, eid, label_id): + result = tx.run("MATCH (src) -[:Source]-> (e:Edge) -[:Target]-> (tgt), " + "(e) -[:Source]-> (:Edge) -[:Target]-> (label)" + "WHERE src.id = $eid " + "AND label.id = $lid " + "RETURN tgt.id", + eid=eid, lid=label_id) + try: + return result.single()[0] + except TypeError: + # No edge found with given label + return None + + elem_exists = self._run_and_return(self._existence_check, eid=str(elem)) is not None + if elem_exists: + result = self._run_and_return(query, eid=str(elem), label_id=str(value_node)) + return UUID(result) if result is not None else None + + def read_dict_node_edge(self, elem: Element, value_node: Node) -> Optional[Edge]: + def query(tx, eid, label_id): + result = tx.run("MATCH (src) -[:Source]-> (e:Edge) -[:Target]-> (), " + "(e) -[:Source]-> (:Edge) -[:Target]-> (label)" + "WHERE src.id = $eid " + "AND label.id = $lid " + "RETURN e.id", + eid=eid, lid=label_id) + try: + return result.single()[0] + except TypeError: + # No edge found with given label + return None + + elem_exists = self._run_and_return(self._existence_check, eid=str(elem)) is not None + if elem_exists: + result = self._run_and_return(query, eid=str(elem), label_id=str(value_node)) + return UUID(result) if result is not None else None + + def read_reverse_dict(self, elem: Element, value: Any) -> Optional[List[Element]]: + def query(tx, eid, label_value): + result = tx.run("MATCH (src) -[:Source]-> (e:Edge) -[:Target]-> (tgt), " + "(e) -[:Source]-> (:Edge) -[:Target]-> (label)" + "WHERE tgt.id = $eid " + "AND label.value = $val " + "RETURN src.id", + eid=eid, val=label_value) + try: + return result.value() + except TypeError: + # No edge found with given label + return None + + elem_exists = self._run_and_return(self._existence_check, eid=str(elem)) is not None + if elem_exists: + result = self._run_and_return(query, eid=str(elem), label_value=repr(value)) + return [UUID(x) for x in result if x is not None] + + def delete_node(self, node: Node) -> None: + def query(tx, nid): + result = tx.run("MATCH (n:Node) " + "WHERE n.id = $nid " + "OPTIONAL MATCH (n) -- (e:Edge) " + "DETACH DELETE n " + "RETURN e.id", + nid=nid) + return result.value() + + to_be_deleted = self._run_and_return(query, nid=str(node)) + to_be_deleted = [UUID(x) for x in to_be_deleted if x is not None] + for edge in to_be_deleted: + self.delete_edge(edge) + + def delete_edge(self, edge: Edge) -> None: + def query(tx, eid): + result = tx.run("MATCH (e1:Edge) " + "WHERE e1.id = $eid " + "OPTIONAL MATCH (e1) -- (e2:Edge) " + "WHERE (e1) -[:Source]-> (e2) " + "OR (e1) <-[:Target]- (e2) " + "DETACH DELETE e1 " + "RETURN e2.id", + eid=eid) + return result.value() + + to_be_deleted = self._run_and_return(query, eid=str(edge)) + to_be_deleted = [UUID(x) for x in to_be_deleted if x is not None] + for edge in to_be_deleted: + self.delete_edge(edge) diff --git a/state/rdfstate.py b/state/rdfstate.py new file mode 100644 index 0000000..a6209a5 --- /dev/null +++ b/state/rdfstate.py @@ -0,0 +1,276 @@ +from typing import Any, List, Tuple, Optional, Generator +from rdflib import Graph, Namespace, URIRef, Literal +from rdflib.plugins.sparql import prepareQuery +import json + +from .base import State + +# Define graph datasctructures used by implementation +# Use NewType to create distinct type or just create a type alias +Element = URIRef +Node = URIRef +Edge = URIRef + + +class RDFState(State): + def __init__(self, namespace_uri="http://modelverse.mv/#"): + self.graph = Graph() + self.namespace_uri = namespace_uri + self.mv = Namespace(namespace_uri) + self.graph.bind("MV", self.mv) + self.prepared_queries = { + "read_value": """ + SELECT ?value + WHERE { + ?var1 MV:hasValue ?value . + } + """, + "read_outgoing": """ + SELECT ?link + WHERE { + ?link MV:hasSource ?var1 . + } + """, + "read_incoming": """ + SELECT ?link + WHERE { + ?link MV:hasTarget ?var1 . + } + """, + "read_edge": """ + SELECT ?source ?target + WHERE { + ?var1 MV:hasSource ?source ; + MV:hasTarget ?target . + } + """, + "read_dict_keys": """ + SELECT ?key + WHERE { + ?main_edge MV:hasSource ?var1 . + ?attr_edge MV:hasSource ?main_edge ; + MV:hasTarget ?key . + } + """, + "read_dict_node": """ + SELECT ?value_node + WHERE { + ?main_edge MV:hasSource ?var1 ; + MV:hasTarget ?value_node . + ?attr_edge MV:hasSource ?main_edge ; + MV:hasTarget ?var2 . + } + """, + "read_dict_node_edge": """ + SELECT ?main_edge + WHERE { + ?main_edge MV:hasSource ?var1 . + ?attr_edge MV:hasSource ?main_edge ; + MV:hasTarget ?var2 . + } + """, + "delete_node": """ + SELECT ?edge + WHERE { + { ?edge MV:hasTarget ?var1 . } + UNION + { ?edge MV:hasSource ?var1 . } + } + """, + "delete_edge": """ + SELECT ?edge + WHERE { + { ?edge MV:hasTarget ?var1 . } + UNION + { ?edge MV:hasSource ?var1 . } + } + """, + } + self.garbage = set() + + for k, v in list(self.prepared_queries.items()): + self.prepared_queries[k] = prepareQuery(self.prepared_queries[k], initNs={"MV": self.mv}) + + self.root = self.create_node() + + def create_node(self) -> Node: + return URIRef(self.namespace_uri + str(self.new_id())) + + def create_edge(self, source: Element, target: Element) -> Optional[Edge]: + if not isinstance(source, URIRef): + return None + elif not isinstance(target, URIRef): + return None + edge = URIRef(self.namespace_uri + str(self.new_id())) + self.graph.add((edge, self.mv.hasSource, source)) + self.graph.add((edge, self.mv.hasTarget, target)) + return edge + + def create_nodevalue(self, value: Any) -> Optional[Node]: + if not self.is_valid_datavalue(value): + return None + node = URIRef(self.namespace_uri + str(self.new_id())) + if isinstance(value, tuple): + value = {"Type": value[0]} + self.graph.add((node, self.mv.hasValue, Literal(json.dumps(value)))) + return node + + def create_dict(self, source: Element, value: Any, target: Element) -> Optional[Tuple[Edge, Edge, Node]]: + if not isinstance(source, URIRef): + return + if not isinstance(target, URIRef): + return + if not self.is_valid_datavalue(value): + return + + n = self.create_nodevalue(value) + e = self.create_edge(source, target) + self.create_edge(e, n) + + def read_root(self) -> Node: + return self.root + + def read_value(self, node: Node) -> Optional[Any]: + if not isinstance(node, URIRef) or not (node, None, None) in self.graph: + return None + result = self.graph.query(self.prepared_queries["read_value"], initBindings={"var1": node}) + if len(result) == 0: + return None + result = json.loads(list(result)[0][0]) + return result if not isinstance(result, dict) else (result["Type"],) + + def read_outgoing(self, elem: Element) -> Optional[List[Edge]]: + if not isinstance(elem, URIRef) or elem in self.garbage: + return None + result = self.graph.query(self.prepared_queries["read_outgoing"], initBindings={"var1": elem}) + return [i[0] for i in result] + + def read_incoming(self, elem: Element) -> Optional[List[Edge]]: + if not isinstance(elem, URIRef) or elem in self.garbage: + return None + result = self.graph.query(self.prepared_queries["read_incoming"], initBindings={"var1": elem}) + return [i[0] for i in result] + + def read_edge(self, edge: Edge) -> Tuple[Optional[Node], Optional[Node]]: + if not isinstance(edge, URIRef) or not (edge, None, None) in self.graph: + return None, None + result = self.graph.query(self.prepared_queries["read_edge"], initBindings={"var1": edge}) + if len(result) == 0: + return None, None + else: + return list(result)[0][0], list(result)[0][1] + + def read_dict(self, elem: Element, value: Any) -> Optional[Element]: + if not isinstance(elem, URIRef): + return None + q = f""" + SELECT ?value_node + WHERE {{ + ?main_edge MV:hasSource <{elem}> ; + MV:hasTarget ?value_node . + ?attr_edge MV:hasSource ?main_edge ; + MV:hasTarget ?attr_node . + ?attr_node MV:hasValue '{json.dumps(value)}' . + }} + """ + result = self.graph.query(q) + if len(result) == 0: + return None + return list(result)[0][0] + + def read_dict_keys(self, elem: Element) -> Optional[List[Any]]: + if not isinstance(elem, URIRef): + return None + result = self.graph.query(self.prepared_queries["read_dict_keys"], initBindings={"var1": elem}) + return [i[0] for i in result] + + def read_dict_edge(self, elem: Element, value: Any) -> Optional[Edge]: + if not isinstance(elem, URIRef): + return None + result = self.graph.query( + f""" + SELECT ?main_edge + WHERE {{ + ?main_edge MV:hasSource <{elem}> ; + MV:hasTarget ?value_node . + ?attr_edge MV:hasSource ?main_edge ; + MV:hasTarget ?attr_node . + ?attr_node MV:hasValue '{json.dumps(value)}' . + }} + """) + if len(result) == 0: + return None + return list(result)[0][0] + + def read_dict_node(self, elem: Element, value_node: Node) -> Optional[Element]: + if not isinstance(elem, URIRef): + return None + if not isinstance(value_node, URIRef): + return None + result = self.graph.query( + self.prepared_queries["read_dict_node"], initBindings={"var1": elem, "var2": value_node} + ) + if len(result) == 0: + return None + return list(result)[0][0] + + def read_dict_node_edge(self, elem: Element, value_node: Node) -> Optional[Edge]: + if not isinstance(elem, URIRef): + return None + if not isinstance(value_node, URIRef): + return None + result = self.graph.query( + self.prepared_queries["read_dict_node_edge"], initBindings={"var1": elem, "var2": value_node} + ) + if len(result) == 0: + return None + return list(result)[0][0] + + def read_reverse_dict(self, elem: Element, value: Any) -> Optional[List[Element]]: + if not isinstance(elem, URIRef): + return None + result = self.graph.query( + f""" + SELECT ?source_node + WHERE {{ + ?main_edge MV:hasTarget <{elem}> ; + MV:hasSource ?source_node . + ?attr_edge MV:hasSource ?main_edge ; + MV:hasTarget ?value_node . + ?value_node MV:hasValue '{json.dumps(value)}' . + }} + """) + + return [i[0] for i in result] + + def delete_node(self, node: Node) -> None: + if node == self.root: + return + if not isinstance(node, URIRef): + return + # Check whether node isn't an edge + if (node, self.mv.hasSource, None) in self.graph or (node, self.mv.hasTarget, None) in self.graph: + return + # Remove its value if it exists + self.graph.remove((node, None, None)) + # Get all edges connecting this + result = self.graph.query(self.prepared_queries["delete_node"], initBindings={"var1": node}) + # ... and remove them + for e in result: + self.delete_edge(e[0]) + self.garbage.add(node) + + def delete_edge(self, edge: Edge) -> None: + if not isinstance(edge, URIRef): + return + # Check whether edge is actually an edge + if not ((edge, self.mv.hasSource, None) in self.graph and (edge, self.mv.hasTarget, None) in self.graph): + return + # Remove its links + self.graph.remove((edge, None, None)) + # Get all edges connecting this + result = self.graph.query(self.prepared_queries["delete_edge"], initBindings={"var1": edge}) + # ... and remove them + for e in result: + self.delete_edge(e[0]) + self.garbage.add(edge) diff --git a/state/test/fixtures/state.py b/state/test/fixtures/state.py index ab7eb87..86177a2 100644 --- a/state/test/fixtures/state.py +++ b/state/test/fixtures/state.py @@ -1,13 +1,13 @@ import pytest from state.pystate import PyState -# from state.rdfstate import RDFState -# from state.neo4jstate import Neo4jState +from state.rdfstate import RDFState +from state.neo4jstate import Neo4jState @pytest.fixture(params=[ (PyState,), - # (RDFState, "http://example.org/#"), - # (Neo4jState,) + (RDFState, "http://example.org/#"), + (Neo4jState,) ]) def state(request): if len(request.param) > 1: @@ -15,5 +15,5 @@ def state(request): else: state = request.param[0]() yield state - # if isinstance(state, Neo4jState): - # state.close(clear=True) + if isinstance(state, Neo4jState): + state.close(clear=True) From 9ab337a6a1efa37e8d00c34b21ffff596117a0db Mon Sep 17 00:00:00 2001 From: Andrei Bondarenko Date: Wed, 11 Aug 2021 23:03:58 +0200 Subject: [PATCH 44/46] Bottom service for bootstrap cycle 0 added --- service/__init__.py | 0 service/base.py | 7 ++++ service/bottom_0.py | 81 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 88 insertions(+) create mode 100644 service/__init__.py create mode 100644 service/base.py create mode 100644 service/bottom_0.py diff --git a/service/__init__.py b/service/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/service/base.py b/service/base.py new file mode 100644 index 0000000..651607e --- /dev/null +++ b/service/base.py @@ -0,0 +1,7 @@ +from abc import ABC, abstractmethod +from uuid import UUID + + +class Service(ABC): + def __init__(self, model: UUID): + self.model = model diff --git a/service/bottom_0.py b/service/bottom_0.py new file mode 100644 index 0000000..4a6be94 --- /dev/null +++ b/service/bottom_0.py @@ -0,0 +1,81 @@ +from service.base import Service, UUID +from state.base import State +from typing import Any, List + + +class Bottom0(Service): + def __init__(self, model: UUID, state: State): + super().__init__(model) + self.state = state + + def create_node(self, value=None) -> UUID: + if value is None: + return self.state.create_node() + else: + return self.state.create_nodevalue(value) + + def create_edge(self, source: UUID, target: UUID, label=None): + pass + + def read_model_root(self) -> UUID: + return self.model + + def read_value(self, node: UUID) -> Any: + return self.state.read_value(node) + + def read_edge_source(self, edge: UUID) -> UUID: + result = self.state.read_edge(edge) + return result[0] if result is not None else result + + def read_edge_target(self, edge: UUID) -> UUID: + result = self.state.read_edge(edge) + return result[1] if result is not None else result + + def read_incoming_edges(self, target: UUID, label=None) -> List[UUID]: + def read_label(_edge: UUID): + try: + label_edge, = self.state.read_outgoing(_edge) + _, tgt = self.state.read_edge(label_edge) + _label = self.state.read_value(tgt) + return _label + except (TypeError, ValueError): + return None + + edges = self.state.read_incoming(target) + if edges is None: + return [] + if label is not None: + edges = [e for e in edges if read_label(e) == label] + return edges + + def read_outgoing_edges(self, source: UUID, label=None) -> List[UUID]: + def read_label(_edge: UUID): + try: + label_edge, = self.state.read_outgoing(_edge) + _, tgt = self.state.read_edge(label_edge) + _label = self.state.read_value(tgt) + return _label + except (TypeError, ValueError): + return None + + edges = self.state.read_outgoing(source) + if edges is None: + return [] + if label is not None: + edges = [e for e in edges if read_label(e) == label] + return edges + + def read_keys(self, element: UUID) -> List[str]: + key_nodes = self.state.read_dict_keys(element) + unique_keys = {self.state.read_value(node) for node in key_nodes} + return list(unique_keys) + + def delete_element(self, element: UUID): + src, tgt = self.state.read_edge(element) + if src is None and tgt is None: + # node + self.state.delete_node(element) + else: + # edge + self.state.delete_edge(element) + From f3680c289fc5bda29b3269d42de3dcb60b60d11e Mon Sep 17 00:00:00 2001 From: Andrei Bondarenko Date: Wed, 11 Aug 2021 23:15:24 +0200 Subject: [PATCH 45/46] rEFACTORED SOME NAMES --- service/{bottom_0.py => bottom/V0.py} | 2 +- service/bottom/__init__.py | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename service/{bottom_0.py => bottom/V0.py} (99%) create mode 100644 service/bottom/__init__.py diff --git a/service/bottom_0.py b/service/bottom/V0.py similarity index 99% rename from service/bottom_0.py rename to service/bottom/V0.py index 4a6be94..36a51d8 100644 --- a/service/bottom_0.py +++ b/service/bottom/V0.py @@ -3,7 +3,7 @@ from state.base import State from typing import Any, List -class Bottom0(Service): +class Bottom(Service): def __init__(self, model: UUID, state: State): super().__init__(model) self.state = state diff --git a/service/bottom/__init__.py b/service/bottom/__init__.py new file mode 100644 index 0000000..e69de29 From 2264a8b02dcce8cb0dd9167fc51f29c0900aa342 Mon Sep 17 00:00:00 2001 From: Andrei Bondarenko Date: Wed, 11 Aug 2021 23:03:58 +0200 Subject: [PATCH 46/46] Implemented LTM_bottom^(0) --- service/__init__.py | 0 service/base.py | 7 ++++ service/bottom/V0.py | 81 ++++++++++++++++++++++++++++++++++++++ service/bottom/__init__.py | 0 4 files changed, 88 insertions(+) create mode 100644 service/__init__.py create mode 100644 service/base.py create mode 100644 service/bottom/V0.py create mode 100644 service/bottom/__init__.py diff --git a/service/__init__.py b/service/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/service/base.py b/service/base.py new file mode 100644 index 0000000..651607e --- /dev/null +++ b/service/base.py @@ -0,0 +1,7 @@ +from abc import ABC, abstractmethod +from uuid import UUID + + +class Service(ABC): + def __init__(self, model: UUID): + self.model = model diff --git a/service/bottom/V0.py b/service/bottom/V0.py new file mode 100644 index 0000000..36a51d8 --- /dev/null +++ b/service/bottom/V0.py @@ -0,0 +1,81 @@ +from service.base import Service, UUID +from state.base import State +from typing import Any, List + + +class Bottom(Service): + def __init__(self, model: UUID, state: State): + super().__init__(model) + self.state = state + + def create_node(self, value=None) -> UUID: + if value is None: + return self.state.create_node() + else: + return self.state.create_nodevalue(value) + + def create_edge(self, source: UUID, target: UUID, label=None): + pass + + def read_model_root(self) -> UUID: + return self.model + + def read_value(self, node: UUID) -> Any: + return self.state.read_value(node) + + def read_edge_source(self, edge: UUID) -> UUID: + result = self.state.read_edge(edge) + return result[0] if result is not None else result + + def read_edge_target(self, edge: UUID) -> UUID: + result = self.state.read_edge(edge) + return result[1] if result is not None else result + + def read_incoming_edges(self, target: UUID, label=None) -> List[UUID]: + def read_label(_edge: UUID): + try: + label_edge, = self.state.read_outgoing(_edge) + _, tgt = self.state.read_edge(label_edge) + _label = self.state.read_value(tgt) + return _label + except (TypeError, ValueError): + return None + + edges = self.state.read_incoming(target) + if edges is None: + return [] + if label is not None: + edges = [e for e in edges if read_label(e) == label] + return edges + + def read_outgoing_edges(self, source: UUID, label=None) -> List[UUID]: + def read_label(_edge: UUID): + try: + label_edge, = self.state.read_outgoing(_edge) + _, tgt = self.state.read_edge(label_edge) + _label = self.state.read_value(tgt) + return _label + except (TypeError, ValueError): + return None + + edges = self.state.read_outgoing(source) + if edges is None: + return [] + if label is not None: + edges = [e for e in edges if read_label(e) == label] + return edges + + def read_keys(self, element: UUID) -> List[str]: + key_nodes = self.state.read_dict_keys(element) + unique_keys = {self.state.read_value(node) for node in key_nodes} + return list(unique_keys) + + def delete_element(self, element: UUID): + src, tgt = self.state.read_edge(element) + if src is None and tgt is None: + # node + self.state.delete_node(element) + else: + # edge + self.state.delete_edge(element) + diff --git a/service/bottom/__init__.py b/service/bottom/__init__.py new file mode 100644 index 0000000..e69de29