gremlinpython 3.6.5__tar.gz → 3.6.6__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 (42) hide show
  1. {gremlinpython-3.6.5/gremlinpython.egg-info → gremlinpython-3.6.6}/PKG-INFO +1 -1
  2. {gremlinpython-3.6.5 → gremlinpython-3.6.6}/gremlin_python/__version__.py +2 -2
  3. {gremlinpython-3.6.5 → gremlinpython-3.6.6}/gremlin_python/driver/aiohttp/transport.py +96 -0
  4. {gremlinpython-3.6.5 → gremlinpython-3.6.6}/gremlin_python/driver/client.py +24 -9
  5. {gremlinpython-3.6.5 → gremlinpython-3.6.6}/gremlin_python/driver/connection.py +1 -0
  6. {gremlinpython-3.6.5 → gremlinpython-3.6.6}/gremlin_python/driver/protocol.py +68 -3
  7. {gremlinpython-3.6.5 → gremlinpython-3.6.6}/gremlin_python/driver/serializer.py +5 -2
  8. {gremlinpython-3.6.5 → gremlinpython-3.6.6}/gremlin_python/driver/useragent.py +1 -1
  9. gremlinpython-3.6.6/gremlin_python/process/translator.py +297 -0
  10. {gremlinpython-3.6.5 → gremlinpython-3.6.6/gremlinpython.egg-info}/PKG-INFO +1 -1
  11. gremlinpython-3.6.5/gremlin_python/process/translator.py +0 -175
  12. {gremlinpython-3.6.5 → gremlinpython-3.6.6}/LICENSE +0 -0
  13. {gremlinpython-3.6.5 → gremlinpython-3.6.6}/MANIFEST.in +0 -0
  14. {gremlinpython-3.6.5 → gremlinpython-3.6.6}/NOTICE +0 -0
  15. {gremlinpython-3.6.5 → gremlinpython-3.6.6}/README.rst +0 -0
  16. {gremlinpython-3.6.5 → gremlinpython-3.6.6}/gremlin_python/__init__.py +0 -0
  17. {gremlinpython-3.6.5 → gremlinpython-3.6.6}/gremlin_python/driver/__init__.py +0 -0
  18. {gremlinpython-3.6.5 → gremlinpython-3.6.6}/gremlin_python/driver/aiohttp/__init__.py +0 -0
  19. {gremlinpython-3.6.5 → gremlinpython-3.6.6}/gremlin_python/driver/driver_remote_connection.py +0 -0
  20. {gremlinpython-3.6.5 → gremlinpython-3.6.6}/gremlin_python/driver/remote_connection.py +0 -0
  21. {gremlinpython-3.6.5 → gremlinpython-3.6.6}/gremlin_python/driver/request.py +0 -0
  22. {gremlinpython-3.6.5 → gremlinpython-3.6.6}/gremlin_python/driver/resultset.py +0 -0
  23. {gremlinpython-3.6.5 → gremlinpython-3.6.6}/gremlin_python/driver/transport.py +0 -0
  24. {gremlinpython-3.6.5 → gremlinpython-3.6.6}/gremlin_python/process/__init__.py +0 -0
  25. {gremlinpython-3.6.5 → gremlinpython-3.6.6}/gremlin_python/process/anonymous_traversal.py +0 -0
  26. {gremlinpython-3.6.5 → gremlinpython-3.6.6}/gremlin_python/process/graph_traversal.py +0 -0
  27. {gremlinpython-3.6.5 → gremlinpython-3.6.6}/gremlin_python/process/strategies.py +0 -0
  28. {gremlinpython-3.6.5 → gremlinpython-3.6.6}/gremlin_python/process/traversal.py +0 -0
  29. {gremlinpython-3.6.5 → gremlinpython-3.6.6}/gremlin_python/statics.py +0 -0
  30. {gremlinpython-3.6.5 → gremlinpython-3.6.6}/gremlin_python/structure/__init__.py +0 -0
  31. {gremlinpython-3.6.5 → gremlinpython-3.6.6}/gremlin_python/structure/graph.py +0 -0
  32. {gremlinpython-3.6.5 → gremlinpython-3.6.6}/gremlin_python/structure/io/__init__.py +0 -0
  33. {gremlinpython-3.6.5 → gremlinpython-3.6.6}/gremlin_python/structure/io/graphbinaryV1.py +0 -0
  34. {gremlinpython-3.6.5 → gremlinpython-3.6.6}/gremlin_python/structure/io/graphsonV2d0.py +0 -0
  35. {gremlinpython-3.6.5 → gremlinpython-3.6.6}/gremlin_python/structure/io/graphsonV3d0.py +0 -0
  36. {gremlinpython-3.6.5 → gremlinpython-3.6.6}/gremlin_python/structure/io/util.py +0 -0
  37. {gremlinpython-3.6.5 → gremlinpython-3.6.6}/gremlinpython.egg-info/SOURCES.txt +0 -0
  38. {gremlinpython-3.6.5 → gremlinpython-3.6.6}/gremlinpython.egg-info/dependency_links.txt +0 -0
  39. {gremlinpython-3.6.5 → gremlinpython-3.6.6}/gremlinpython.egg-info/requires.txt +0 -0
  40. {gremlinpython-3.6.5 → gremlinpython-3.6.6}/gremlinpython.egg-info/top_level.txt +0 -0
  41. {gremlinpython-3.6.5 → gremlinpython-3.6.6}/setup.cfg +0 -0
  42. {gremlinpython-3.6.5 → gremlinpython-3.6.6}/setup.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: gremlinpython
3
- Version: 3.6.5
3
+ Version: 3.6.6
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.6.5'
20
- timestamp = 1691177118
19
+ version = '3.6.6'
20
+ timestamp = 1701718721
@@ -138,3 +138,99 @@ 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 await self._http_req_resp.read()
215
+
216
+ return self._loop.run_until_complete(async_read())
217
+
218
+ def close(self):
219
+ # Inner function to perform async close.
220
+ async def async_close():
221
+ if self._client_session is not None and not self._client_session.closed:
222
+ await self._client_session.close()
223
+ self._client_session = None
224
+
225
+ # If the loop is not closed (connection hasn't already been closed)
226
+ if not self._loop.is_closed():
227
+ # Execute the async close synchronously.
228
+ self._loop.run_until_complete(async_close())
229
+
230
+ # Close the event loop.
231
+ self._loop.close()
232
+
233
+ @property
234
+ def closed(self):
235
+ # Connection is closed when client session is closed.
236
+ 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:
@@ -61,6 +61,7 @@ class Connection:
61
61
  request_id = str(uuid.uuid4())
62
62
  if request_message.args.get("requestId"):
63
63
  request_id = request_message.args.get("requestId")
64
+ uuid.UUID(request_id) # Checks for proper UUID or else server will return an error.
64
65
  result_set = resultset.ResultSet(queue.Queue(), request_id)
65
66
  self._results[request_id] = result_set
66
67
  # 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,71 @@ 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, message, results_dict):
227
+ # if Gremlin Server cuts off then we get a None for the message
228
+ if message 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
+ message = self._message_serializer.deserialize_message(message)
234
+ request_id = message['requestId']
235
+ result_set = results_dict[request_id] if request_id in results_dict else ResultSet(None, None)
236
+ status_code = message['status']['code']
237
+ aggregate_to = message['result']['meta'].get('aggregateTo', 'list')
238
+ data = message['result']['data']
239
+ result_set.aggregate_to = aggregate_to
240
+
241
+ if status_code == 204:
242
+ result_set.stream.put_nowait([])
243
+ del results_dict[request_id]
244
+ return status_code
245
+ elif status_code in [200, 206]:
246
+ result_set.stream.put_nowait(data)
247
+ if status_code == 200:
248
+ result_set.status_attributes = message['status']['attributes']
249
+ del results_dict[request_id]
250
+ return status_code
251
+ else:
252
+ # This message is going to be huge and kind of hard to read, but in the event of an error,
253
+ # it can provide invaluable info, so space it out appropriately.
254
+ log.error("\r\nReceived error message '%s'\r\n\r\nWith results dictionary '%s'",
255
+ str(message), str(results_dict))
256
+ del results_dict[request_id]
257
+ raise GremlinServerError(message['status'])
@@ -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
 
@@ -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.6.5" # DO NOT MODIFY - Configured automatically by Maven Replacer Plugin
21
+ gremlin_version = "3.6.6" # DO NOT MODIFY - Configured automatically by Maven Replacer Plugin
22
22
 
23
23
  def _generate_user_agent():
24
24
  application_name = "NotAvailable"
@@ -0,0 +1,297 @@
1
+ #
2
+ # Licensed to the Apache Software Foundation (ASF) under one
3
+ # or more contributor license agreements. See the NOTICE file
4
+ # distributed with this work for additional information
5
+ # regarding copyright ownership. The ASF licenses this file
6
+ # to you under the Apache License, Version 2.0 (the
7
+ # "License"); you may not use this file except in compliance
8
+ # with the License. You may obtain a copy of the License at
9
+ #
10
+ # http://www.apache.org/licenses/LICENSE-2.0
11
+ #
12
+ # Unless required by applicable law or agreed to in writing,
13
+ # software distributed under the License is distributed on an
14
+ # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15
+ # KIND, either express or implied. See the License for the
16
+ # specific language governing permissions and limitations
17
+ # under the License.
18
+ #
19
+
20
+ """
21
+ Class that can turn traversals back into Gremlin Groovy format text queries.
22
+ Those queries can then be run in the Gremlin console or using the GLV submit(<String>) API or
23
+ sent to any TinkerPop compliant HTTP endpoint.
24
+ """
25
+ __author__ = 'Kelvin R. Lawrence (gfxman)'
26
+
27
+ import math
28
+ import numbers
29
+ import re
30
+
31
+ from gremlin_python.process.traversal import *
32
+ from gremlin_python.process.strategies import *
33
+ from gremlin_python.structure.graph import Vertex, Edge, VertexProperty
34
+ from datetime import datetime
35
+
36
+
37
+ class Translator:
38
+ """
39
+ Turn a bytecode object back into a textual query (Gremlin Groovy script).
40
+ """
41
+
42
+ # Dictionary used to reverse-map token IDs to strings
43
+ options = {
44
+ WithOptions.tokens: 'tokens',
45
+ WithOptions.none: 'none',
46
+ WithOptions.ids: 'ids',
47
+ WithOptions.labels: 'labels',
48
+ WithOptions.keys: 'keys',
49
+ WithOptions.values: 'values',
50
+ WithOptions.all: 'all',
51
+ WithOptions.indexer: 'indexer',
52
+ WithOptions.list: 'list',
53
+ WithOptions.map: 'map'
54
+ }
55
+
56
+ conn_p = ['and', 'or']
57
+
58
+ def __init__(self, traversal_source=None):
59
+ self.traversal_source = traversal_source
60
+
61
+ def get_traversal_source(self):
62
+ return self.traversal_source
63
+
64
+ def get_target_language(self):
65
+ return "gremlin-groovy"
66
+
67
+ def of(self, traversal_source):
68
+ self.traversal_source = traversal_source
69
+ return self
70
+
71
+ # Do any needed special processing for the representation
72
+ # of strings and dates and boolean.
73
+ def fixup(self, v):
74
+ if isinstance(v, str):
75
+ return f'{v!r}' # use repr() format for canonical string rep
76
+ elif type(v) == datetime:
77
+ return self.process_date(v)
78
+ elif type(v) == bool:
79
+ return 'true' if v else 'false'
80
+ elif isinstance(v, numbers.Number):
81
+ return self.process_number(v)
82
+ elif isinstance(v, set):
83
+ return f'[{str(v)[1:-1]}]'
84
+ elif isinstance(v, P):
85
+ return self.process_predicate(v)
86
+ elif type(v) == Vertex:
87
+ return self.process_vertex(v)
88
+ elif type(v) == Edge:
89
+ return self.process_edge(v)
90
+ elif type(v) in [Merge]: # on_create on_match out_v in_v
91
+ tmp = str(v)
92
+ return f'{tmp.split("_")[0]}{tmp.split("_")[1].capitalize()}' if tmp.find('_') else tmp
93
+ elif v is None:
94
+ return 'null'
95
+ else:
96
+ return str(v)
97
+
98
+ # Turn a Python datetime into the equivalent new Date(...)
99
+ def process_date(self, date):
100
+ y = date.year - 1900
101
+ mo = date.month
102
+ d = date.day
103
+ h = date.hour
104
+ mi = date.minute
105
+ s = date.second
106
+ return f'new Date({y},{mo},{d},{h},{mi},{s})'
107
+
108
+ # Do special processing needed to format predicates that come in
109
+ # such as "gt(a)" correctly.
110
+ def process_predicate(self, p):
111
+ res = ''
112
+ if p.operator in self.conn_p:
113
+ res += f'{self.process_predicate(p.value)}.{p.operator}({self.process_predicate(p.other)})'
114
+ else:
115
+ res += f'{self.process_p_value(p)}'
116
+ return res
117
+
118
+ # process the value of the predicates
119
+ def process_p_value(self, p):
120
+ res = str(p).split('(')[0] + '('
121
+ if type(p.value) == list:
122
+ res += '['
123
+ for v in p.value:
124
+ res += self.fixup(v) + ','
125
+ res = (res[0:-1] + ']') if len(p.value) > 0 else (res + ']')
126
+ else:
127
+ res += self.fixup(p.value)
128
+ if p.other is not None:
129
+ res += f',{self.fixup(p.other)}'
130
+ res += ')'
131
+ return res
132
+
133
+ # Special processing to handle strategies
134
+ def process_strategy(self, s):
135
+ c = 0
136
+ res = ''
137
+ # if parameter is empty, only pass class name (referenced GroovyTranslator.java)
138
+ if not s.configuration:
139
+ res += s.strategy_name
140
+ else:
141
+ res = f'new {str(s)}('
142
+ for key in s.configuration:
143
+ res += ',' if c > 0 else ''
144
+ res += key + ':'
145
+ val = s.configuration[key]
146
+ if isinstance(val, Traversal):
147
+ res += self.translate(val.bytecode, child=True)
148
+ else:
149
+ res += self.fixup(val)
150
+ c += 1
151
+ res += ')'
152
+ return res
153
+ pass
154
+
155
+ # Special processing to handle vertices
156
+ def process_vertex(self, vertex):
157
+ return f'new ReferenceVertex({self.fixup(vertex.id)},\'{vertex.label}\')'
158
+
159
+ # Special processing to handle edges
160
+ def process_edge(self, edge):
161
+ return f'new ReferenceEdge({str(edge.id)},\'{edge.label}\',' \
162
+ f'new ReferenceVertex({str(edge.inV.id)},\'{edge.inV.label}\'),' \
163
+ f'new ReferenceVertex({str(edge.outV.id)},\'{edge.outV.label}\'))'
164
+
165
+ # Special processing to handle vertex property
166
+ def process_vertex_property(self, vp):
167
+ return f'new ReferenceVertexProperty({str(vp.id)},\'{vp.label}\',{self.fixup(vp.value)})'
168
+
169
+ # Special processing to handle lambda
170
+ def process_lambda(self, lam):
171
+ lambda_result = lam()
172
+ script = lambda_result if isinstance(lambda_result, str) else lambda_result[0]
173
+ return f'{script}' if re.match(r"^\{.*\}$", script, flags=re.DOTALL) else f'{{{script}}}'
174
+
175
+ def process_dict(self, d):
176
+ c = 0
177
+ res = '['
178
+ if len(d) == 0:
179
+ res += ':'
180
+ else:
181
+ for k, v in d.items():
182
+ wrap = not isinstance(k, str)
183
+ res += ',' if c > 0 else ''
184
+ res += '(' if wrap else ''
185
+ res += self.fixup(k)
186
+ res += ')' if wrap else ''
187
+ res += f':{self.fixup(v)}'
188
+ c += 1
189
+ res += ']'
190
+ return res
191
+
192
+ def process_number(self, n):
193
+ if isinstance(n, float):
194
+ # converting floats into doubles for script since python doesn't distinguish and java defaults to double
195
+ if math.isnan(n):
196
+ return "Double.NaN"
197
+ elif math.isinf(n) and n > 0:
198
+ return "Double.POSITIVE_INFINITY"
199
+ elif math.isinf(n) and n < 0:
200
+ return "Double.NEGATIVE_INFINITY"
201
+ else:
202
+ return f'{n}d'
203
+ return f'{n}'
204
+
205
+ def process_list(self, l):
206
+ c = 0
207
+ res = '['
208
+ for i in l:
209
+ res += ',' if c > 0 else ''
210
+ res += self.fixup(i)
211
+ c += 1
212
+ res += ']'
213
+ return res
214
+
215
+ # Special processing to handle bindings inside of traversals
216
+ def process_binding(self, binding):
217
+ return f'Bindings.instance().of(\'{binding.key}\', {str(binding.value)})'
218
+
219
+ # Main driver of the translation. Different parts of
220
+ # a Traversal are handled appropriately.
221
+ def do_translation(self, step):
222
+ script = ''
223
+ params = step[1:]
224
+ script += '.' + step[0] + '('
225
+ if len(params) > 0:
226
+ c = 0
227
+ with_opts = False
228
+ is_merge_op = (step[0] == 'mergeV') or (step[0] == 'mergeE')
229
+ for p in params:
230
+ script += ',' if c > 0 else ''
231
+ if with_opts:
232
+ script += f'WithOptions.{self.options[p]}'
233
+ elif type(p) == Bytecode:
234
+ script += self.translate(p, True)
235
+ elif isinstance(p, P):
236
+ script += self.process_predicate(p)
237
+ elif type(p) == Vertex:
238
+ script += self.process_vertex(p)
239
+ elif type(p) == Edge:
240
+ script += self.process_edge(p)
241
+ elif type(p) == VertexProperty:
242
+ script += self.process_vertex_property(p)
243
+ elif type(p) in [Cardinality, Pop, Operator, Scope, T]:
244
+ tmp = str(p)
245
+ script += tmp[0:-1] if tmp.endswith('_') else tmp
246
+ elif type(p) in [Merge]: # on_create on_match out_v in_v
247
+ is_merge_op = True
248
+ tmp = str(p)
249
+ script += f'{tmp.split("_")[0]}{tmp.split("_")[1].capitalize()}' if tmp.find('_') else tmp
250
+ elif isinstance(p, TraversalStrategy): # this will capture all strategies
251
+ script += self.process_strategy(p)
252
+ elif type(p) == datetime:
253
+ script += self.process_date(p)
254
+ elif p == WithOptions.tokens:
255
+ script += 'WithOptions.tokens'
256
+ with_opts = True
257
+ elif isinstance(p, str):
258
+ script += f'{p!r}' # use repr() format for canonical string rep
259
+ elif type(p) == bool:
260
+ script += 'true' if p else 'false'
261
+ elif isinstance(p, type(lambda: None)) and p.__name__ == (lambda: None).__name__:
262
+ script += self.process_lambda(p)
263
+ elif type(p) == Binding:
264
+ script += self.process_binding(p)
265
+ elif p is None:
266
+ script += '(Traversal) null' if is_merge_op else 'null'
267
+ elif isinstance(p, type):
268
+ script += p.__name__
269
+ elif isinstance(p, dict):
270
+ script += self.process_dict(p)
271
+ elif isinstance(p, numbers.Number):
272
+ script += self.process_number(p)
273
+ elif isinstance(p, set):
274
+ script += f'[{str(p)[1:-1]}] as Set' if len(p) > 0 else '[] as Set'
275
+ elif isinstance(p, list):
276
+ script += self.process_list(p)
277
+ else:
278
+ script += str(p)
279
+ c += 1
280
+ script += ')'
281
+ return script
282
+
283
+ # Translation starts here. There are two main parts to a
284
+ # traversal. Source instructions such as "withSideEffect"
285
+ # and "withStrategies", and step instructions such as
286
+ # "addV" and "repeat". If child is True we will generate
287
+ # anonymous traversal style syntax.
288
+ def translate(self, bytecode, child=False):
289
+ script = '__' if child else self.traversal_source
290
+
291
+ for step in bytecode.source_instructions:
292
+ script += self.do_translation(step)
293
+
294
+ for step in bytecode.step_instructions:
295
+ script += self.do_translation(step)
296
+
297
+ return script
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: gremlinpython
3
- Version: 3.6.5
3
+ Version: 3.6.6
4
4
  Summary: Gremlin-Python for Apache TinkerPop
5
5
  Home-page: http://tinkerpop.apache.org
6
6
  License: Apache 2
@@ -1,175 +0,0 @@
1
- #
2
- # Licensed to the Apache Software Foundation (ASF) under one
3
- # or more contributor license agreements. See the NOTICE file
4
- # distributed with this work for additional information
5
- # regarding copyright ownership. The ASF licenses this file
6
- # to you under the Apache License, Version 2.0 (the
7
- # "License"); you may not use this file except in compliance
8
- # with the License. You may obtain a copy of the License at
9
- #
10
- # http://www.apache.org/licenses/LICENSE-2.0
11
- #
12
- # Unless required by applicable law or agreed to in writing,
13
- # software distributed under the License is distributed on an
14
- # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15
- # KIND, either express or implied. See the License for the
16
- # specific language governing permissions and limitations
17
- # under the License.
18
- #
19
-
20
- """
21
- Class that can turn traversals back into Gremlin Groovy format text queries.
22
- Those queries can then be run in the Gremlin console or using the GLV submit(<String>) API or
23
- sent to any TinkerPop compliant HTTP endpoint.
24
- """
25
- __author__ = 'Kelvin R. Lawrence (gfxman)'
26
-
27
- from gremlin_python.process.graph_traversal import __
28
- from gremlin_python.process.anonymous_traversal import traversal
29
- from gremlin_python.process.traversal import *
30
- from gremlin_python.driver.driver_remote_connection import DriverRemoteConnection
31
- from gremlin_python.process.strategies import *
32
- from datetime import datetime
33
-
34
-
35
- class Translator:
36
- """
37
- Turn a bytecode object back into a textual query (Gremlin Groovy script).
38
- """
39
-
40
- # Dictionary used to reverse-map token IDs to strings
41
- options = {
42
- WithOptions.tokens: 'tokens',
43
- WithOptions.none: 'none',
44
- WithOptions.ids: 'ids',
45
- WithOptions.labels: 'labels',
46
- WithOptions.keys: 'keys',
47
- WithOptions.values: 'values',
48
- WithOptions.all: 'all',
49
- WithOptions.indexer: 'indexer',
50
- WithOptions.list: 'list',
51
- WithOptions.map: 'map'
52
- }
53
-
54
- def __init__(self, traversal_source=None):
55
- self.traversal_source = traversal_source
56
-
57
- def get_traversal_source(self):
58
- return self.traversal_source
59
-
60
- def get_target_language(self):
61
- return "gremlin-groovy"
62
-
63
- def of(self,traversal_source):
64
- self.traversal_source = traversal_source
65
- return self
66
-
67
- # Do any needed special processing for the representation
68
- # of strings and dates.
69
- def fixup(self, v):
70
- if isinstance(v, str):
71
- return f'\'{v}\''
72
- elif type(v) == datetime:
73
- return self.process_date(v)
74
- else:
75
- return str(v)
76
-
77
- # Turn a Python datetime into the equivalent new Date(...)
78
- def process_date(self, date):
79
- y = date.year - 1900
80
- mo = date.month
81
- d = date.day
82
- h = date.hour
83
- mi = date.minute
84
- s = date.second
85
- return f'new Date({y},{mo},{d},{h},{mi},{s})'
86
-
87
- # Do special processing needed to format predicates that come in
88
- # such as "gt(a)" correctly.
89
- def process_predicate(self, p):
90
- res = str(p).split('(')[0] + '('
91
-
92
- if type(p.value) == list:
93
- res += '['
94
- for v in p.value:
95
- res += self.fixup(v) + ','
96
- res = res[0:-1] + ']'
97
- else:
98
- res += self.fixup(p.value)
99
- if p.other is not None:
100
- res+= ',' + self.fixup(p.other)
101
- res += ')'
102
- return res
103
-
104
- # Special processing to handle strategies
105
- def process_strategy(self, s):
106
- c = 0
107
- res = f'new {str(s)}('
108
- for key in s.configuration:
109
- res += ',' if c > 0 else ''
110
- res += key + ':'
111
- val = s.configuration[key]
112
- if isinstance(val, Traversal):
113
- res += self.translate(val.bytecode, child=True)
114
- else:
115
- res += self.fixup(val)
116
- c += 1
117
- res += ')'
118
- return res
119
- pass
120
-
121
- # Main driver of the translation. Different parts of
122
- # a Traversal are handled appropriately.
123
- def do_translation(self, step):
124
- script = ''
125
- params = step[1:]
126
- script += '.' + step[0] + '('
127
- if len(params) > 0:
128
- c = 0
129
- with_opts = False
130
- for p in params:
131
- script += ',' if c > 0 else ''
132
- if with_opts:
133
- script += f'WithOptions.{self.options[p]}'
134
- elif type(p) == Bytecode:
135
- script += self.translate(p, True)
136
- elif isinstance(p, P):
137
- script += self.process_predicate(p)
138
- elif type(p) in [Cardinality, Pop, Operator]:
139
- tmp = str(p)
140
- script += tmp[0:-1] if tmp.endswith('_') else tmp
141
- elif type(p) in [ReadOnlyStrategy, SubgraphStrategy, VertexProgramStrategy,
142
- OptionsStrategy, PartitionStrategy]:
143
- script += self.process_strategy(p)
144
- elif type(p) == datetime:
145
- script += self.process_date(p)
146
- elif p == WithOptions.tokens:
147
- script += 'WithOptions.tokens'
148
- with_opts = True
149
- elif isinstance(p, str):
150
- script += f'\'{p}\''
151
- elif type(p) == bool:
152
- script += 'true' if p else 'false'
153
- elif p is None:
154
- script += 'null'
155
- else:
156
- script += str(p)
157
- c += 1
158
- script += ')'
159
- return script
160
-
161
- # Translation starts here. There are two main parts to a
162
- # traversal. Source instructions such as "withSideEffect"
163
- # and "withStrategies", and step instructions such as
164
- # "addV" and "repeat". If child is True we will generate
165
- # anonymous traversal style syntax.
166
- def translate(self, bytecode, child=False):
167
- script = '__' if child else self.traversal_source
168
-
169
- for step in bytecode.source_instructions:
170
- script += self.do_translation(step)
171
-
172
- for step in bytecode.step_instructions:
173
- script += self.do_translation(step)
174
-
175
- return script
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes