From 050a4dfdc158504d822f39fda8cb16b716cd93a2 Mon Sep 17 00:00:00 2001 From: Julian Minder Date: Mon, 7 Mar 2022 12:19:15 +0100 Subject: [PATCH] fixed bug in config parser that created parse error with spaces after wrappers of dynamic attributes --- README.md | 10 +++++----- rel2graph/core/config_parser.py | 13 +++++++++---- setup.py | 2 +- tests/unit/core/resources/dynamic_keys.yaml | 16 ++++++++++++++++ .../core/resources/typing_exceptions1.yaml | 1 - .../core/resources/typing_exceptions2.yaml | 1 - tests/unit/core/test_config_parser.py | 18 ++++++++++++++++++ 7 files changed, 49 insertions(+), 12 deletions(-) create mode 100644 tests/unit/core/resources/dynamic_keys.yaml delete mode 100644 tests/unit/core/resources/typing_exceptions1.yaml delete mode 100644 tests/unit/core/resources/typing_exceptions2.yaml diff --git a/README.md b/README.md index d9e889b..0f31bb9 100644 --- a/README.md +++ b/README.md @@ -11,9 +11,9 @@ The library is built specifically for converting data into a [neo4j](https://neo Note: The [py2neo](https://py2neo.org/2021.1/index.html) library does not support parallel relations of the same type (same source, same target and same type). If your graph requires such parallel relations please checkout the provided [py2neo extensions](/docs/documentation.md#py2neo-extensions). ## Installation -If you have setup a private ssh key for your github, copy-paste the command below to install the latest version ([v0.4.3][latest_tag]): +If you have setup a private ssh key for your github, copy-paste the command below to install the latest version ([v0.4.4][latest_tag]): ``` -$ pip install git+ssh://git@github.com/sg-dev/rel2graph@v0.4.3 +$ pip install git+ssh://git@github.com/sg-dev/rel2graph@v0.4.4 ``` If you don't have ssh set up, download the latest wheel [here][latest_wheel] and install the wheel with: @@ -85,7 +85,7 @@ converter() # Known issues If you encounter a bug or an unexplainable behavior, please check the [known issues](https://github.com/sg-dev/rel2graph/labels/bug) list. If your issue is not found, submit a new one. -[latest_version]: v0.4.3 -[latest_tag]: https://github.com/sg-dev/rel2graph/releases/tag/v0.4.3 -[latest_wheel]: https://github.com/sg-dev/rel2graph/releases/download/v0.4.3/rel2graph-0.4.3-py3-none-any.whl +[latest_version]: v0.4.4 +[latest_tag]: https://github.com/sg-dev/rel2graph/releases/tag/v0.4.4 +[latest_wheel]: https://github.com/sg-dev/rel2graph/releases/download/v0.4.4/rel2graph-0.4.4-py3-none-any.whl [wiki]: docs/documentation.md diff --git a/rel2graph/core/config_parser.py b/rel2graph/core/config_parser.py index d7ae814..85c7f73 100644 --- a/rel2graph/core/config_parser.py +++ b/rel2graph/core/config_parser.py @@ -136,14 +136,14 @@ def _string_to_instructions(config_str: str) -> List[Any]: end_e = config_str[end_e+1:].find("[") if end_e >= 0 and end > end_e: # unpack list - args = _split_arguments(config_str[end_e+1:-1]) # because we need to remove the closing bracket + args = _split_arguments(config_str.strip()[end_e+1:-1]) # because we need to remove the closing bracket arg_instructions = [] for arg in args: arg_instructions.append(_string_to_instructions(arg)) instructions.extend(arg_instructions) elif end > 0: instructions.append(config_str[:end]) - args = _split_arguments(config_str[end+1:-1]) # because we need to remove the closing bracket + args = _split_arguments(config_str.strip()[end+1:-1]) # because we need to remove the closing bracket arg_instructions = [] for arg in args: @@ -151,7 +151,7 @@ def _string_to_instructions(config_str: str) -> List[Any]: instructions.append(arg_instructions) elif end_e >= 0 and len(config_str) == 2: # Handle empty list of args - args = _split_arguments(config_str[end_e+1:-1]) # because we need to remove the closing bracket + args = _split_arguments(config_str.strip()[end_e+1:-1]) # because we need to remove the closing bracket instructions.extend(args) else: # static argument -> parse to type if config_str == "None": @@ -298,7 +298,9 @@ def _precompile_dynamic_nokey_arguments(self, config_str: str, allowed_presymbol # Parse Dynamic Entity Attributes without key for match in re.finditer(f"(?!\"=)([{allowed_presymbols}])\s*(\w+)(?!&)[.](?!&)(\w+)", config_str): if match.group(2) == self._entity_type: - config_str = re.sub(match.group(0), _convert(match, "{0}AttributeFactory(&None,\"{2}\",&None)"), config_str) + tmp = match.groups() + null = match.groups(0) + config_str = re.sub(_escape(match.group(0)), _convert(match, "{0}AttributeFactory(&None,\"{2}\",&None)"), config_str) else: # TODO: Inefficient since dynamic attributes are recomputed and not extracted # Need structure to dynamically extract information from an existing node @@ -316,6 +318,9 @@ def _precompile_entity_type(self, config_str: str) -> str: def _precompile_graph_element(self, config_str: str) -> str: """Precompiles a graph element (Node or Relation)""" + # Remove identity + config_str = config_str[:_index_of_closing_bracket(config_str, 0)+1] + # Convert ids into Matchers for id in self._ids: # The first lookahead makes sure that an even amount of \" are after the match -> the id is not in a string diff --git a/setup.py b/setup.py index 6460eae..f7bdf6b 100644 --- a/setup.py +++ b/setup.py @@ -9,7 +9,7 @@ setup( name = "rel2graph", packages = find_packages(), - version = "0.4.3", + version = "0.4.4", description = "Library for converting relational data into graph data (neo4j)", author = "Julian Minder", author_email = "jminder@ethz.ch", diff --git a/tests/unit/core/resources/dynamic_keys.yaml b/tests/unit/core/resources/dynamic_keys.yaml new file mode 100644 index 0000000..47f3953 --- /dev/null +++ b/tests/unit/core/resources/dynamic_keys.yaml @@ -0,0 +1,16 @@ +ENTITY("entity"): + NODE(entity.dynamic_key) node: + + attr = entity.dynamic_key + - attr = WRAPPER(entity.dynamic_key) + NODE(WRAPPER(entity.dynamic_key)) node: + + attr = entity.dynamic_key + NODE(entity.dynamic_key, WRAPPER(entity.dynamic_key)) node: + + attr = entity.dynamic_key + NODE(entity.dynamic_key , WRAPPER(entity.dynamic_key) ) node: + NODE(WRAPPER(entity.dynamic_key), entity.dynamic_key) node: + + attr = entity.dynamic_key + RELATION(node, entity.dynamic_key, node): + + attr = entity.dynamic_key + RELATION(node, WRAPPER(entity.dynamic_key), node): + RELATION(node, WRAPPER(entity.dynamic_key) , node): + RELATION(node, WRAPPER( entity.dynamic_key ) , node): \ No newline at end of file diff --git a/tests/unit/core/resources/typing_exceptions1.yaml b/tests/unit/core/resources/typing_exceptions1.yaml deleted file mode 100644 index 39179ec..0000000 --- a/tests/unit/core/resources/typing_exceptions1.yaml +++ /dev/null @@ -1 +0,0 @@ -ENTITY("TEST") \ No newline at end of file diff --git a/tests/unit/core/resources/typing_exceptions2.yaml b/tests/unit/core/resources/typing_exceptions2.yaml deleted file mode 100644 index 39179ec..0000000 --- a/tests/unit/core/resources/typing_exceptions2.yaml +++ /dev/null @@ -1 +0,0 @@ -ENTITY("TEST") \ No newline at end of file diff --git a/tests/unit/core/test_config_parser.py b/tests/unit/core/test_config_parser.py index 6d66d9e..a394f14 100644 --- a/tests/unit/core/test_config_parser.py +++ b/tests/unit/core/test_config_parser.py @@ -12,6 +12,11 @@ import pytest from rel2graph.core.config_parser import parse +from rel2graph import register_attribute_preprocessor + +@register_attribute_preprocessor +def WRAPPER(resource): + return resource def get_rel_type(relation_factory): return relation_factory._type.static_attribute_value @@ -103,3 +108,16 @@ def test_typing(): conditions = af2str(fm._conditions) check_types(conditions) +def test_dynkeys(): + node_supplychain, relation_supplychain = parse(get_filepath("dynamic_keys"))["entity"] + + for nf in node_supplychain.factories: + for label in nf._labels: + assert(label._entity_attribute == "dynamic_key") + for attr in nf._attributes: + assert(attr._entity_attribute == "dynamic_key") + + for rf in relation_supplychain.factories: + assert(rf._type._entity_attribute == "dynamic_key") + for attr in nf._attributes: + assert(attr._entity_attribute == "dynamic_key") \ No newline at end of file