From fd64d3f37859b452554849f22d55e3e0fff2ac0d Mon Sep 17 00:00:00 2001 From: Rouven Bauer Date: Tue, 6 Jul 2021 12:23:13 +0200 Subject: [PATCH] Bump driver version to 4.4, drop Python 3.5 support --- CHANGELOG.md | 6 ++- README.rst | 3 +- TESTING.md | 6 +-- docs/source/index.rst | 3 +- neo4j/conf.py | 32 ++++------------ neo4j/io/__init__.py | 65 +++++++++++++++++--------------- neo4j/io/_bolt3.py | 2 +- neo4j/io/_bolt4.py | 15 ++++++-- neo4j/meta.py | 2 +- setup.py | 1 - testkit/Dockerfile | 4 +- tests/unit/io/test_class_bolt.py | 4 +- tox-unit.ini | 2 +- tox.ini | 1 - 14 files changed, 72 insertions(+), 74 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2ab46ed4c..0ac71da9a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ # Neo4j Driver Change Log +## Version 4.4 + +- Python 3.5 support has been dropped. + ## Version 4.3 @@ -35,4 +39,4 @@ + Python 3.2 support has been dropped. + Python 3.1 support has been dropped. + Python 3.0 support has been dropped. -+ Python 2.7 support has been dropped. \ No newline at end of file ++ Python 2.7 support has been dropped. diff --git a/README.rst b/README.rst index 3d24acea8..d905dc7c5 100644 --- a/README.rst +++ b/README.rst @@ -10,7 +10,6 @@ These drivers will also be compatible with the previous Neo4j release, although + Python 3.8 supported. + Python 3.7 supported. + Python 3.6 supported. -+ Python 3.5 supported. Python 2.7 support has been dropped as of the Neo4j 4.0 release. @@ -120,4 +119,4 @@ Other Information .. _`Neo4j Cypher Refcard`: https://neo4j.com/docs/cypher-refcard/current/ .. _`Example Project`: https://github.com/neo4j-examples/movies-python-bolt .. _`Driver Wiki`: https://github.com/neo4j/neo4j-python-driver/wiki -.. _`Neo4j 4.0 Migration Guide`: https://neo4j.com/docs/migration-guide/4.0/ \ No newline at end of file +.. _`Neo4j 4.0 Migration Guide`: https://neo4j.com/docs/migration-guide/4.0/ diff --git a/TESTING.md b/TESTING.md index 872de6444..88d99878c 100644 --- a/TESTING.md +++ b/TESTING.md @@ -1,7 +1,7 @@ # Neo4j Driver Testing To run driver tests, [Tox](https://tox.readthedocs.io) is required as well as at least one version of Python. -The versions of Python supported by this driver are CPython 2.7, 3.4, 3.5 and 3.6. +The versions of Python supported by this driver are CPython 3.6, 3.7, 3.8, and 3.9. ## Unit Tests & Stub Tests @@ -40,7 +40,7 @@ a report can be viewed after the run with `coverage report --show-missing`. ## Testing with Testkit -Tests **require** the latest [Testkit 4.3](https://github.com/neo4j-drivers/testkit/tree/4.3), Python3 and Docker. +Tests **require** the latest [Testkit 4.4](https://github.com/neo4j-drivers/testkit/tree/4.4), Python3 and Docker. Testkit is needed to be cloned and configured to run against the Python Driver. Use the following steps to configure Testkit. @@ -69,4 +69,4 @@ To run test against against some Neo4j version: python3 main.py ``` -More details about how to use Teskit could be found on [its repository](https://github.com/neo4j-drivers/testkit/tree/4.3) +More details about how to use Teskit could be found on [its repository](https://github.com/neo4j-drivers/testkit/tree/4.4) diff --git a/docs/source/index.rst b/docs/source/index.rst index 284a5e16e..feb8d90d4 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -6,8 +6,8 @@ The Official Neo4j Driver for Python. Neo4j versions supported: +* Neo4j 4.4 * Neo4j 4.3 -* Neo4j 4.2 * Neo4j 3.5 Python versions supported: @@ -16,7 +16,6 @@ Python versions supported: * Python 3.8 * Python 3.7 * Python 3.6 -* Python 3.5 .. note:: diff --git a/neo4j/conf.py b/neo4j/conf.py index 0dfa33ee9..80ad44c8b 100644 --- a/neo4j/conf.py +++ b/neo4j/conf.py @@ -232,30 +232,14 @@ def get_ssl_context(self): # TLS 1.1 - Released in 2006, published as RFC 4346. (Disabled) # TLS 1.2 - Released in 2008, published as RFC 5246. - try: - # python 3.6+ - # https://docs.python.org/3.6/library/ssl.html#ssl.PROTOCOL_TLS_CLIENT - ssl_context = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT) - - # For recommended security options see - # https://docs.python.org/3.6/library/ssl.html#protocol-versions - ssl_context.options |= ssl.OP_NO_TLSv1 # Python 3.2 - ssl_context.options |= ssl.OP_NO_TLSv1_1 # Python 3.4 - - except AttributeError: - # python 3.5 - # https://docs.python.org/3.5/library/ssl.html#ssl.PROTOCOL_TLS - ssl_context = ssl.SSLContext(ssl.PROTOCOL_TLS) - - # For recommended security options see - # https://docs.python.org/3.5/library/ssl.html#protocol-versions - ssl_context.options |= ssl.OP_NO_SSLv2 # Python 3.2 - ssl_context.options |= ssl.OP_NO_SSLv3 # Python 3.2 - ssl_context.options |= ssl.OP_NO_TLSv1 # Python 3.2 - ssl_context.options |= ssl.OP_NO_TLSv1_1 # Python 3.4 - - ssl_context.verify_mode = ssl.CERT_REQUIRED # https://docs.python.org/3.5/library/ssl.html#ssl.SSLContext.verify_mode - ssl_context.check_hostname = True # https://docs.python.org/3.5/library/ssl.html#ssl.SSLContext.check_hostname + # https://docs.python.org/3.6/library/ssl.html#ssl.PROTOCOL_TLS_CLIENT + ssl_context = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT) + + # For recommended security options see + # https://docs.python.org/3.6/library/ssl.html#protocol-versions + ssl_context.options |= ssl.OP_NO_TLSv1 # Python 3.2 + ssl_context.options |= ssl.OP_NO_TLSv1_1 # Python 3.4 + if self.trust == TRUST_ALL_CERTIFICATES: ssl_context.check_hostname = False diff --git a/neo4j/io/__init__.py b/neo4j/io/__init__.py index 2e758a7f1..783a85380 100644 --- a/neo4j/io/__init__.py +++ b/neo4j/io/__init__.py @@ -211,7 +211,7 @@ def protocol_handlers(cls, protocol_version=None): # Carry out Bolt subclass imports locally to avoid circular dependency issues. from neo4j.io._bolt3 import Bolt3 - from neo4j.io._bolt4 import Bolt4x0, Bolt4x1, Bolt4x2, Bolt4x3 + from neo4j.io._bolt4 import Bolt4x0, Bolt4x1, Bolt4x2, Bolt4x3, Bolt4x4 handlers = { Bolt3.PROTOCOL_VERSION: Bolt3, @@ -219,6 +219,7 @@ def protocol_handlers(cls, protocol_version=None): Bolt4x1.PROTOCOL_VERSION: Bolt4x1, Bolt4x2.PROTOCOL_VERSION: Bolt4x2, Bolt4x3.PROTOCOL_VERSION: Bolt4x3, + Bolt4x4.PROTOCOL_VERSION: Bolt4x4, } if protocol_version is None: @@ -238,25 +239,24 @@ def version_list(cls, versions, limit=4): preference. The number of protocol versions (or ranges) returned is limited to four. """ - ranges_supported = versions[0] >= Version(4, 3) - without_changes = {Version(4, 2)} - if versions and ranges_supported: - start, end = 0, 0 - first_major = versions[start][0] - minors = [] - for version in versions: - if version[0] == first_major: - minors.append(version[1]) - else: - break - new_versions = [ - Version(first_major, minors), - *(v for v in versions[1:] if v not in without_changes) - ] - else: - new_versions = [v for v in versions if v not in without_changes] - new_versions = [*new_versions[:(limit - 1)], versions[-1]] - return new_versions + # In fact, 4.3 is the fist version to support ranges. However, the range + # support got backported to 4.2. But even if the server is too old to + # have the backport, negotiating BOLT 4.1 is no problem as it's + # equivalent to 4.2 + first_with_range_support = Version(4, 2) + result = [] + for version in versions: + if (result + and version >= first_with_range_support + and result[-1][0] == version[0] + and result[-1][1][1] == version[1] + 1): + # can use range to encompass this version + result[-1][1][1] = version[1] + continue + result.append(Version(version[0], [version[1], version[1]])) + if len(result) == 4: + break + return result @classmethod def get_handshake(cls): @@ -310,26 +310,26 @@ def open(cls, address, *, auth=None, timeout=None, routing_context=None, **pool_ keep_alive=pool_config.keep_alive, ) + # Carry out Bolt subclass imports locally to avoid circular dependency + # issues. if pool_config.protocol_version == (3, 0): - # Carry out Bolt subclass imports locally to avoid circular dependency issues. from neo4j.io._bolt3 import Bolt3 - connection = Bolt3(address, s, pool_config.max_connection_lifetime, auth=auth, user_agent=pool_config.user_agent, routing_context=routing_context) + bolt_cls = Bolt3 elif pool_config.protocol_version == (4, 0): - # Carry out Bolt subclass imports locally to avoid circular dependency issues. from neo4j.io._bolt4 import Bolt4x0 - connection = Bolt4x0(address, s, pool_config.max_connection_lifetime, auth=auth, user_agent=pool_config.user_agent, routing_context=routing_context) + bolt_cls = Bolt4x0 elif pool_config.protocol_version == (4, 1): - # Carry out Bolt subclass imports locally to avoid circular dependency issues. from neo4j.io._bolt4 import Bolt4x1 - connection = Bolt4x1(address, s, pool_config.max_connection_lifetime, auth=auth, user_agent=pool_config.user_agent, routing_context=routing_context) + bolt_cls = Bolt4x1 elif pool_config.protocol_version == (4, 2): - # Carry out Bolt subclass imports locally to avoid circular dependency issues. from neo4j.io._bolt4 import Bolt4x2 - connection = Bolt4x2(address, s, pool_config.max_connection_lifetime, auth=auth, user_agent=pool_config.user_agent, routing_context=routing_context) + bolt_cls = Bolt4x2 elif pool_config.protocol_version == (4, 3): - # Carry out Bolt subclass imports locally to avoid circular dependency issues. from neo4j.io._bolt4 import Bolt4x3 - connection = Bolt4x3(address, s, pool_config.max_connection_lifetime, auth=auth, user_agent=pool_config.user_agent, routing_context=routing_context) + bolt_cls = Bolt4x3 + elif pool_config.protocol_version == (4, 4): + from neo4j.io._bolt4 import Bolt4x4 + bolt_cls = Bolt4x4 else: log.debug("[#%04X] S: ", s.getpeername()[1]) _close_socket(s) @@ -337,6 +337,11 @@ def open(cls, address, *, auth=None, timeout=None, routing_context=None, **pool_ supported_versions = Bolt.protocol_handlers().keys() raise BoltHandshakeError("The Neo4J server does not support communication with this driver. This driver have support for Bolt Protocols {}".format(supported_versions), address=address, request_data=handshake, response_data=data) + connection = bolt_cls( + address, s, pool_config.max_connection_lifetime, auth=auth, + user_agent=pool_config.user_agent, routing_context=routing_context + ) + try: connection.hello() except Exception: diff --git a/neo4j/io/_bolt3.py b/neo4j/io/_bolt3.py index 57edcc242..54215595a 100644 --- a/neo4j/io/_bolt3.py +++ b/neo4j/io/_bolt3.py @@ -55,7 +55,7 @@ class Bolt3(Bolt): """ Protocol handler for Bolt 3. - This is supported by Neo4j versions 3.5, 4.0, 4.1 and 4.2. + This is supported by Neo4j versions 3.5, 4.0, 4.1, 4.2, 4.3, and 4.4. """ PROTOCOL_VERSION = Version(3, 0) diff --git a/neo4j/io/_bolt4.py b/neo4j/io/_bolt4.py index 13470adcb..f32f7fc07 100644 --- a/neo4j/io/_bolt4.py +++ b/neo4j/io/_bolt4.py @@ -316,7 +316,7 @@ def defunct(self): class Bolt4x1(Bolt4x0): """ Protocol handler for Bolt 4.1. - This is supported by Neo4j versions 4.1 and 4.2. + This is supported by Neo4j versions 4.1 - 4.4. """ PROTOCOL_VERSION = Version(4, 1) @@ -338,7 +338,7 @@ def get_base_headers(self): class Bolt4x2(Bolt4x1): """ Protocol handler for Bolt 4.2. - This is supported by Neo4j version 4.2. + This is supported by Neo4j version 4.2 - 4.4. """ PROTOCOL_VERSION = Version(4, 2) @@ -347,7 +347,7 @@ class Bolt4x2(Bolt4x1): class Bolt4x3(Bolt4x2): """ Protocol handler for Bolt 4.3. - This is supported by Neo4j version 4.3. + This is supported by Neo4j version 4.3 - 4.4. """ PROTOCOL_VERSION = Version(4, 3) @@ -376,3 +376,12 @@ def fail(md): self.send_all() self.fetch_all() return [metadata.get("rt")] + + +class Bolt4x4(Bolt4x3): + """ Protocol handler for Bolt 4.4. + + This is supported by Neo4j version 4.4. + """ + + PROTOCOL_VERSION = Version(4, 4) diff --git a/neo4j/meta.py b/neo4j/meta.py index 4492def44..d9b0ae872 100644 --- a/neo4j/meta.py +++ b/neo4j/meta.py @@ -21,7 +21,7 @@ # Can be automatically overridden in builds package = "neo4j" -version = "4.3.dev0" +version = "4.4.dev0" def get_user_agent(): diff --git a/setup.py b/setup.py index c3a4f8a4c..88ce7b95a 100644 --- a/setup.py +++ b/setup.py @@ -33,7 +33,6 @@ "Operating System :: OS Independent", "Topic :: Database", "Topic :: Software Development", - "Programming Language :: Python :: 3.5", "Programming Language :: Python :: 3.6", "Programming Language :: Python :: 3.7", "Programming Language :: Python :: 3.8", diff --git a/testkit/Dockerfile b/testkit/Dockerfile index 504d0e34b..fe8130ae5 100644 --- a/testkit/Dockerfile +++ b/testkit/Dockerfile @@ -40,9 +40,9 @@ ENV PYENV_ROOT /.pyenv ENV PATH $PYENV_ROOT/shims:$PYENV_ROOT/bin:$PATH # Set minimum supported Python version -RUN pyenv install 3.5.9 +RUN pyenv install 3.6.13 RUN pyenv rehash -RUN pyenv global 3.5.9 +RUN pyenv global 3.6.13 # Install Latest pip for each environment # https://pip.pypa.io/en/stable/news/ diff --git a/tests/unit/io/test_class_bolt.py b/tests/unit/io/test_class_bolt.py index 7a27b6fb6..2001546c1 100644 --- a/tests/unit/io/test_class_bolt.py +++ b/tests/unit/io/test_class_bolt.py @@ -28,7 +28,7 @@ def test_class_method_protocol_handlers(): # python -m pytest tests/unit/io/test_class_bolt.py -s -v -k test_class_method_protocol_handlers protocol_handlers = Bolt.protocol_handlers() - assert len(protocol_handlers) == 5 + assert len(protocol_handlers) == 6 @pytest.mark.parametrize( @@ -53,7 +53,7 @@ def test_class_method_protocol_handlers_with_invalid_protocol_version(): def test_class_method_get_handshake(): # python -m pytest tests/unit/io/test_class_bolt.py -s -v -k test_class_method_get_handshake handshake = Bolt.get_handshake() - assert handshake == b"\x00\x03\x03\x04\x00\x00\x01\x04\x00\x00\x00\x04\x00\x00\x00\x03" + assert handshake == b"\x00\x02\x04\x04\x00\x00\x01\x04\x00\x00\x00\x04\x00\x00\x00\x03" def test_magic_preamble(): diff --git a/tox-unit.ini b/tox-unit.ini index 7c0f324cf..8ac52441f 100644 --- a/tox-unit.ini +++ b/tox-unit.ini @@ -1,6 +1,6 @@ [tox] envlist = - py35 + py36 [testenv] deps = diff --git a/tox.ini b/tox.ini index 849b96f89..36eb6506d 100644 --- a/tox.ini +++ b/tox.ini @@ -1,6 +1,5 @@ [tox] envlist = - py35 py36 py37 py38