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.
- {gremlinpython-3.6.5/gremlinpython.egg-info → gremlinpython-3.6.6}/PKG-INFO +1 -1
- {gremlinpython-3.6.5 → gremlinpython-3.6.6}/gremlin_python/__version__.py +2 -2
- {gremlinpython-3.6.5 → gremlinpython-3.6.6}/gremlin_python/driver/aiohttp/transport.py +96 -0
- {gremlinpython-3.6.5 → gremlinpython-3.6.6}/gremlin_python/driver/client.py +24 -9
- {gremlinpython-3.6.5 → gremlinpython-3.6.6}/gremlin_python/driver/connection.py +1 -0
- {gremlinpython-3.6.5 → gremlinpython-3.6.6}/gremlin_python/driver/protocol.py +68 -3
- {gremlinpython-3.6.5 → gremlinpython-3.6.6}/gremlin_python/driver/serializer.py +5 -2
- {gremlinpython-3.6.5 → gremlinpython-3.6.6}/gremlin_python/driver/useragent.py +1 -1
- gremlinpython-3.6.6/gremlin_python/process/translator.py +297 -0
- {gremlinpython-3.6.5 → gremlinpython-3.6.6/gremlinpython.egg-info}/PKG-INFO +1 -1
- gremlinpython-3.6.5/gremlin_python/process/translator.py +0 -175
- {gremlinpython-3.6.5 → gremlinpython-3.6.6}/LICENSE +0 -0
- {gremlinpython-3.6.5 → gremlinpython-3.6.6}/MANIFEST.in +0 -0
- {gremlinpython-3.6.5 → gremlinpython-3.6.6}/NOTICE +0 -0
- {gremlinpython-3.6.5 → gremlinpython-3.6.6}/README.rst +0 -0
- {gremlinpython-3.6.5 → gremlinpython-3.6.6}/gremlin_python/__init__.py +0 -0
- {gremlinpython-3.6.5 → gremlinpython-3.6.6}/gremlin_python/driver/__init__.py +0 -0
- {gremlinpython-3.6.5 → gremlinpython-3.6.6}/gremlin_python/driver/aiohttp/__init__.py +0 -0
- {gremlinpython-3.6.5 → gremlinpython-3.6.6}/gremlin_python/driver/driver_remote_connection.py +0 -0
- {gremlinpython-3.6.5 → gremlinpython-3.6.6}/gremlin_python/driver/remote_connection.py +0 -0
- {gremlinpython-3.6.5 → gremlinpython-3.6.6}/gremlin_python/driver/request.py +0 -0
- {gremlinpython-3.6.5 → gremlinpython-3.6.6}/gremlin_python/driver/resultset.py +0 -0
- {gremlinpython-3.6.5 → gremlinpython-3.6.6}/gremlin_python/driver/transport.py +0 -0
- {gremlinpython-3.6.5 → gremlinpython-3.6.6}/gremlin_python/process/__init__.py +0 -0
- {gremlinpython-3.6.5 → gremlinpython-3.6.6}/gremlin_python/process/anonymous_traversal.py +0 -0
- {gremlinpython-3.6.5 → gremlinpython-3.6.6}/gremlin_python/process/graph_traversal.py +0 -0
- {gremlinpython-3.6.5 → gremlinpython-3.6.6}/gremlin_python/process/strategies.py +0 -0
- {gremlinpython-3.6.5 → gremlinpython-3.6.6}/gremlin_python/process/traversal.py +0 -0
- {gremlinpython-3.6.5 → gremlinpython-3.6.6}/gremlin_python/statics.py +0 -0
- {gremlinpython-3.6.5 → gremlinpython-3.6.6}/gremlin_python/structure/__init__.py +0 -0
- {gremlinpython-3.6.5 → gremlinpython-3.6.6}/gremlin_python/structure/graph.py +0 -0
- {gremlinpython-3.6.5 → gremlinpython-3.6.6}/gremlin_python/structure/io/__init__.py +0 -0
- {gremlinpython-3.6.5 → gremlinpython-3.6.6}/gremlin_python/structure/io/graphbinaryV1.py +0 -0
- {gremlinpython-3.6.5 → gremlinpython-3.6.6}/gremlin_python/structure/io/graphsonV2d0.py +0 -0
- {gremlinpython-3.6.5 → gremlinpython-3.6.6}/gremlin_python/structure/io/graphsonV3d0.py +0 -0
- {gremlinpython-3.6.5 → gremlinpython-3.6.6}/gremlin_python/structure/io/util.py +0 -0
- {gremlinpython-3.6.5 → gremlinpython-3.6.6}/gremlinpython.egg-info/SOURCES.txt +0 -0
- {gremlinpython-3.6.5 → gremlinpython-3.6.6}/gremlinpython.egg-info/dependency_links.txt +0 -0
- {gremlinpython-3.6.5 → gremlinpython-3.6.6}/gremlinpython.egg-info/requires.txt +0 -0
- {gremlinpython-3.6.5 → gremlinpython-3.6.6}/gremlinpython.egg-info/top_level.txt +0 -0
- {gremlinpython-3.6.5 → gremlinpython-3.6.6}/setup.cfg +0 -0
- {gremlinpython-3.6.5 → gremlinpython-3.6.6}/setup.py +0 -0
|
@@ -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
|
-
|
|
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():
|
|
76
|
-
self.
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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.
|
|
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,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
|
|
File without changes
|
{gremlinpython-3.6.5 → gremlinpython-3.6.6}/gremlin_python/driver/driver_remote_connection.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|