From ea95023f62b17b31fae43cf1f87752d12809a812 Mon Sep 17 00:00:00 2001 From: Robsdedude Date: Wed, 17 May 2023 16:35:36 +0200 Subject: [PATCH 1/5] Updated examples and docs * Remove 3.5->4.x migration notes * Update examples to use `driver.execute_query` where appropriate * Updated links to external resources --- README.rst | 112 ++++++------------- docs/source/api.rst | 2 +- docs/source/index.rst | 166 ++++++++++++++-------------- src/neo4j/_async/auth_management.py | 6 +- src/neo4j/_auth_management.py | 4 + src/neo4j/_sync/auth_management.py | 6 +- tests/integration/test_readme.py | 61 +++++----- 7 files changed, 160 insertions(+), 197 deletions(-) diff --git a/README.rst b/README.rst index 5f89113b..9efb7fc4 100644 --- a/README.rst +++ b/README.rst @@ -47,99 +47,53 @@ Quick Example .. code-block:: python - from neo4j import GraphDatabase + from neo4j import GraphDatabase, RoutingControl - driver = GraphDatabase.driver("neo4j://localhost:7687", - auth=("neo4j", "password")) - def add_friend(tx, name, friend_name): - tx.run("MERGE (a:Person {name: $name}) " - "MERGE (a)-[:KNOWS]->(friend:Person {name: $friend_name})", - name=name, friend_name=friend_name) + URI = "neo4j://localhost:7687" + AUTH = ("neo4j", "password") - def print_friends(tx, name): - query = ("MATCH (a:Person)-[:KNOWS]->(friend) WHERE a.name = $name " - "RETURN friend.name ORDER BY friend.name") - for record in tx.run(query, name=name): - print(record["friend.name"]) - - with driver.session(database="neo4j") as session: - session.execute_write(add_friend, "Arthur", "Guinevere") - session.execute_write(add_friend, "Arthur", "Lancelot") - session.execute_write(add_friend, "Arthur", "Merlin") - session.execute_read(print_friends, "Arthur") - - driver.close() - - -Connection Settings Breaking Change (4.x) -========================================= - -+ The driver’s default configuration for encrypted is now false - (meaning that driver will only attempt plain text connections by default). - -+ Connections to encrypted services (such as Neo4j Aura) should now explicitly - be set to encrypted. - -+ When encryption is explicitly enabled, the default trust mode is to trust the - CAs that are trusted by operating system and use hostname verification. - -+ This means that encrypted connections to servers holding self-signed - certificates will now fail on certificate verification by default. - -+ Using the new ``neo4j+ssc`` scheme will allow to connect to servers holding self-signed certificates and not use hostname verification. - -+ The ``neo4j://`` scheme replaces ``bolt+routing://`` and can be used for both clustered and single-instance configurations with Neo4j 4.0. - - - -See, https://neo4j.com/docs/migration-guide/4.0/upgrade-driver/#upgrade-driver-breakingchanges + def add_friend(driver, name, friend_name): + driver.execute_query( + "MERGE (a:Person {name: $name}) " + "MERGE (a)-[:KNOWS]->(friend:Person {name: $friend_name})", + name=name, friend_name=friend_name, database_="neo4j", + ) -See, https://neo4j.com/docs/driver-manual/current/client-applications/#driver-connection-uris for changes in default security settings between 3.x and 4.x + def print_friends(driver, name): + records, _, _ = driver.execute_query( + "MATCH (a:Person)-[:KNOWS]->(friend) WHERE a.name = $name " + "RETURN friend.name ORDER BY friend.name", + name=name, database_="neo4j", routing_=RoutingControl.READ, + ) + for record in records: + print(record["friend.name"]) -Connecting with Python Driver 4.x to Neo4j 3.5 (EOL) ----------------------------------------------------- - -Using the Python Driver 4.x and connecting to Neo4j 3.5 with default connection settings for Neo4j 3.5. - -.. code-block:: python - - # the preferred form - - driver = GraphDatabase.driver("neo4j+ssc://localhost:7687", auth=("neo4j", "password")) - - # is equivalent to - - driver = GraphDatabase.driver("neo4j://localhost:7687", auth=("neo4j", "password"), encrypted=True, trust=False) - - -Connecting with Python Driver 1.7 (EOL) to Neo4j 4.x ----------------------------------------------------- - -Using the Python Driver 1.7 and connecting to Neo4j 4.x with default connection settings for Neo4j 4.x. - -.. code-block:: python - driver = GraphDatabase.driver("neo4j://localhost:7687", auth=("neo4j", "password"), encrypted=False) + with GraphDatabase.driver(URI, auth=AUTH) as driver: + add_friend(driver, "Arthur", "Guinevere") + add_friend(driver, "Arthur", "Lancelot") + add_friend(driver, "Arthur", "Merlin") + print_friends(driver, "Arthur") -Other Information -================= +Further Information +=================== -* `The Neo4j Operations Manual`_ -* `The Neo4j Drivers Manual`_ -* `Python Driver API Documentation`_ -* `Neo4j Cypher Refcard`_ -* `Example Project`_ +* `The Neo4j Operations Manual`_ (docs on how to run a Neo4j server) +* `The Neo4j Python Driver Manual`_ (good introduction to this driver) +* `Python Driver API Documentation`_ (full API documentation for this driver) +* `Neo4j Cypher Cheat Sheet`_ (summary of Cypher syntax - Neo4j's graph query language) +* `Example Project`_ (small web application using this driver) * `Driver Wiki`_ (includes change logs) -* `Neo4j 4.0 Migration Guide`_ +* `Neo4j Migration Guide`_ .. _`The Neo4j Operations Manual`: https://neo4j.com/docs/operations-manual/current/ -.. _`The Neo4j Drivers Manual`: https://neo4j.com/docs/driver-manual/current/ +.. _`The Neo4j Python Driver Manual`: https://neo4j.com/docs/python-manual/current/ .. _`Python Driver API Documentation`: https://neo4j.com/docs/api/python-driver/current/ -.. _`Neo4j Cypher Refcard`: https://neo4j.com/docs/cypher-refcard/current/ +.. _`Neo4j Cypher Cheat Sheet`: https://neo4j.com/docs/cypher-cheat-sheet/ .. _`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/ +.. _`Neo4j Migration Guide`: https://neo4j.com/docs/migration-guide/current/ diff --git a/docs/source/api.rst b/docs/source/api.rst index 970d08d2..c9fd9d59 100644 --- a/docs/source/api.rst +++ b/docs/source/api.rst @@ -1030,7 +1030,7 @@ See :class:`.BookmarkManager` for more information. ``auth`` -------- Optional :class:`neo4j.Auth` or ``(user, password)``-tuple. Use this overwrite the -authentication information for the session. +authentication information for the session (user-switching). This requires the server to support re-authentication on the protocol level. You can check this by calling :meth:`.Driver.supports_session_auth` / :meth:`.AsyncDriver.supports_session_auth`. diff --git a/docs/source/index.rst b/docs/source/index.rst index 3e334931..f2c42977 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -69,6 +69,14 @@ To install the latest pre-release, use: python -m pip install --pre neo4j +.. TODO: 7.0 - remove this note + +.. note:: + + ``neo4j-driver`` is the old name for this package. It is now deprecated and + and will receive no further updates starting with 6.0.0. Make sure to + install ``neo4j`` as shown above. + .. note:: It is always recommended to install python packages for user space in a virtual environment. @@ -100,60 +108,39 @@ To deactivate the current active virtual environment, use: Quick Example ************* -Creating nodes and relationships. - .. code-block:: python - from neo4j import GraphDatabase + from neo4j import GraphDatabase, RoutingControl URI = "neo4j://localhost:7687" AUTH = ("neo4j", "password") - def create_person(tx, name): - tx.run("CREATE (a:Person {name: $name})", name=name) + def add_friend(driver, name, friend_name): + driver.execute_query( + "MERGE (a:Person {name: $name}) " + "MERGE (a)-[:KNOWS]->(friend:Person {name: $friend_name})", + name=name, friend_name=friend_name, database_="neo4j", + ) - def create_friend_of(tx, name, friend): - tx.run("MATCH (a:Person) WHERE a.name = $name " - "CREATE (a)-[:KNOWS]->(:Person {name: $friend})", - name=name, friend=friend) + def print_friends(driver, name): + records, _, _ = driver.execute_query( + "MATCH (a:Person)-[:KNOWS]->(friend) WHERE a.name = $name " + "RETURN friend.name ORDER BY friend.name", + name=name, database_="neo4j", routing_=RoutingControl.READ, + ) + for record in records: + print(record["friend.name"]) with GraphDatabase.driver(URI, auth=AUTH) as driver: - with driver.session() as session: - session.execute_write(create_person, "Alice") - session.execute_write(create_friend_of, "Alice", "Bob") - session.execute_write(create_friend_of, "Alice", "Carl") - + add_friend(driver, "Arthur", "Guinevere") + add_friend(driver, "Arthur", "Lancelot") + add_friend(driver, "Arthur", "Merlin") + print_friends(driver, "Arthur") -Finding nodes. - -.. code-block:: python - - from neo4j import GraphDatabase - - - URI = "neo4j://localhost:7687" - AUTH = ("neo4j", "password") - - - def get_friends_of(tx, name): - friends = [] - result = tx.run("MATCH (a:Person)-[:KNOWS]->(f) " - "WHERE a.name = $name " - "RETURN f.name AS friend", name=name) - for record in result: - friends.append(record["friend"]) - return friends - - - with GraphDatabase.driver(URI, auth=AUTH) as driver: - with driver.session() as session: - friends = session.execute_read(get_friends_of, "Alice") - for friend in friends: - print(friend) ******************* @@ -163,78 +150,87 @@ Example Application .. code-block:: python import logging - from neo4j import GraphDatabase - from neo4j.exceptions import ServiceUnavailable + + from neo4j import GraphDatabase, RoutingControl + from neo4j.exceptions import DriverError, Neo4jError class App: - def __init__(self, uri, user, password): + def __init__(self, uri, user, password, database=None): self.driver = GraphDatabase.driver(uri, auth=(user, password)) + self.database = database def close(self): - # Don't forget to close the driver connection when you are finished with it + # Don't forget to close the driver connection when you are finished + # with it self.driver.close() def create_friendship(self, person1_name, person2_name): with self.driver.session() as session: - # Write transactions allow the driver to handle retries and transient errors - result = session.execute_write( - self._create_and_return_friendship, person1_name, person2_name) - for record in result: - print("Created friendship between: {p1}, {p2}".format( - p1=record['p1'], p2=record['p2'])) + # Write transactions allow the driver to handle retries and + # transient errors + result = self._create_and_return_friendship( + person1_name, person2_name + ) + print("Created friendship between: " + f"{result['p1']}, {result['p2']}") - @staticmethod - def _create_and_return_friendship(tx, person1_name, person2_name): + def _create_and_return_friendship(self, person1_name, person2_name): # To learn more about the Cypher syntax, # see https://neo4j.com/docs/cypher-manual/current/ - # The Reference Card is also a good resource for keywords, - # see https://neo4j.com/docs/cypher-refcard/current/ + # The Cheat Sheet is also a good resource for keywords, + # see https://neo4j.com/docs/cypher-cheat-sheet/ query = ( "CREATE (p1:Person { name: $person1_name }) " "CREATE (p2:Person { name: $person2_name }) " "CREATE (p1)-[:KNOWS]->(p2) " - "RETURN p1, p2" + "RETURN p1.name, p2.name" ) - result = tx.run(query, person1_name=person1_name, person2_name=person2_name) try: - return [{"p1": record["p1"]["name"], "p2": record["p2"]["name"]} - for record in result] + record = self.driver.execute_query( + query, person1_name=person1_name, person2_name=person2_name, + database_=self.database, + result_transformer_=lambda r: r.single(strict=True) + ) + return {"p1": record["p1.name"], "p2": record["p2.name"]} # Capture any errors along with the query and data for traceability - except ServiceUnavailable as exception: - logging.error("{query} raised an error: \n {exception}".format( - query=query, exception=exception)) + except (DriverError, Neo4jError) as exception: + logging.error("%s raised an error: \n%s", query, exception) raise def find_person(self, person_name): - with self.driver.session() as session: - result = session.execute_read(self._find_and_return_person, person_name) - for record in result: - print("Found person: {record}".format(record=record)) + names = self._find_and_return_person(person_name) + for name in names: + print(f"Found person: {name}") - @staticmethod - def _find_and_return_person(tx, person_name): + def _find_and_return_person(self, person_name): query = ( "MATCH (p:Person) " "WHERE p.name = $person_name " "RETURN p.name AS name" ) - result = tx.run(query, person_name=person_name) - return [record["name"] for record in result] + names = self.driver.execute_query( + query, person_name=person_name, + database_=self.database, routing_=RoutingControl.READ, + result_transformer_=lambda r: r.value("name") + ) + return names if __name__ == "__main__": - # See https://neo4j.com/developer/aura-connect-driver/ for Aura specific connection URL. + # For Aura specific connection URI, + # see https://neo4j.com/developer/aura-connect-driver/ . scheme = "neo4j" # Connecting to Aura, use the "neo4j+s" URI scheme host_name = "example.com" port = 7687 - url = f"{scheme}://{host_name}:{port}" + uri = f"{scheme}://{host_name}:{port}" user = "" password = "" - app = App(url, user, password) + database = "neo4j" + app = App(uri, user, password, database) try: app.create_friendship("Alice", "David") app.find_person("Alice") @@ -242,20 +238,22 @@ Example Application app.close() -***************** -Other Information -***************** +******************* +Further Information +******************* -* `Neo4j Documentation`_ -* `The Neo4j Drivers Manual`_ -* `Cypher Cheat Sheet`_ -* `Example Project`_ +* `The Neo4j Operations Manual`_ (docs on how to run a Neo4j server) +* `The Neo4j Python Driver Manual`_ (good introduction to this driver) +* `Python Driver API Documentation`_ (full API documentation for this driver) +* `Neo4j Cypher Cheat Sheet`_ (summary of Cypher syntax - Neo4j's graph query language) +* `Example Project`_ (small web application using this driver) * `Driver Wiki`_ (includes change logs) -* `Neo4j Aura`_ +* `Neo4j Migration Guide`_ -.. _`Neo4j Documentation`: https://neo4j.com/docs/ -.. _`The Neo4j Drivers Manual`: https://neo4j.com/docs/driver-manual/current/ -.. _`Cypher Cheat Sheet`: https://neo4j.com/docs/cypher-cheat-sheet/current/ +.. _`The Neo4j Operations Manual`: https://neo4j.com/docs/operations-manual/current/ +.. _`The Neo4j Python Driver Manual`: https://neo4j.com/docs/python-manual/current/ +.. _`Python Driver API Documentation`: https://neo4j.com/docs/api/python-driver/current/ +.. _`Neo4j Cypher Cheat Sheet`: https://neo4j.com/docs/cypher-cheat-sheet/ .. _`Example Project`: https://github.com/neo4j-examples/movies-python-bolt .. _`Driver Wiki`: https://github.com/neo4j/neo4j-python-driver/wiki -.. _`Neo4j Aura`: https://neo4j.com/neo4j-aura/ +.. _`Neo4j Migration Guide`: https://neo4j.com/docs/migration-guide/current/ diff --git a/src/neo4j/_async/auth_management.py b/src/neo4j/_async/auth_management.py index 6fe02d0a..44dbcea6 100644 --- a/src/neo4j/_async/auth_management.py +++ b/src/neo4j/_async/auth_management.py @@ -159,9 +159,11 @@ def expiration_based( The provider function **must not** interact with the driver in any way as this can cause deadlocks and undefined behaviour. - The provider function only ever return auth information belonging - to the same identity. + The provider must function only ever return auth information + belonging to the same identity. Switching identities is undefined behavior. + You may use session-level authentication for such use-cases + :ref:`session-auth-ref`. Example:: diff --git a/src/neo4j/_auth_management.py b/src/neo4j/_auth_management.py index 14ff89db..ce820d2b 100644 --- a/src/neo4j/_auth_management.py +++ b/src/neo4j/_auth_management.py @@ -74,6 +74,8 @@ class AuthManager(metaclass=abc.ABCMeta): The token returned must always belong to the same identity. Switching identities using the `AuthManager` is undefined behavior. + You may use session-level authentication for such use-cases + :ref:`session-auth-ref`. **This is a preview** (see :ref:`filter-warnings-ref`). It might be changed without following the deprecation policy. @@ -96,6 +98,8 @@ def get_auth(self) -> _TAuth: The method must only ever return auth information belonging to the same identity. Switching identities using the `AuthManager` is undefined behavior. + You may use session-level authentication for such use-cases + :ref:`session-auth-ref`. """ ... diff --git a/src/neo4j/_sync/auth_management.py b/src/neo4j/_sync/auth_management.py index 4513860a..ab4247b9 100644 --- a/src/neo4j/_sync/auth_management.py +++ b/src/neo4j/_sync/auth_management.py @@ -159,9 +159,11 @@ def expiration_based( The provider function **must not** interact with the driver in any way as this can cause deadlocks and undefined behaviour. - The provider function only ever return auth information belonging - to the same identity. + The provider must function only ever return auth information + belonging to the same identity. Switching identities is undefined behavior. + You may use session-level authentication for such use-cases + :ref:`session-auth-ref`. Example:: diff --git a/tests/integration/test_readme.py b/tests/integration/test_readme.py index 7bebde9a..6c3983a1 100644 --- a/tests/integration/test_readme.py +++ b/tests/integration/test_readme.py @@ -22,49 +22,52 @@ # python -m pytest tests/integration/test_readme.py -s -v -def _work(tx, query, **params): - tx.run(query, **params).consume() - - def test_should_run_readme(uri, auth): names = set() print = names.add # === START: README === - from neo4j import GraphDatabase + from neo4j import ( + GraphDatabase, + RoutingControl, + ) - driver = GraphDatabase.driver("neo4j://localhost:7687", - auth=("neo4j", "password")) - # === END: README === - driver.close() - driver = GraphDatabase.driver(uri, auth=auth) - # === START: README === - def add_friend(tx, name, friend_name): - tx.run("MERGE (a:Person {name: $name}) " - "MERGE (a)-[:KNOWS]->(friend:Person {name: $friend_name})", - name=name, friend_name=friend_name) + URI = "neo4j://localhost:7687" + AUTH = ("neo4j", "password") + - def print_friends(tx, name): - query = ("MATCH (a:Person)-[:KNOWS]->(friend) WHERE a.name = $name " - "RETURN friend.name ORDER BY friend.name") - for record in tx.run(query, name=name): + def add_friend(driver, name, friend_name): + driver.execute_query( + "MERGE (a:Person {name: $name}) " + "MERGE (a)-[:KNOWS]->(friend:Person {name: $friend_name})", + name=name, friend_name=friend_name, database_="neo4j", + ) + + + def print_friends(driver, name): + records, _, _ = driver.execute_query( + "MATCH (a:Person)-[:KNOWS]->(friend) WHERE a.name = $name " + "RETURN friend.name ORDER BY friend.name", + name=name, database_="neo4j", routing_=RoutingControl.READ, + ) + for record in records: print(record["friend.name"]) - with driver.session(database="neo4j") as session: + + with GraphDatabase.driver(URI, auth=AUTH) as driver: # === END: README === - session.execute_write(_work, "MATCH (a) DETACH DELETE a") + pass + with GraphDatabase.driver(uri, auth=auth) as driver: + driver.execute_query("MATCH (a) DETACH DELETE a") # === START: README === - session.execute_write(add_friend, "Arthur", "Guinevere") - session.execute_write(add_friend, "Arthur", "Lancelot") - session.execute_write(add_friend, "Arthur", "Merlin") - session.execute_read(print_friends, "Arthur") + add_friend(driver, "Arthur", "Guinevere") + add_friend(driver, "Arthur", "Lancelot") + add_friend(driver, "Arthur", "Merlin") + print_friends(driver, "Arthur") # === END: README === - session.execute_write(_work, "MATCH (a) DETACH DELETE a") - # === START: README === + driver.execute_query("MATCH (a) DETACH DELETE a") - driver.close() - # === END: README === assert names == {"Guinevere", "Lancelot", "Merlin"} From 85d1810f4f2c5bb0463e603b1aef9a1267ae0621 Mon Sep 17 00:00:00 2001 From: Robsdedude Date: Wed, 17 May 2023 18:58:21 +0200 Subject: [PATCH 2/5] Fix isort breaking readme integration test --- tests/integration/test_readme.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/tests/integration/test_readme.py b/tests/integration/test_readme.py index 6c3983a1..78bf2b3e 100644 --- a/tests/integration/test_readme.py +++ b/tests/integration/test_readme.py @@ -16,6 +16,7 @@ # limitations under the License. +import re from pathlib import Path @@ -27,10 +28,7 @@ def test_should_run_readme(uri, auth): print = names.add # === START: README === - from neo4j import ( - GraphDatabase, - RoutingControl, - ) + from neo4j import GraphDatabase, RoutingControl # isort:skip URI = "neo4j://localhost:7687" @@ -91,6 +89,7 @@ def test_readme_contains_example(): adding = False continue if adding: + line = re.sub(r"\s+# isort:skip\s+$", "\n", line) stripped_test_content += line assert stripped_test_content in readme_content From a95f8e76d628fac27e54a3a131d9bcba0f1e8dc7 Mon Sep 17 00:00:00 2001 From: Florent Biville Date: Mon, 22 May 2023 17:04:54 +0200 Subject: [PATCH 3/5] Improve query in examples Signed-off-by: Rouven Bauer --- README.rst | 3 ++- docs/source/index.rst | 3 ++- tests/integration/test_readme.py | 3 ++- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/README.rst b/README.rst index 9efb7fc4..dff5cc28 100644 --- a/README.rst +++ b/README.rst @@ -57,7 +57,8 @@ Quick Example def add_friend(driver, name, friend_name): driver.execute_query( "MERGE (a:Person {name: $name}) " - "MERGE (a)-[:KNOWS]->(friend:Person {name: $friend_name})", + "MERGE (friend:Person {name: $friend_name}) " + "MERGE (a)-[:KNOWS]->(friend)", name=name, friend_name=friend_name, database_="neo4j", ) diff --git a/docs/source/index.rst b/docs/source/index.rst index f2c42977..227b0c72 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -120,7 +120,8 @@ Quick Example def add_friend(driver, name, friend_name): driver.execute_query( "MERGE (a:Person {name: $name}) " - "MERGE (a)-[:KNOWS]->(friend:Person {name: $friend_name})", + "MERGE (friend:Person {name: $friend_name}) " + "MERGE (a)-[:KNOWS]->(friend)", name=name, friend_name=friend_name, database_="neo4j", ) diff --git a/tests/integration/test_readme.py b/tests/integration/test_readme.py index 78bf2b3e..30b2bc41 100644 --- a/tests/integration/test_readme.py +++ b/tests/integration/test_readme.py @@ -38,7 +38,8 @@ def test_should_run_readme(uri, auth): def add_friend(driver, name, friend_name): driver.execute_query( "MERGE (a:Person {name: $name}) " - "MERGE (a)-[:KNOWS]->(friend:Person {name: $friend_name})", + "MERGE (friend:Person {name: $friend_name}) " + "MERGE (a)-[:KNOWS]->(friend)", name=name, friend_name=friend_name, database_="neo4j", ) From de1bfa717d7bf940d743604b0ba7207787e6fbaf Mon Sep 17 00:00:00 2001 From: Florent Biville Date: Mon, 22 May 2023 17:06:29 +0200 Subject: [PATCH 4/5] Fix grammar Signed-off-by: Rouven Bauer --- src/neo4j/_async/auth_management.py | 2 +- src/neo4j/_sync/auth_management.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/neo4j/_async/auth_management.py b/src/neo4j/_async/auth_management.py index 44dbcea6..71c382c2 100644 --- a/src/neo4j/_async/auth_management.py +++ b/src/neo4j/_async/auth_management.py @@ -159,7 +159,7 @@ def expiration_based( The provider function **must not** interact with the driver in any way as this can cause deadlocks and undefined behaviour. - The provider must function only ever return auth information + The provider function must only ever return auth information belonging to the same identity. Switching identities is undefined behavior. You may use session-level authentication for such use-cases diff --git a/src/neo4j/_sync/auth_management.py b/src/neo4j/_sync/auth_management.py index ab4247b9..39940847 100644 --- a/src/neo4j/_sync/auth_management.py +++ b/src/neo4j/_sync/auth_management.py @@ -159,7 +159,7 @@ def expiration_based( The provider function **must not** interact with the driver in any way as this can cause deadlocks and undefined behaviour. - The provider must function only ever return auth information + The provider function must only ever return auth information belonging to the same identity. Switching identities is undefined behavior. You may use session-level authentication for such use-cases From 6e23fad44db80009069ba2a21d73371b4922a392 Mon Sep 17 00:00:00 2001 From: Florent Biville Date: Mon, 22 May 2023 17:06:53 +0200 Subject: [PATCH 5/5] Add GraphAcademy to further reading links Signed-off-by: Rouven Bauer --- README.rst | 2 ++ docs/source/index.rst | 2 ++ 2 files changed, 4 insertions(+) diff --git a/README.rst b/README.rst index dff5cc28..2a8ea716 100644 --- a/README.rst +++ b/README.rst @@ -88,6 +88,7 @@ Further Information * `Python Driver API Documentation`_ (full API documentation for this driver) * `Neo4j Cypher Cheat Sheet`_ (summary of Cypher syntax - Neo4j's graph query language) * `Example Project`_ (small web application using this driver) +* `GraphAcademy`_ (interactive, free online trainings for Neo4j) * `Driver Wiki`_ (includes change logs) * `Neo4j Migration Guide`_ @@ -96,5 +97,6 @@ Further Information .. _`Python Driver API Documentation`: https://neo4j.com/docs/api/python-driver/current/ .. _`Neo4j Cypher Cheat Sheet`: https://neo4j.com/docs/cypher-cheat-sheet/ .. _`Example Project`: https://github.com/neo4j-examples/movies-python-bolt +.. _`GraphAcademy`: https://graphacademy.neo4j.com/categories/python/ .. _`Driver Wiki`: https://github.com/neo4j/neo4j-python-driver/wiki .. _`Neo4j Migration Guide`: https://neo4j.com/docs/migration-guide/current/ diff --git a/docs/source/index.rst b/docs/source/index.rst index 227b0c72..79e53a16 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -248,6 +248,7 @@ Further Information * `Python Driver API Documentation`_ (full API documentation for this driver) * `Neo4j Cypher Cheat Sheet`_ (summary of Cypher syntax - Neo4j's graph query language) * `Example Project`_ (small web application using this driver) +* `GraphAcademy`_ (interactive, free online trainings for Neo4j) * `Driver Wiki`_ (includes change logs) * `Neo4j Migration Guide`_ @@ -256,5 +257,6 @@ Further Information .. _`Python Driver API Documentation`: https://neo4j.com/docs/api/python-driver/current/ .. _`Neo4j Cypher Cheat Sheet`: https://neo4j.com/docs/cypher-cheat-sheet/ .. _`Example Project`: https://github.com/neo4j-examples/movies-python-bolt +.. _`GraphAcademy`: https://graphacademy.neo4j.com/categories/python/ .. _`Driver Wiki`: https://github.com/neo4j/neo4j-python-driver/wiki .. _`Neo4j Migration Guide`: https://neo4j.com/docs/migration-guide/current/