gremlinpython 3.7.0__tar.gz → 3.7.2__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 (43) hide show
  1. gremlinpython-3.7.2/NOTICE +5 -0
  2. {gremlinpython-3.7.0/gremlinpython.egg-info → gremlinpython-3.7.2}/PKG-INFO +1 -1
  3. {gremlinpython-3.7.0 → gremlinpython-3.7.2}/gremlin_python/__version__.py +2 -2
  4. {gremlinpython-3.7.0 → gremlinpython-3.7.2}/gremlin_python/driver/aiohttp/transport.py +98 -0
  5. {gremlinpython-3.7.0 → gremlinpython-3.7.2}/gremlin_python/driver/client.py +24 -9
  6. {gremlinpython-3.7.0 → gremlinpython-3.7.2}/gremlin_python/driver/connection.py +4 -2
  7. {gremlinpython-3.7.0 → gremlinpython-3.7.2}/gremlin_python/driver/protocol.py +70 -3
  8. {gremlinpython-3.7.0 → gremlinpython-3.7.2}/gremlin_python/driver/serializer.py +6 -3
  9. {gremlinpython-3.7.0 → gremlinpython-3.7.2}/gremlin_python/driver/useragent.py +1 -1
  10. {gremlinpython-3.7.0 → gremlinpython-3.7.2}/gremlin_python/process/graph_traversal.py +320 -2
  11. gremlinpython-3.7.2/gremlin_python/process/translator.py +297 -0
  12. {gremlinpython-3.7.0 → gremlinpython-3.7.2}/gremlin_python/process/traversal.py +9 -2
  13. {gremlinpython-3.7.0 → gremlinpython-3.7.2}/gremlin_python/structure/io/graphbinaryV1.py +7 -1
  14. {gremlinpython-3.7.0 → gremlinpython-3.7.2}/gremlin_python/structure/io/graphsonV2d0.py +25 -1
  15. {gremlinpython-3.7.0 → gremlinpython-3.7.2}/gremlin_python/structure/io/graphsonV3d0.py +27 -1
  16. {gremlinpython-3.7.0 → gremlinpython-3.7.2/gremlinpython.egg-info}/PKG-INFO +1 -1
  17. gremlinpython-3.7.0/NOTICE +0 -5
  18. gremlinpython-3.7.0/gremlin_python/process/translator.py +0 -175
  19. {gremlinpython-3.7.0 → gremlinpython-3.7.2}/LICENSE +0 -0
  20. {gremlinpython-3.7.0 → gremlinpython-3.7.2}/MANIFEST.in +0 -0
  21. {gremlinpython-3.7.0 → gremlinpython-3.7.2}/README.rst +0 -0
  22. {gremlinpython-3.7.0 → gremlinpython-3.7.2}/gremlin_python/__init__.py +0 -0
  23. {gremlinpython-3.7.0 → gremlinpython-3.7.2}/gremlin_python/driver/__init__.py +0 -0
  24. {gremlinpython-3.7.0 → gremlinpython-3.7.2}/gremlin_python/driver/aiohttp/__init__.py +0 -0
  25. {gremlinpython-3.7.0 → gremlinpython-3.7.2}/gremlin_python/driver/driver_remote_connection.py +0 -0
  26. {gremlinpython-3.7.0 → gremlinpython-3.7.2}/gremlin_python/driver/remote_connection.py +0 -0
  27. {gremlinpython-3.7.0 → gremlinpython-3.7.2}/gremlin_python/driver/request.py +0 -0
  28. {gremlinpython-3.7.0 → gremlinpython-3.7.2}/gremlin_python/driver/resultset.py +0 -0
  29. {gremlinpython-3.7.0 → gremlinpython-3.7.2}/gremlin_python/driver/transport.py +0 -0
  30. {gremlinpython-3.7.0 → gremlinpython-3.7.2}/gremlin_python/process/__init__.py +0 -0
  31. {gremlinpython-3.7.0 → gremlinpython-3.7.2}/gremlin_python/process/anonymous_traversal.py +0 -0
  32. {gremlinpython-3.7.0 → gremlinpython-3.7.2}/gremlin_python/process/strategies.py +0 -0
  33. {gremlinpython-3.7.0 → gremlinpython-3.7.2}/gremlin_python/statics.py +0 -0
  34. {gremlinpython-3.7.0 → gremlinpython-3.7.2}/gremlin_python/structure/__init__.py +0 -0
  35. {gremlinpython-3.7.0 → gremlinpython-3.7.2}/gremlin_python/structure/graph.py +0 -0
  36. {gremlinpython-3.7.0 → gremlinpython-3.7.2}/gremlin_python/structure/io/__init__.py +0 -0
  37. {gremlinpython-3.7.0 → gremlinpython-3.7.2}/gremlin_python/structure/io/util.py +0 -0
  38. {gremlinpython-3.7.0 → gremlinpython-3.7.2}/gremlinpython.egg-info/SOURCES.txt +0 -0
  39. {gremlinpython-3.7.0 → gremlinpython-3.7.2}/gremlinpython.egg-info/dependency_links.txt +0 -0
  40. {gremlinpython-3.7.0 → gremlinpython-3.7.2}/gremlinpython.egg-info/requires.txt +0 -0
  41. {gremlinpython-3.7.0 → gremlinpython-3.7.2}/gremlinpython.egg-info/top_level.txt +0 -0
  42. {gremlinpython-3.7.0 → gremlinpython-3.7.2}/setup.cfg +0 -0
  43. {gremlinpython-3.7.0 → gremlinpython-3.7.2}/setup.py +0 -0
@@ -0,0 +1,5 @@
1
+ Apache TinkerPop
2
+ Copyright 2015-2024 The Apache Software Foundation.
3
+
4
+ This product includes software developed at
5
+ The Apache Software Foundation (http://www.apache.org/).
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: gremlinpython
3
- Version: 3.7.0
3
+ Version: 3.7.2
4
4
  Summary: Gremlin-Python for Apache TinkerPop
5
5
  Home-page: http://tinkerpop.apache.org
6
6
  License: Apache 2
@@ -16,5 +16,5 @@ KIND, either express or implied. See the License for the
16
16
  specific language governing permissions and limitations
17
17
  under the License.
18
18
  '''
19
- version = '3.7.0'
20
- timestamp = 1691178354
19
+ version = '3.7.2'
20
+ timestamp = 1713201447
@@ -138,3 +138,101 @@ class AiohttpTransport(AbstractBaseTransport):
138
138
  def closed(self):
139
139
  # Connection is closed if either the websocket or the client session is closed.
140
140
  return self._websocket.closed or self._client_session.closed
141
+
142
+
143
+ class AiohttpHTTPTransport(AbstractBaseTransport):
144
+ nest_asyncio_applied = False
145
+
146
+ def __init__(self, call_from_event_loop=None, read_timeout=None, write_timeout=None, **kwargs):
147
+ if call_from_event_loop is not None and call_from_event_loop and not AiohttpTransport.nest_asyncio_applied:
148
+ """
149
+ The AiohttpTransport implementation uses the asyncio event loop. Because of this, it cannot be called
150
+ within an event loop without nest_asyncio. If the code is ever refactored so that it can be called
151
+ within an event loop this import and call can be removed. Without this, applications which use the
152
+ event loop to call gremlin-python (such as Jupyter) will not work.
153
+ """
154
+ import nest_asyncio
155
+ nest_asyncio.apply()
156
+ AiohttpTransport.nest_asyncio_applied = True
157
+
158
+ # Start event loop and initialize client session and response to None
159
+ self._loop = asyncio.new_event_loop()
160
+ self._client_session = None
161
+ self._http_req_resp = None
162
+ self._enable_ssl = False
163
+
164
+ # Set all inner variables to parameters passed in.
165
+ self._aiohttp_kwargs = kwargs
166
+ self._write_timeout = write_timeout
167
+ self._read_timeout = read_timeout
168
+ if "ssl_options" in self._aiohttp_kwargs:
169
+ self._ssl_context = self._aiohttp_kwargs.pop("ssl_options")
170
+ self._enable_ssl = True
171
+
172
+ def __del__(self):
173
+ # Close will only actually close if things are left open, so this is safe to call.
174
+ # Clean up any connection resources and close the event loop.
175
+ self.close()
176
+
177
+ def connect(self, url, headers=None):
178
+ # Inner function to perform async connect.
179
+ async def async_connect():
180
+ # Start client session and use it to send all HTTP requests. Base url is the endpoint, headers are set here
181
+ # Base url can only parse basic url with no path, see https://github.com/aio-libs/aiohttp/issues/6647
182
+ if self._enable_ssl:
183
+ # ssl context is established through tcp connector
184
+ tcp_conn = aiohttp.TCPConnector(ssl_context=self._ssl_context)
185
+ self._client_session = aiohttp.ClientSession(connector=tcp_conn,
186
+ base_url=url, headers=headers, loop=self._loop)
187
+ else:
188
+ self._client_session = aiohttp.ClientSession(base_url=url, headers=headers, loop=self._loop)
189
+
190
+ # Execute the async connect synchronously.
191
+ self._loop.run_until_complete(async_connect())
192
+
193
+ def write(self, message):
194
+ # Inner function to perform async write.
195
+ async def async_write():
196
+ basic_auth = None
197
+ # basic password authentication for https connections
198
+ if message['auth']:
199
+ basic_auth = aiohttp.BasicAuth(message['auth']['username'], message['auth']['password'])
200
+ async with async_timeout.timeout(self._write_timeout):
201
+ self._http_req_resp = await self._client_session.post(url="/gremlin",
202
+ auth=basic_auth,
203
+ data=message['payload'],
204
+ headers=message['headers'],
205
+ **self._aiohttp_kwargs)
206
+
207
+ # Execute the async write synchronously.
208
+ self._loop.run_until_complete(async_write())
209
+
210
+ def read(self):
211
+ # Inner function to perform async read.
212
+ async def async_read():
213
+ async with async_timeout.timeout(self._read_timeout):
214
+ return {"content": await self._http_req_resp.read(),
215
+ "ok": self._http_req_resp.ok,
216
+ "status": self._http_req_resp.status}
217
+
218
+ return self._loop.run_until_complete(async_read())
219
+
220
+ def close(self):
221
+ # Inner function to perform async close.
222
+ async def async_close():
223
+ if self._client_session is not None and not self._client_session.closed:
224
+ await self._client_session.close()
225
+ self._client_session = None
226
+
227
+ # If the loop is not closed (connection hasn't already been closed)
228
+ if not self._loop.is_closed():
229
+ # Execute the async close synchronously.
230
+ self._loop.run_until_complete(async_close())
231
+
232
+ # Close the event loop.
233
+ self._loop.close()
234
+
235
+ @property
236
+ def closed(self):
237
+ # Connection is closed when client session is closed.
238
+ return self._client_session.closed
@@ -19,6 +19,7 @@
19
19
  import logging
20
20
  import warnings
21
21
  import queue
22
+ import re
22
23
  from concurrent.futures import ThreadPoolExecutor
23
24
 
24
25
  from gremlin_python.driver import connection, protocol, request, serializer
@@ -45,12 +46,16 @@ class Client:
45
46
  kerberized_service="", headers=None, session=None,
46
47
  enable_user_agent_on_connect=True, **transport_kwargs):
47
48
  log.info("Creating Client with url '%s'", url)
49
+
50
+ # check via url that we are using http protocol
51
+ self._use_http = re.search('^http', url)
52
+
48
53
  self._closed = False
49
54
  self._url = url
50
55
  self._headers = headers
51
56
  self._enable_user_agent_on_connect = enable_user_agent_on_connect
52
57
  self._traversal_source = traversal_source
53
- if "max_content_length" not in transport_kwargs:
58
+ if not self._use_http and "max_content_length" not in transport_kwargs:
54
59
  transport_kwargs["max_content_length"] = 10 * 1024 * 1024
55
60
  if message_serializer is None:
56
61
  message_serializer = serializer.GraphBinarySerializersV1()
@@ -63,21 +68,31 @@ class Client:
63
68
  if transport_factory is None:
64
69
  try:
65
70
  from gremlin_python.driver.aiohttp.transport import (
66
- AiohttpTransport)
71
+ AiohttpTransport, AiohttpHTTPTransport)
67
72
  except ImportError:
68
73
  raise Exception("Please install AIOHTTP or pass "
69
74
  "custom transport factory")
70
75
  else:
71
76
  def transport_factory():
72
- return AiohttpTransport(**transport_kwargs)
77
+ if self._use_http:
78
+ return AiohttpHTTPTransport(**transport_kwargs)
79
+ else:
80
+ return AiohttpTransport(**transport_kwargs)
73
81
  self._transport_factory = transport_factory
74
82
  if protocol_factory is None:
75
- def protocol_factory(): return protocol.GremlinServerWSProtocol(
76
- self._message_serializer,
77
- username=self._username,
78
- password=self._password,
79
- kerberized_service=kerberized_service,
80
- max_content_length=transport_kwargs["max_content_length"])
83
+ def protocol_factory():
84
+ if self._use_http:
85
+ return protocol.GremlinServerHTTPProtocol(
86
+ self._message_serializer,
87
+ username=self._username,
88
+ password=self._password)
89
+ else:
90
+ return protocol.GremlinServerWSProtocol(
91
+ self._message_serializer,
92
+ username=self._username,
93
+ password=self._password,
94
+ kerberized_service=kerberized_service,
95
+ max_content_length=transport_kwargs["max_content_length"])
81
96
  self._protocol_factory = protocol_factory
82
97
  if self._session_enabled:
83
98
  if pool_size is None:
@@ -58,9 +58,11 @@ class Connection:
58
58
  def write(self, request_message):
59
59
  if not self._inited:
60
60
  self.connect()
61
- request_id = str(uuid.uuid4())
62
61
  if request_message.args.get("requestId"):
63
- request_id = request_message.args.get("requestId")
62
+ request_id = str(request_message.args.get("requestId"))
63
+ uuid.UUID(request_id) # Checks for proper UUID or else server will return an error.
64
+ else:
65
+ request_id = str(uuid.uuid4())
64
66
  result_set = resultset.ResultSet(queue.Queue(), request_id)
65
67
  self._results[request_id] = result_set
66
68
  # Create write task
@@ -16,6 +16,7 @@
16
16
  # specific language governing permissions and limitations
17
17
  # under the License.
18
18
  #
19
+ import json
19
20
  import logging
20
21
  import abc
21
22
  import base64
@@ -25,6 +26,8 @@ import struct
25
26
 
26
27
  from gremlin_python.driver import request
27
28
  from gremlin_python.driver.resultset import ResultSet
29
+ from gremlin_python.process.translator import Translator
30
+ from gremlin_python.process.traversal import Bytecode
28
31
 
29
32
  log = logging.getLogger("gremlinpython")
30
33
 
@@ -63,7 +66,6 @@ class AbstractBaseProtocol(metaclass=abc.ABCMeta):
63
66
 
64
67
 
65
68
  class GremlinServerWSProtocol(AbstractBaseProtocol):
66
-
67
69
  QOP_AUTH_BIT = 1
68
70
  _kerberos_context = None
69
71
  _max_content_length = 10 * 1024 * 1024
@@ -133,7 +135,7 @@ class GremlinServerWSProtocol(AbstractBaseProtocol):
133
135
  # This message is going to be huge and kind of hard to read, but in the event of an error,
134
136
  # it can provide invaluable info, so space it out appropriately.
135
137
  log.error("\r\nReceived error message '%s'\r\n\r\nWith results dictionary '%s'",
136
- str(message), str(results_dict))
138
+ str(message), str(results_dict))
137
139
  del results_dict[request_id]
138
140
  raise GremlinServerError(message['status'])
139
141
 
@@ -185,8 +187,73 @@ class GremlinServerWSProtocol(AbstractBaseProtocol):
185
187
  name_length = len(self._username)
186
188
  fmt = '!I' + str(name_length) + 's'
187
189
  word = self.QOP_AUTH_BIT << 24 | self._max_content_length
188
- out = struct.pack(fmt, word, self._username.encode("utf-8"),)
190
+ out = struct.pack(fmt, word, self._username.encode("utf-8"), )
189
191
  encoded = base64.b64encode(out).decode('ascii')
190
192
  kerberos.authGSSClientWrap(self._kerberos_context, encoded)
191
193
  auth = kerberos.authGSSClientResponse(self._kerberos_context)
192
194
  return request.RequestMessage('', 'authentication', {'sasl': auth})
195
+
196
+
197
+ class GremlinServerHTTPProtocol(AbstractBaseProtocol):
198
+
199
+ def __init__(self,
200
+ message_serializer,
201
+ username='', password=''):
202
+ self._message_serializer = message_serializer
203
+ self._username = username
204
+ self._password = password
205
+
206
+ def connection_made(self, transport):
207
+ super(GremlinServerHTTPProtocol, self).connection_made(transport)
208
+
209
+ def write(self, request_id, request_message):
210
+
211
+ basic_auth = {}
212
+ if self._username and self._password:
213
+ basic_auth['username'] = self._username
214
+ basic_auth['password'] = self._password
215
+
216
+ content_type = str(self._message_serializer.version, encoding='utf-8')
217
+ message = {
218
+ 'headers': {'CONTENT-TYPE': content_type,
219
+ 'ACCEPT': content_type},
220
+ 'payload': self._message_serializer.serialize_message(request_id, request_message),
221
+ 'auth': basic_auth
222
+ }
223
+
224
+ self._transport.write(message)
225
+
226
+ def data_received(self, response, results_dict):
227
+ # if Gremlin Server cuts off then we get a None for the message
228
+ if response is None:
229
+ log.error("Received empty message from server.")
230
+ raise GremlinServerError({'code': 500,
231
+ 'message': 'Server disconnected - please try to reconnect', 'attributes': {}})
232
+
233
+ if response['ok']:
234
+ message = self._message_serializer.deserialize_message(response['content'])
235
+ request_id = message['requestId']
236
+ result_set = results_dict[request_id] if request_id in results_dict else ResultSet(None, None)
237
+ status_code = message['status']['code']
238
+ aggregate_to = message['result']['meta'].get('aggregateTo', 'list')
239
+ data = message['result']['data']
240
+ result_set.aggregate_to = aggregate_to
241
+
242
+ if status_code == 204:
243
+ result_set.stream.put_nowait([])
244
+ del results_dict[request_id]
245
+ return status_code
246
+ elif status_code in [200, 206]:
247
+ result_set.stream.put_nowait(data)
248
+ if status_code == 200:
249
+ result_set.status_attributes = message['status']['attributes']
250
+ del results_dict[request_id]
251
+ return status_code
252
+ else:
253
+ # This message is going to be huge and kind of hard to read, but in the event of an error,
254
+ # it can provide invaluable info, so space it out appropriately.
255
+ log.error("\r\nReceived error message '%s'\r\n\r\nWith results dictionary '%s'",
256
+ str(response['content']), str(results_dict))
257
+ body = json.loads(response['content'])
258
+ del results_dict[body['requestId']]
259
+ raise GremlinServerError({'code': response['status'], 'message': body['message'], 'attributes': {}})
@@ -17,6 +17,7 @@
17
17
  # under the License.
18
18
  #
19
19
 
20
+ import base64
20
21
  import logging
21
22
  import struct
22
23
  import uuid
@@ -156,7 +157,8 @@ class GraphSONMessageSerializer(object):
156
157
  return message
157
158
 
158
159
  def deserialize_message(self, message):
159
- msg = json.loads(message.decode('utf-8'))
160
+ # for parsing string message via HTTP connections
161
+ msg = json.loads(message if isinstance(message, str) else message.decode('utf-8'))
160
162
  return self._graphson_reader.to_object(msg)
161
163
 
162
164
 
@@ -238,7 +240,7 @@ class GraphBinarySerializersV1(object):
238
240
  def finalize_message(self, message, mime_len, mime_type):
239
241
  ba = bytearray()
240
242
 
241
- request_id = uuid.UUID(message['requestId'])
243
+ request_id = uuid.UUID(str(message['requestId']))
242
244
  ba.extend(self.header_pack(mime_len, mime_type, 0x81,
243
245
  (request_id.int >> 64) & self.max_int64, request_id.int & self.max_int64))
244
246
 
@@ -268,7 +270,8 @@ class GraphBinarySerializersV1(object):
268
270
  return bytes(ba)
269
271
 
270
272
  def deserialize_message(self, message):
271
- b = io.BytesIO(message)
273
+ # for parsing string message via HTTP connections
274
+ b = io.BytesIO(base64.b64decode(message) if isinstance(message, str) else message)
272
275
 
273
276
  b.read(1) # version
274
277
 
@@ -18,7 +18,7 @@
18
18
  #
19
19
  import platform
20
20
 
21
- gremlin_version = "3.7.0" # DO NOT MODIFY - Configured automatically by Maven Replacer Plugin
21
+ gremlin_version = "3.7.2" # DO NOT MODIFY - Configured automatically by Maven Replacer Plugin
22
22
 
23
23
  def _generate_user_agent():
24
24
  application_name = "NotAvailable"