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.
- gremlinpython-3.7.2/NOTICE +5 -0
- {gremlinpython-3.7.0/gremlinpython.egg-info → gremlinpython-3.7.2}/PKG-INFO +1 -1
- {gremlinpython-3.7.0 → gremlinpython-3.7.2}/gremlin_python/__version__.py +2 -2
- {gremlinpython-3.7.0 → gremlinpython-3.7.2}/gremlin_python/driver/aiohttp/transport.py +98 -0
- {gremlinpython-3.7.0 → gremlinpython-3.7.2}/gremlin_python/driver/client.py +24 -9
- {gremlinpython-3.7.0 → gremlinpython-3.7.2}/gremlin_python/driver/connection.py +4 -2
- {gremlinpython-3.7.0 → gremlinpython-3.7.2}/gremlin_python/driver/protocol.py +70 -3
- {gremlinpython-3.7.0 → gremlinpython-3.7.2}/gremlin_python/driver/serializer.py +6 -3
- {gremlinpython-3.7.0 → gremlinpython-3.7.2}/gremlin_python/driver/useragent.py +1 -1
- {gremlinpython-3.7.0 → gremlinpython-3.7.2}/gremlin_python/process/graph_traversal.py +320 -2
- gremlinpython-3.7.2/gremlin_python/process/translator.py +297 -0
- {gremlinpython-3.7.0 → gremlinpython-3.7.2}/gremlin_python/process/traversal.py +9 -2
- {gremlinpython-3.7.0 → gremlinpython-3.7.2}/gremlin_python/structure/io/graphbinaryV1.py +7 -1
- {gremlinpython-3.7.0 → gremlinpython-3.7.2}/gremlin_python/structure/io/graphsonV2d0.py +25 -1
- {gremlinpython-3.7.0 → gremlinpython-3.7.2}/gremlin_python/structure/io/graphsonV3d0.py +27 -1
- {gremlinpython-3.7.0 → gremlinpython-3.7.2/gremlinpython.egg-info}/PKG-INFO +1 -1
- gremlinpython-3.7.0/NOTICE +0 -5
- gremlinpython-3.7.0/gremlin_python/process/translator.py +0 -175
- {gremlinpython-3.7.0 → gremlinpython-3.7.2}/LICENSE +0 -0
- {gremlinpython-3.7.0 → gremlinpython-3.7.2}/MANIFEST.in +0 -0
- {gremlinpython-3.7.0 → gremlinpython-3.7.2}/README.rst +0 -0
- {gremlinpython-3.7.0 → gremlinpython-3.7.2}/gremlin_python/__init__.py +0 -0
- {gremlinpython-3.7.0 → gremlinpython-3.7.2}/gremlin_python/driver/__init__.py +0 -0
- {gremlinpython-3.7.0 → gremlinpython-3.7.2}/gremlin_python/driver/aiohttp/__init__.py +0 -0
- {gremlinpython-3.7.0 → gremlinpython-3.7.2}/gremlin_python/driver/driver_remote_connection.py +0 -0
- {gremlinpython-3.7.0 → gremlinpython-3.7.2}/gremlin_python/driver/remote_connection.py +0 -0
- {gremlinpython-3.7.0 → gremlinpython-3.7.2}/gremlin_python/driver/request.py +0 -0
- {gremlinpython-3.7.0 → gremlinpython-3.7.2}/gremlin_python/driver/resultset.py +0 -0
- {gremlinpython-3.7.0 → gremlinpython-3.7.2}/gremlin_python/driver/transport.py +0 -0
- {gremlinpython-3.7.0 → gremlinpython-3.7.2}/gremlin_python/process/__init__.py +0 -0
- {gremlinpython-3.7.0 → gremlinpython-3.7.2}/gremlin_python/process/anonymous_traversal.py +0 -0
- {gremlinpython-3.7.0 → gremlinpython-3.7.2}/gremlin_python/process/strategies.py +0 -0
- {gremlinpython-3.7.0 → gremlinpython-3.7.2}/gremlin_python/statics.py +0 -0
- {gremlinpython-3.7.0 → gremlinpython-3.7.2}/gremlin_python/structure/__init__.py +0 -0
- {gremlinpython-3.7.0 → gremlinpython-3.7.2}/gremlin_python/structure/graph.py +0 -0
- {gremlinpython-3.7.0 → gremlinpython-3.7.2}/gremlin_python/structure/io/__init__.py +0 -0
- {gremlinpython-3.7.0 → gremlinpython-3.7.2}/gremlin_python/structure/io/util.py +0 -0
- {gremlinpython-3.7.0 → gremlinpython-3.7.2}/gremlinpython.egg-info/SOURCES.txt +0 -0
- {gremlinpython-3.7.0 → gremlinpython-3.7.2}/gremlinpython.egg-info/dependency_links.txt +0 -0
- {gremlinpython-3.7.0 → gremlinpython-3.7.2}/gremlinpython.egg-info/requires.txt +0 -0
- {gremlinpython-3.7.0 → gremlinpython-3.7.2}/gremlinpython.egg-info/top_level.txt +0 -0
- {gremlinpython-3.7.0 → gremlinpython-3.7.2}/setup.cfg +0 -0
- {gremlinpython-3.7.0 → gremlinpython-3.7.2}/setup.py +0 -0
|
@@ -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
|
-
|
|
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:
|
|
@@ -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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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.
|
|
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"
|