Blob Blame History Raw
From a9d55a0d61eb84e63cf2079d920add79ff1fef98 Mon Sep 17 00:00:00 2001
From: mprahl <mprahl@users.noreply.github.com>
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()