From 73fabaacb25955eb273e54a451a8741af59a5558 Mon Sep 17 00:00:00 2001 From: mprahl Date: Jun 19 2018 12:41:20 +0000 Subject: Support python-neo4j-driver v1.6.0 --- diff --git a/neo4j-driver-1.6-support.patch b/neo4j-driver-1.6-support.patch new file mode 100644 index 0000000..440d940 --- /dev/null +++ b/neo4j-driver-1.6-support.patch @@ -0,0 +1,178 @@ +From a9d55a0d61eb84e63cf2079d920add79ff1fef98 Mon Sep 17 00:00:00 2001 +From: mprahl +Date: Thu, 14 Jun 2018 09:48:37 -0400 +Subject: [PATCH] Support neo4j-driver v1.6.0 + +As part of neo4j-driver v1.6.0, `properties` was renamed to +`_properties` on `neo4j.v1.types.graph.Node`. This commit addresses +that and changes the version restriction in setup.py to allow +neo4j-driver 1.6.0. +--- + neomodel/contrib/semi_structured.py | 12 +++++++----- + neomodel/core.py | 7 ++++--- + neomodel/relationship_manager.py | 5 +++-- + neomodel/util.py | 10 ++++++++++ + setup.py | 2 +- + test/test_properties.py | 14 ++++++++------ + 6 files changed, 33 insertions(+), 17 deletions(-) + +diff --git a/neomodel/contrib/semi_structured.py b/neomodel/contrib/semi_structured.py +index 20c25b6..883a589 100644 +--- a/neomodel/contrib/semi_structured.py ++++ b/neomodel/contrib/semi_structured.py +@@ -1,5 +1,6 @@ + from neomodel.core import StructuredNode + from neomodel.exceptions import InflateConflict, DeflateConflict ++from neomodel.util import _get_node_properties + + + class SemiStructuredNode(StructuredNode): +@@ -33,18 +34,19 @@ def inflate(cls, node): + else: + props = {} + for key, prop in cls.__all_properties__: +- if key in node.properties: +- props[key] = prop.inflate(node.properties[key], node) ++ node_properties = _get_node_properties(node) ++ if key in node_properties: ++ props[key] = prop.inflate(node_properties[key], node) + elif prop.has_default: + props[key] = prop.default_value() + else: + props[key] = None + # handle properties not defined on the class +- for free_key in (x for x in node.properties if x not in props): ++ for free_key in (x for x in node_properties if x not in props): + if hasattr(cls, free_key): + raise InflateConflict(cls, free_key, +- node.properties[free_key], node.id) +- props[free_key] = node.properties[free_key] ++ node_properties[free_key], node.id) ++ props[free_key] = node_properties[free_key] + + snode = cls(**props) + snode.id = node.id +diff --git a/neomodel/core.py b/neomodel/core.py +index 5d56e28..65d6e48 100644 +--- a/neomodel/core.py ++++ b/neomodel/core.py +@@ -6,7 +6,7 @@ + from neomodel.exceptions import DoesNotExist + from neomodel.hooks import hooks + from neomodel.properties import Property, PropertyManager +-from neomodel.util import Database, classproperty, _UnsavedNode ++from neomodel.util import Database, classproperty, _UnsavedNode, _get_node_properties + + db = Database() + +@@ -438,13 +438,14 @@ def inflate(cls, node): + snode = cls() + snode.id = node + else: ++ node_properties = _get_node_properties(node) + props = {} + for key, prop in cls.__all_properties__: + # map property name from database to object property + db_property = prop.db_property or key + +- if db_property in node.properties: +- props[key] = prop.inflate(node.properties[db_property], node) ++ if db_property in node_properties: ++ props[key] = prop.inflate(node_properties[db_property], node) + elif prop.has_default: + props[key] = prop.default_value() + else: +diff --git a/neomodel/relationship_manager.py b/neomodel/relationship_manager.py +index 0563aeb..cc027b6 100644 +--- a/neomodel/relationship_manager.py ++++ b/neomodel/relationship_manager.py +@@ -2,7 +2,7 @@ + import functools + from importlib import import_module + from .exceptions import NotConnected +-from .util import deprecated ++from .util import deprecated, _get_node_properties + from .match import OUTGOING, INCOMING, EITHER, _rel_helper, Traversal, NodeSet + from .relationship import StructuredRel + +@@ -190,7 +190,8 @@ def reconnect(self, old_node, new_node): + "MATCH (us), (old) WHERE id(us)={self} and id(old)={old} " + "MATCH " + old_rel + " RETURN r", {'old': old_node.id}) + if result: +- existing_properties = result[0][0].properties.keys() ++ node_properties = _get_node_properties(result[0][0]) ++ existing_properties = node_properties.keys() + else: + raise NotConnected('reconnect', self.source, old_node) + +diff --git a/neomodel/util.py b/neomodel/util.py +index bad14ff..d175688 100644 +--- a/neomodel/util.py ++++ b/neomodel/util.py +@@ -193,3 +193,13 @@ def __repr__(self): + + def __str__(self): + return self.__repr__() ++ ++ ++def _get_node_properties(node): ++ """Get the properties from a neo4j.v1.types.graph.Node object.""" ++ # 1.6.x and newer have it as `_properties` ++ if hasattr(node, '_properties'): ++ return node._properties ++ # 1.5.x and older have it as `properties` ++ else: ++ return node.properties +diff --git a/setup.py b/setup.py +index 889fe46..1943d83 100644 +--- a/setup.py ++++ b/setup.py +@@ -17,7 +17,7 @@ + scripts=['scripts/neomodel_install_labels', 'scripts/neomodel_remove_labels'], + setup_requires=['pytest-runner'] if any(x in ('pytest', 'test') for x in sys.argv) else [], + tests_require=['pytest'], +- install_requires=['neo4j-driver==1.5.2', 'pytz>=2016.10'], ++ install_requires=['neo4j-driver>=1.5.2, <1.7.0', 'pytz>=2016.10'], + classifiers=[ + "Development Status :: 5 - Production/Stable", + 'Intended Audience :: Developers', +diff --git a/test/test_properties.py b/test/test_properties.py +index 0b85ebd..368caf8 100644 +--- a/test/test_properties.py ++++ b/test/test_properties.py +@@ -10,8 +10,7 @@ + EmailProperty, JSONProperty, NormalProperty, NormalizedProperty, + RegexProperty, StringProperty, UniqueIdProperty + ) +- +- ++from neomodel.util import _get_node_properties + + + class FooBar(object): +@@ -187,9 +186,11 @@ class TestNode(StructuredNode): + + # check database property name on low level + results, meta = db.cypher_query("MATCH (n:TestNode) RETURN n") +- assert results[0][0].properties['name'] == "jim" ++ node_properties = _get_node_properties(results[0][0]) ++ assert node_properties['name'] == "jim" + +- assert not 'name_' in results[0][0].properties ++ node_properties = _get_node_properties(results[0][0]) ++ assert not 'name_' in node_properties + assert not hasattr(x, 'name') + assert hasattr(x, 'name_') + assert TestNode.nodes.filter(name_="jim").all()[0].name_ == x.name_ +@@ -210,8 +211,9 @@ class TestNode(StructuredNode): + + # check database property name on low level + results, meta = db.cypher_query("MATCH (n:TestNode) RETURN n") +- assert results[0][0].properties['name'] == "jim" +- assert 'name_' not in results[0][0].properties ++ node_properties = _get_node_properties(results[0][0]) ++ assert node_properties['name'] == "jim" ++ assert 'name_' not in node_properties + + # delete node afterwards + x.delete() diff --git a/python-neomodel.spec b/python-neomodel.spec index 172fd63..f64df18 100644 --- a/python-neomodel.spec +++ b/python-neomodel.spec @@ -4,12 +4,13 @@ Name: python-%{srcname} Version: 3.2.8 -Release: 3%{?dist} +Release: 4%{?dist} Summary: %{sum} License: MIT URL: https://pypi.python.org/pypi/%{srcname} Source0: https://files.pythonhosted.org/packages/source/n/%{srcname}/%{srcname}-%{version}.tar.gz +Patch0: neo4j-driver-1.6-support.patch BuildArch: noarch BuildRequires: python2-devel @@ -100,6 +101,10 @@ ln -s %{_bindir}/neomodel_remove_labels %{buildroot}/%{_bindir}/neomodel_remove_ %changelog +* Tue Jun 19 2018 mprahl - 3.2.8-4 +- add upstream patch to support python neo4j-driver 1.6.x +- unpin the python-neo4j-driver version + * Tue Jun 19 2018 Miro HronĨok - 3.2.8-3 - Rebuilt for Python 3.7