Skip to content

Commit

Permalink
Merge pull request #707 from neo4j-contrib/rc/5.0.1
Browse files Browse the repository at this point in the history
Release 5.0.1
  • Loading branch information
mariusconjeaud committed Jun 1, 2023
2 parents fb90198 + 9c8cf6c commit 074bc6d
Show file tree
Hide file tree
Showing 23 changed files with 330 additions and 361 deletions.
8 changes: 6 additions & 2 deletions .github/workflows/integration-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ on:
push:
branches: [ "master" ]
pull_request:
branches: [ "master" ]
branches: [ "master", "rc/*" ]

jobs:
build:
Expand All @@ -34,7 +34,11 @@ jobs:
python -m pip install --upgrade pip
pip install -e '.[dev]'
- name: Test with pytest
env:
AURA_TEST_DB_USER: ${{ secrets.AURA_TEST_DB_USER }}
AURA_TEST_DB_PASSWORD: ${{ secrets.AURA_TEST_DB_PASSWORD }}
AURA_TEST_DB_HOSTNAME: ${{ secrets.AURA_TEST_DB_HOSTNAME }}
run: |
pytest
pytest --cov=neomodel --cov-report=html:coverage_report
- name: Upload coverage reports to Codecov
uses: codecov/codecov-action@v3
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,6 @@ lib
.vscode
pyvenv.cfg
coverage_report/
.coverage*
.coverage*
.DS_STORE
cov.xml
5 changes: 5 additions & 0 deletions Changelog
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
Version 5.0.1 2023-06
* Removed deprecated methods StructuredRel.delete and RelationshipManager.search
* Extended test coverage, fixed some typos, improve linting
* Upcoming breaking change notice : Version 5.1.0 will introduce a breaking change for users targeting a Neo4j database in version 5. This release will introduce Neo4j's new element_id, which replaces id. The breaking change will happen if you have custom Cypher queries that do things like "WHERE id(n)=$id" => you will have to use Cypher's elementId() function instead starting from neomodel 5.1.0

Version 5.0.0 2023-03
* Confirmed support of Neo4j versions 5.x and 4.4 (LTS)
* Dropped support of EOL Neo4j versions (4.3 and below)
Expand Down
6 changes: 6 additions & 0 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,12 @@ Setup a virtual environment, install neomodel for development and run the test s
$ pip install -e '.[dev]'
$ pytest

The tests in "test_connection.py" will fail locally if you don't specify the following environment variables::

$ export AURA_TEST_DB_USER=username
$ export AURA_TEST_DB_PASSWORD=password
$ export AURA_TEST_DB_HOSTNAME=url

If you are running a neo4j database for the first time the test suite will set the password to 'test'.
If the database is already populated, the test suite will abort with an error message and ask you to re-run it with the
``--resetdb`` switch. This is a safeguard to ensure that the test suite does not accidentally wipe out a database if you happen
Expand Down
7 changes: 6 additions & 1 deletion doc/source/relationships.rst
Original file line number Diff line number Diff line change
Expand Up @@ -251,7 +251,7 @@ that are directly related to another ``Person``, through all relationships::
definition)
all_jims_relations = relations_traversal.all()

The ``defintion`` argument is a :term:`py3:mapping` with these items:
The ``definition`` argument is a :term:`py3:mapping` with these items:

================= ===============================================================
``node_class`` The class of the traversal target node.
Expand All @@ -260,3 +260,8 @@ The ``defintion`` argument is a :term:`py3:mapping` with these items:
or an explicit name of a relation type (the edge's label).
``model`` The class of the relation model, ``None`` for such without one.
================= ===============================================================

.. note::

The ``RelationshipTo`` and ``RelationshipFrom`` objects are simply a proxy
for defining Traversal objects at the class level.
2 changes: 1 addition & 1 deletion neomodel/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,4 +38,4 @@
__email__ = "[email protected]"
__license__ = "MIT"
__package__ = "neomodel"
__version__ = "5.0.0"
__version__ = "5.0.1"
91 changes: 19 additions & 72 deletions neomodel/contrib/spatial_properties.py
Original file line number Diff line number Diff line change
Expand Up @@ -102,11 +102,7 @@ def __init__(self, *args, **kwargs):

# CRS validity check is common to both types of constructors that follow
if crs is not None and crs not in ACCEPTABLE_CRS:
raise ValueError(
"Invalid CRS({}). Expected one of {}".format(
crs, ",".join(ACCEPTABLE_CRS)
)
)
raise ValueError(f"Invalid CRS({crs}). Expected one of {','.join(ACCEPTABLE_CRS)}")
self._crs = crs

# If positional arguments have been supplied, then this is a possible call to the copy constructor or
Expand All @@ -116,11 +112,7 @@ def __init__(self, *args, **kwargs):
if isinstance(args[0], (tuple, list)):
# Check dimensionality of tuple
if len(args[0]) < 2 or len(args[0]) > 3:
raise ValueError(
"Invalid vector dimensions. Expected 2 or 3, received {}".format(
len(args[0])
)
)
raise ValueError(f"Invalid vector dimensions. Expected 2 or 3, received {len(args[0])}")
x = args[0][0]
y = args[0][1]
if len(args[0]) == 3:
Expand Down Expand Up @@ -148,20 +140,10 @@ def __init__(self, *args, **kwargs):
if crs is None:
self._crs = "cartesian-3d"
else:
raise ValueError(
"Invalid vector dimensions. "
"Expected 2 or 3, received {}".format(
len(args[0].coords[0])
)
)
raise ValueError(f"Invalid vector dimensions. Expected 2 or 3, received {len(args[0].coords[0])}")
return
else:
raise TypeError(
"Invalid object passed to copy constructor. "
"Expected NeomodelPoint or shapely Point, received {}".format(
type(args[0])
)
)
raise TypeError(f"Invalid object passed to copy constructor. Expected NeomodelPoint or shapely Point, received {type(args[0])}")

# Initialisation is either via x,y[,z] XOR longitude,latitude[,height]. Specifying both leads to an error.
if any(i is not None for i in [x, y, z]) and any(
Expand Down Expand Up @@ -208,16 +190,12 @@ def __init__(self, *args, **kwargs):
if "-3d" not in self._crs:
super().__init__((float(_x), float(_y)), **kwargs)
else:
raise ValueError(
"Invalid vector dimensions(2) for given CRS({}).".format(self._crs)
)
raise ValueError(f"Invalid vector dimensions(2) for given CRS({self._crs}).")
else:
if "-3d" in self._crs:
super().__init__((float(_x), float(_y), float(_z)), **kwargs)
else:
raise ValueError(
"Invalid vector dimensions(3) for given CRS({}).".format(self._crs)
)
raise ValueError(f"Invalid vector dimensions(3) for given CRS({self._crs}).")

@property
def crs(self):
Expand All @@ -227,53 +205,47 @@ def crs(self):
def x(self):
if not self._crs.startswith("cartesian"):
raise AttributeError(
'Invalid coordinate ("x") for points defined over {}'.format(self.crs)
f'Invalid coordinate ("x") for points defined over {self.crs}'
)
return super().x

@property
def y(self):
if not self._crs.startswith("cartesian"):
raise AttributeError(
'Invalid coordinate ("y") for points defined over {}'.format(self.crs)
f'Invalid coordinate ("y") for points defined over {self.crs}'
)
return super().y

@property
def z(self):
if not self._crs == "cartesian-3d":
raise AttributeError(
'Invalid coordinate ("z") for points defined over {}'.format(self.crs)
f'Invalid coordinate ("z") for points defined over {self.crs}'
)
return super().z

@property
def latitude(self):
if not self._crs.startswith("wgs-84"):
raise AttributeError(
'Invalid coordinate ("latitude") for points defined over {}'.format(
self.crs
)
f'Invalid coordinate ("latitude") for points defined over {self.crs}'
)
return super().y

@property
def longitude(self):
if not self._crs.startswith("wgs-84"):
raise AttributeError(
'Invalid coordinate ("longitude") for points defined over {}'.format(
self.crs
)
f'Invalid coordinate ("longitude") for points defined over {self.crs}'
)
return super().x

@property
def height(self):
if not self._crs == "wgs-84-3d":
raise AttributeError(
'Invalid coordinate ("height") for points defined over {}'.format(
self.crs
)
f'Invalid coordinate ("height") for points defined over {self.crs}'
)
return super().z

Expand Down Expand Up @@ -312,22 +284,13 @@ def __init__(self, *args, **kwargs):
crs = None

if crs is None or (crs not in ACCEPTABLE_CRS):
raise ValueError(
"Invalid CRS({}). "
"Point properties require CRS to be one of {}".format(
crs, ",".join(ACCEPTABLE_CRS)
)
)
raise ValueError(f"Invalid CRS({crs}). Point properties require CRS to be one of {','.join(ACCEPTABLE_CRS)}")

# If a default value is passed and it is not a callable, then make sure it is in the right type
if "default" in kwargs:
if not hasattr(kwargs["default"], "__call__"):
if not isinstance(kwargs["default"], NeomodelPoint):
raise TypeError(
"Invalid default value. "
"Expected NeomodelPoint, received {}".format(
type(kwargs["default"])
)
raise TypeError(f"Invalid default value. Expected NeomodelPoint, received {type(kwargs['default'])}"
)

super().__init__(*args, **kwargs)
Expand All @@ -344,26 +307,16 @@ def inflate(self, value):
"""
if not isinstance(value, neo4j.spatial.Point):
raise TypeError(
"Invalid datatype to inflate. Expected POINT datatype, received {}".format(
type(value)
)
f"Invalid datatype to inflate. Expected POINT datatype, received {type(value)}"
)

try:
value_point_crs = SRID_TO_CRS[value.srid]
except KeyError as e:
raise ValueError(
"Invalid SRID to inflate. "
"Expected one of {}, received {}".format(SRID_TO_CRS.keys(), value.srid)
) from e
raise ValueError(f"Invalid SRID to inflate. Expected one of {SRID_TO_CRS.keys()}, received {value.srid}") from e

if self._crs != value_point_crs:
raise ValueError(
"Invalid CRS. "
"Expected POINT defined over {}, received {}".format(
self._crs, value_point_crs
)
)
raise ValueError(f"Invalid CRS. Expected POINT defined over {self._crs}, received {value_point_crs}")
# cartesian
if value.srid == 7203:
return NeomodelPoint(x=value.x, y=value.y)
Expand Down Expand Up @@ -393,17 +346,11 @@ def deflate(self, value):
"""
if not isinstance(value, NeomodelPoint):
raise TypeError(
"Invalid datatype to deflate. Expected NeomodelPoint, received {}".format(
type(value)
)
f"Invalid datatype to deflate. Expected NeomodelPoint, received {type(value)}"
)

if not value.crs == self._crs:
raise ValueError(
"Invalid CRS. "
"Expected NeomodelPoint defined over {}, "
"received NeomodelPoint defined over {}".format(self._crs, value.crs)
)
raise ValueError(f"Invalid CRS. Expected NeomodelPoint defined over {self._crs}, received NeomodelPoint defined over {value.crs}")

if value.crs == "cartesian-3d":
return neo4j.spatial.CartesianPoint((value.x, value.y, value.z))
Expand Down
Loading

0 comments on commit 074bc6d

Please sign in to comment.