pyneoinstance 4.0.1__tar.gz → 4.0.3__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (34) hide show
  1. {pyneoinstance-4.0.1/pyneoinstance.egg-info → pyneoinstance-4.0.3}/PKG-INFO +4 -3
  2. {pyneoinstance-4.0.1 → pyneoinstance-4.0.3}/README.md +3 -2
  3. {pyneoinstance-4.0.1 → pyneoinstance-4.0.3}/pyneoinstance/__init__.py +1 -1
  4. {pyneoinstance-4.0.1 → pyneoinstance-4.0.3}/pyneoinstance/database/neo4jdbms.py +30 -9
  5. {pyneoinstance-4.0.1 → pyneoinstance-4.0.3/pyneoinstance.egg-info}/PKG-INFO +4 -3
  6. {pyneoinstance-4.0.1 → pyneoinstance-4.0.3}/pyneoinstance.egg-info/SOURCES.txt +0 -2
  7. {pyneoinstance-4.0.1 → pyneoinstance-4.0.3}/pyproject.toml +1 -1
  8. {pyneoinstance-4.0.1 → pyneoinstance-4.0.3}/test/test_neo4jdb.py +44 -0
  9. pyneoinstance-4.0.1/test/__init__.py +0 -3
  10. pyneoinstance-4.0.1/test/context.py +0 -8
  11. {pyneoinstance-4.0.1 → pyneoinstance-4.0.3}/LICENSE +0 -0
  12. {pyneoinstance-4.0.1 → pyneoinstance-4.0.3}/MANIFEST.in +0 -0
  13. {pyneoinstance-4.0.1 → pyneoinstance-4.0.3}/pyneoinstance/database/__init__.py +0 -0
  14. {pyneoinstance-4.0.1 → pyneoinstance-4.0.3}/pyneoinstance/database/context.py +0 -0
  15. {pyneoinstance-4.0.1 → pyneoinstance-4.0.3}/pyneoinstance/fileload/__init__.py +0 -0
  16. {pyneoinstance-4.0.1 → pyneoinstance-4.0.3}/pyneoinstance/fileload/helpers.py +0 -0
  17. {pyneoinstance-4.0.1 → pyneoinstance-4.0.3}/pyneoinstance/fileload/loadyaml.py +0 -0
  18. {pyneoinstance-4.0.1 → pyneoinstance-4.0.3}/pyneoinstance/py.typed +0 -0
  19. {pyneoinstance-4.0.1 → pyneoinstance-4.0.3}/pyneoinstance/util/__init__.py +0 -0
  20. {pyneoinstance-4.0.1 → pyneoinstance-4.0.3}/pyneoinstance/util/functions.py +0 -0
  21. {pyneoinstance-4.0.1 → pyneoinstance-4.0.3}/pyneoinstance.egg-info/dependency_links.txt +0 -0
  22. {pyneoinstance-4.0.1 → pyneoinstance-4.0.3}/pyneoinstance.egg-info/requires.txt +0 -0
  23. {pyneoinstance-4.0.1 → pyneoinstance-4.0.3}/pyneoinstance.egg-info/top_level.txt +0 -0
  24. {pyneoinstance-4.0.1 → pyneoinstance-4.0.3}/setup.cfg +0 -0
  25. {pyneoinstance-4.0.1 → pyneoinstance-4.0.3}/setup.py +0 -0
  26. {pyneoinstance-4.0.1 → pyneoinstance-4.0.3}/test/age_data.csv +0 -0
  27. {pyneoinstance-4.0.1 → pyneoinstance-4.0.3}/test/cypher_queries.yaml +0 -0
  28. {pyneoinstance-4.0.1 → pyneoinstance-4.0.3}/test/load_files.yaml +0 -0
  29. {pyneoinstance-4.0.1 → pyneoinstance-4.0.3}/test/movies.csv +0 -0
  30. {pyneoinstance-4.0.1 → pyneoinstance-4.0.3}/test/people.csv +0 -0
  31. {pyneoinstance-4.0.1 → pyneoinstance-4.0.3}/test/roles.csv +0 -0
  32. {pyneoinstance-4.0.1 → pyneoinstance-4.0.3}/test/test.py +0 -0
  33. {pyneoinstance-4.0.1 → pyneoinstance-4.0.3}/test/test_fileload.py +0 -0
  34. {pyneoinstance-4.0.1 → pyneoinstance-4.0.3}/test/test_util_functions.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pyneoinstance
3
- Version: 4.0.1
3
+ Version: 4.0.3
4
4
  Summary: Handle Neo4j read and write transactions and provide methods for performing graph EDA
5
5
  Author-email: "Jose A. Alvarado-Guzman" <jose.alvarado-guzman@neo4j.com>
6
6
  License: GNU GENERAL PUBLIC LICENSE
@@ -795,7 +795,7 @@ This YAML file can then be loaded in Python and this information can be retrieve
795
795
  ```python
796
796
  from pyneoinstance import load_yaml_file
797
797
 
798
- config = load_yaml_file(configuration.yaml)
798
+ config = load_yaml_file('configuration.yaml')
799
799
  db_info = config['db_info']
800
800
  graph = Neo4jInstance(db_info['uri'], db_info['user'], db_info['password'])
801
801
  ```
@@ -1038,5 +1038,6 @@ Open `schema.html` in your browser to explore the schema interactively.
1038
1038
  ## More detailed examples
1039
1039
 
1040
1040
  For more detailed examples please refer to the following articles:
1041
- * Data ingestion: [PyNeoInstance: A User-Friendly Python Library for Neo4j](https://medium.com/@a-gilmore/pyneoinstance-a-user-friendly-python-library-for-neo4j-dbefa3117bb2).
1041
+ * Data Ingestion: [PyNeoInstance: A User-Friendly Python Library for Neo4j](https://medium.com/@a-gilmore/pyneoinstance-a-user-friendly-python-library-for-neo4j-dbefa3117bb2).
1042
1042
  * Graph EDA: [Performing graph EDA with the PyNeoInstance Python Package](https://medium.com/@jose.alvarado-guzman/0c0f6f230aa9)
1043
+ * ETL Best Practices: [Python & Neo4j: ETL Best Practices](https://medium.com/@jose.alvarado-guzman/python-neo4j-etl-best-practices-83265ee201d6)
@@ -93,7 +93,7 @@ This YAML file can then be loaded in Python and this information can be retrieve
93
93
  ```python
94
94
  from pyneoinstance import load_yaml_file
95
95
 
96
- config = load_yaml_file(configuration.yaml)
96
+ config = load_yaml_file('configuration.yaml')
97
97
  db_info = config['db_info']
98
98
  graph = Neo4jInstance(db_info['uri'], db_info['user'], db_info['password'])
99
99
  ```
@@ -336,5 +336,6 @@ Open `schema.html` in your browser to explore the schema interactively.
336
336
  ## More detailed examples
337
337
 
338
338
  For more detailed examples please refer to the following articles:
339
- * Data ingestion: [PyNeoInstance: A User-Friendly Python Library for Neo4j](https://medium.com/@a-gilmore/pyneoinstance-a-user-friendly-python-library-for-neo4j-dbefa3117bb2).
339
+ * Data Ingestion: [PyNeoInstance: A User-Friendly Python Library for Neo4j](https://medium.com/@a-gilmore/pyneoinstance-a-user-friendly-python-library-for-neo4j-dbefa3117bb2).
340
340
  * Graph EDA: [Performing graph EDA with the PyNeoInstance Python Package](https://medium.com/@jose.alvarado-guzman/0c0f6f230aa9)
341
+ * ETL Best Practices: [Python & Neo4j: ETL Best Practices](https://medium.com/@jose.alvarado-guzman/python-neo4j-etl-best-practices-83265ee201d6)
@@ -1,5 +1,5 @@
1
1
  from .database.neo4jdbms import Neo4jInstance
2
2
  from .fileload.loadyaml import load_yaml_file
3
3
 
4
- __version__ = '4.0.1'
4
+ __version__ = '4.0.3'
5
5
  __all__ = ['Neo4jInstance', 'load_yaml_file']
@@ -28,6 +28,11 @@ from .context import get_logger, get_batches, get_columns_diff
28
28
 
29
29
  _NODE_LABEL_RE = re.compile(r"\(:?(\w*)\)")
30
30
  _REL_TYPE_RE = re.compile(r"\[:?(\w*)\]")
31
+ _IMPLICIT_TX_RE = re.compile(
32
+ r"\bIN\s+(?:\d+\s+)?CONCURRENT\s+TRANSACTIONS\b"
33
+ r"|\bIN\s+TRANSACTIONS\b",
34
+ re.IGNORECASE,
35
+ )
31
36
 
32
37
  def _get_driver(neo_info : Dict[str, str]):
33
38
  try:
@@ -196,8 +201,14 @@ class Neo4jInstance:
196
201
  params = parameters or {}
197
202
  with _get_session(self._driver, database) as session:
198
203
  try:
199
- result = session.execute_read(
200
- _read_transaction_function,query=query,**params)
204
+ if _IMPLICIT_TX_RE.search(query):
205
+ # 'CALL { ... } IN TRANSACTIONS' is only valid in an implicit
206
+ # (auto-commit) transaction, so it can't go through execute_read.
207
+ read_result = session.run(query, **params)
208
+ result = DataFrame(read_result.values(), columns=read_result.keys())
209
+ else:
210
+ result = session.execute_read(
211
+ _read_transaction_function,query=query,**params)
201
212
  except ServiceUnavailable as exception:
202
213
  raise ServiceUnavailable() from exception
203
214
  except ClientError as exception:
@@ -242,8 +253,15 @@ class Neo4jInstance:
242
253
  params = parameters or {}
243
254
  with _get_session(self._driver, database) as session:
244
255
  try:
245
- graph = session.execute_read(
246
- _graph_transaction_function, query=query, **params)
256
+ if _IMPLICIT_TX_RE.search(query):
257
+ # 'CALL { ... } IN TRANSACTIONS' is only valid in an implicit
258
+ # (auto-commit) transaction, so it can't go through execute_read.
259
+ graph_result = session.run(query, **params)
260
+ list(graph_result) # consume all records so .graph() is populated
261
+ graph = graph_result.graph()
262
+ else:
263
+ graph = session.execute_read(
264
+ _graph_transaction_function, query=query, **params)
247
265
  except ServiceUnavailable as exception:
248
266
  raise ServiceUnavailable() from exception
249
267
  except ClientError as exception:
@@ -750,15 +768,18 @@ class Neo4jInstance:
750
768
  parameters: Optional[Dict[str, Any]] = None
751
769
  ):
752
770
  params = parameters or {}
771
+ kwargs = dict(params)
772
+ if rows is not None:
773
+ kwargs['rows'] = rows
753
774
  try:
754
- if rows is not None:
755
- results = session.execute_write(
756
- _write_transaction_function, query,
757
- rows = rows, **params).counters.__dict__
775
+ if _IMPLICIT_TX_RE.search(query):
776
+ # 'CALL { ... } IN TRANSACTIONS' is only valid in an implicit
777
+ # (auto-commit) transaction, so it can't go through execute_write.
778
+ results = session.run(query, **kwargs).consume().counters.__dict__
758
779
  else:
759
780
  results = session.execute_write(
760
781
  _write_transaction_function, query,
761
- **params).counters.__dict__
782
+ **kwargs).counters.__dict__
762
783
  except ServiceUnavailable as exception:
763
784
  raise ServiceUnavailable() from exception
764
785
  except ClientError as exception:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pyneoinstance
3
- Version: 4.0.1
3
+ Version: 4.0.3
4
4
  Summary: Handle Neo4j read and write transactions and provide methods for performing graph EDA
5
5
  Author-email: "Jose A. Alvarado-Guzman" <jose.alvarado-guzman@neo4j.com>
6
6
  License: GNU GENERAL PUBLIC LICENSE
@@ -795,7 +795,7 @@ This YAML file can then be loaded in Python and this information can be retrieve
795
795
  ```python
796
796
  from pyneoinstance import load_yaml_file
797
797
 
798
- config = load_yaml_file(configuration.yaml)
798
+ config = load_yaml_file('configuration.yaml')
799
799
  db_info = config['db_info']
800
800
  graph = Neo4jInstance(db_info['uri'], db_info['user'], db_info['password'])
801
801
  ```
@@ -1038,5 +1038,6 @@ Open `schema.html` in your browser to explore the schema interactively.
1038
1038
  ## More detailed examples
1039
1039
 
1040
1040
  For more detailed examples please refer to the following articles:
1041
- * Data ingestion: [PyNeoInstance: A User-Friendly Python Library for Neo4j](https://medium.com/@a-gilmore/pyneoinstance-a-user-friendly-python-library-for-neo4j-dbefa3117bb2).
1041
+ * Data Ingestion: [PyNeoInstance: A User-Friendly Python Library for Neo4j](https://medium.com/@a-gilmore/pyneoinstance-a-user-friendly-python-library-for-neo4j-dbefa3117bb2).
1042
1042
  * Graph EDA: [Performing graph EDA with the PyNeoInstance Python Package](https://medium.com/@jose.alvarado-guzman/0c0f6f230aa9)
1043
+ * ETL Best Practices: [Python & Neo4j: ETL Best Practices](https://medium.com/@jose.alvarado-guzman/python-neo4j-etl-best-practices-83265ee201d6)
@@ -18,9 +18,7 @@ pyneoinstance/fileload/helpers.py
18
18
  pyneoinstance/fileload/loadyaml.py
19
19
  pyneoinstance/util/__init__.py
20
20
  pyneoinstance/util/functions.py
21
- test/__init__.py
22
21
  test/age_data.csv
23
- test/context.py
24
22
  test/cypher_queries.yaml
25
23
  test/load_files.yaml
26
24
  test/movies.csv
@@ -6,7 +6,7 @@ build-backend = "setuptools.build_meta"
6
6
 
7
7
  [project]
8
8
  name = "pyneoinstance"
9
- version = "4.0.1"
9
+ version = "4.0.3"
10
10
  description = "Handle Neo4j read and write transactions and provide methods for performing graph EDA"
11
11
  readme = "README.md"
12
12
  authors = [{ name = "Jose A. Alvarado-Guzman",email = "jose.alvarado-guzman@neo4j.com" }]
@@ -97,6 +97,34 @@ class TestWriteMethods(unittest.TestCase):
97
97
  self.assertEqual(
98
98
  {'nodes_created': 1, 'labels_added': 2, 'properties_set': 4}, result)
99
99
 
100
+ def test_execute_write_query_in_transactions(self):
101
+ """Test a query with 'CALL { ... } IN TRANSACTIONS', which requires
102
+ an implicit transaction rather than the managed transaction used by
103
+ execute_write_query for regular queries."""
104
+ query = """
105
+ UNWIND range(1, 3) AS i
106
+ CALL (i) {
107
+ CREATE (:Actor {id: i})
108
+ } IN TRANSACTIONS OF 1 ROWS
109
+ """
110
+ result = self.graph.execute_write_query(query, self.db)
111
+ self.assertEqual(
112
+ {'nodes_created': 3, 'labels_added': 3, 'properties_set': 3}, result)
113
+
114
+ def test_execute_write_query_in_concurrent_transactions(self):
115
+ """Test a query with 'CALL { ... } IN CONCURRENT TRANSACTIONS', which
116
+ also requires an implicit transaction rather than the managed
117
+ transaction used by execute_write_query for regular queries."""
118
+ query = """
119
+ UNWIND range(1, 3) AS i
120
+ CALL (i) {
121
+ CREATE (:Actor {id: i})
122
+ } IN 2 CONCURRENT TRANSACTIONS OF 1 ROWS
123
+ """
124
+ result = self.graph.execute_write_query(query, self.db)
125
+ self.assertEqual(
126
+ {'nodes_created': 3, 'labels_added': 3, 'properties_set': 3}, result)
127
+
100
128
 
101
129
  class TestReadAndEdaMethods(unittest.TestCase):
102
130
  """Test read and EDA methods. Data is loaded once for the entire class."""
@@ -159,6 +187,22 @@ class TestReadAndEdaMethods(unittest.TestCase):
159
187
  query, self.db, {'movie_name': movie})
160
188
  self.assertEqual(movie, result['movie'][0])
161
189
 
190
+ def test_execute_read_query_in_transactions(self):
191
+ """Test a read query with 'CALL { ... } IN TRANSACTIONS', which requires
192
+ an implicit transaction rather than the managed transaction used by
193
+ execute_read_query for regular queries."""
194
+ plain_count = self.graph.execute_read_query(
195
+ "MATCH (p:Person) RETURN count(p) AS row_num", self.db)['row_num'][0]
196
+ query = """
197
+ MATCH (p:Person)
198
+ CALL (p) {
199
+ RETURN p AS person
200
+ } IN TRANSACTIONS OF 1000 ROWS
201
+ RETURN count(person) AS row_num
202
+ """
203
+ result = self.graph.execute_read_query(query, self.db)
204
+ self.assertEqual(plain_count, result['row_num'][0])
205
+
162
206
  def test_get_node_label_freq(self):
163
207
  """Test node label frequency EDA."""
164
208
  result = self.graph.get_node_label_freq(self.db)
@@ -1,3 +0,0 @@
1
- import os
2
-
3
- test_dir = os.path.abspath(os.path.dirname(__file__))
@@ -1,8 +0,0 @@
1
- """Re-exports package modules for use by the test suite."""
2
- import os
3
- import sys
4
- sys.path.insert(0, os.path.abspath(
5
- os.path.join(os.path.dirname(__file__), '..')))
6
- from pyneoinstance import fileload
7
- from pyneoinstance import util
8
- from pyneoinstance import database
File without changes
File without changes
File without changes
File without changes