gremlinpython 3.6.4__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.4/gremlinpython.egg-info → gremlinpython-3.6.6}/PKG-INFO +9 -9
- {gremlinpython-3.6.4 → gremlinpython-3.6.6}/README.rst +8 -8
- {gremlinpython-3.6.4 → gremlinpython-3.6.6}/gremlin_python/__version__.py +2 -2
- {gremlinpython-3.6.4 → gremlinpython-3.6.6}/gremlin_python/driver/aiohttp/transport.py +96 -0
- {gremlinpython-3.6.4 → gremlinpython-3.6.6}/gremlin_python/driver/client.py +27 -9
- {gremlinpython-3.6.4 → gremlinpython-3.6.6}/gremlin_python/driver/connection.py +1 -0
- {gremlinpython-3.6.4 → gremlinpython-3.6.6}/gremlin_python/driver/protocol.py +68 -3
- {gremlinpython-3.6.4 → gremlinpython-3.6.6}/gremlin_python/driver/serializer.py +5 -2
- {gremlinpython-3.6.4 → gremlinpython-3.6.6}/gremlin_python/driver/useragent.py +2 -6
- {gremlinpython-3.6.4 → gremlinpython-3.6.6}/gremlin_python/process/graph_traversal.py +26 -26
- gremlinpython-3.6.6/gremlin_python/process/translator.py +297 -0
- {gremlinpython-3.6.4 → gremlinpython-3.6.6}/gremlin_python/process/traversal.py +1 -1
- {gremlinpython-3.6.4 → gremlinpython-3.6.6/gremlinpython.egg-info}/PKG-INFO +9 -9
- gremlinpython-3.6.4/gremlin_python/process/translator.py +0 -173
- {gremlinpython-3.6.4 → gremlinpython-3.6.6}/LICENSE +0 -0
- {gremlinpython-3.6.4 → gremlinpython-3.6.6}/MANIFEST.in +0 -0
- {gremlinpython-3.6.4 → gremlinpython-3.6.6}/NOTICE +0 -0
- {gremlinpython-3.6.4 → gremlinpython-3.6.6}/gremlin_python/__init__.py +0 -0
- {gremlinpython-3.6.4 → gremlinpython-3.6.6}/gremlin_python/driver/__init__.py +0 -0
- {gremlinpython-3.6.4 → gremlinpython-3.6.6}/gremlin_python/driver/aiohttp/__init__.py +0 -0
- {gremlinpython-3.6.4 → gremlinpython-3.6.6}/gremlin_python/driver/driver_remote_connection.py +0 -0
- {gremlinpython-3.6.4 → gremlinpython-3.6.6}/gremlin_python/driver/remote_connection.py +0 -0
- {gremlinpython-3.6.4 → gremlinpython-3.6.6}/gremlin_python/driver/request.py +0 -0
- {gremlinpython-3.6.4 → gremlinpython-3.6.6}/gremlin_python/driver/resultset.py +0 -0
- {gremlinpython-3.6.4 → gremlinpython-3.6.6}/gremlin_python/driver/transport.py +0 -0
- {gremlinpython-3.6.4 → gremlinpython-3.6.6}/gremlin_python/process/__init__.py +0 -0
- {gremlinpython-3.6.4 → gremlinpython-3.6.6}/gremlin_python/process/anonymous_traversal.py +0 -0
- {gremlinpython-3.6.4 → gremlinpython-3.6.6}/gremlin_python/process/strategies.py +0 -0
- {gremlinpython-3.6.4 → gremlinpython-3.6.6}/gremlin_python/statics.py +0 -0
- {gremlinpython-3.6.4 → gremlinpython-3.6.6}/gremlin_python/structure/__init__.py +0 -0
- {gremlinpython-3.6.4 → gremlinpython-3.6.6}/gremlin_python/structure/graph.py +0 -0
- {gremlinpython-3.6.4 → gremlinpython-3.6.6}/gremlin_python/structure/io/__init__.py +0 -0
- {gremlinpython-3.6.4 → gremlinpython-3.6.6}/gremlin_python/structure/io/graphbinaryV1.py +0 -0
- {gremlinpython-3.6.4 → gremlinpython-3.6.6}/gremlin_python/structure/io/graphsonV2d0.py +0 -0
- {gremlinpython-3.6.4 → gremlinpython-3.6.6}/gremlin_python/structure/io/graphsonV3d0.py +0 -0
- {gremlinpython-3.6.4 → gremlinpython-3.6.6}/gremlin_python/structure/io/util.py +0 -0
- {gremlinpython-3.6.4 → gremlinpython-3.6.6}/gremlinpython.egg-info/SOURCES.txt +0 -0
- {gremlinpython-3.6.4 → gremlinpython-3.6.6}/gremlinpython.egg-info/dependency_links.txt +0 -0
- {gremlinpython-3.6.4 → gremlinpython-3.6.6}/gremlinpython.egg-info/requires.txt +0 -0
- {gremlinpython-3.6.4 → gremlinpython-3.6.6}/gremlinpython.egg-info/top_level.txt +0 -0
- {gremlinpython-3.6.4 → gremlinpython-3.6.6}/setup.cfg +0 -0
- {gremlinpython-3.6.4 → 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.
|
|
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
|
|
@@ -59,16 +59,16 @@ from the Python shell looks like this:
|
|
|
59
59
|
|
|
60
60
|
>>> from gremlin_python.process.anonymous_traversal import traversal
|
|
61
61
|
>>> from gremlin_python.driver.driver_remote_connection import DriverRemoteConnection
|
|
62
|
-
>>> g = traversal().
|
|
62
|
+
>>> g = traversal().with_remote(DriverRemoteConnection('ws://localhost:8182/gremlin','g'))
|
|
63
63
|
|
|
64
64
|
Once "g" has been created using a connection, it is then possible to start writing Gremlin traversals to query the
|
|
65
65
|
remote graph:
|
|
66
66
|
|
|
67
|
-
>>> g.V().both()[1:3].
|
|
67
|
+
>>> g.V().both()[1:3].to_list()
|
|
68
68
|
[v[2], v[4]]
|
|
69
|
-
>>> g.V().both()[1].
|
|
69
|
+
>>> g.V().both()[1].to_list()
|
|
70
70
|
[v[2]]
|
|
71
|
-
>>> g.V().both().name.
|
|
71
|
+
>>> g.V().both().name.to_list()
|
|
72
72
|
[lop, vadas, josh, marko, marko, josh, peter, ripple, lop, marko, josh, lop]
|
|
73
73
|
|
|
74
74
|
-----------------
|
|
@@ -101,7 +101,7 @@ Create Vertex
|
|
|
101
101
|
|
|
102
102
|
def create_vertex(self, vid, vlabel):
|
|
103
103
|
# default database cardinality is used when Cardinality argument is not specified
|
|
104
|
-
g.
|
|
104
|
+
g.add_v(vlabel).property(id, vid). \
|
|
105
105
|
property(single, 'name', 'Apache'). \
|
|
106
106
|
property('lastname', 'Tinkerpop'). \
|
|
107
107
|
next()
|
|
@@ -112,13 +112,13 @@ Find Vertices
|
|
|
112
112
|
.. code:: python
|
|
113
113
|
|
|
114
114
|
def list_all(self, limit=500):
|
|
115
|
-
g.V().limit(limit).
|
|
115
|
+
g.V().limit(limit).element_map().to_list()
|
|
116
116
|
|
|
117
117
|
def find_vertex(self, vid):
|
|
118
|
-
g.V(vid).
|
|
118
|
+
g.V(vid).element_map().next()
|
|
119
119
|
|
|
120
120
|
def list_by_label_name(self, vlabel, name):
|
|
121
|
-
g.V().has(vlabel, 'name', name).
|
|
121
|
+
g.V().has(vlabel, 'name', name).element_map().to_list()
|
|
122
122
|
|
|
123
123
|
Update Vertex
|
|
124
124
|
^^^^^^^^^^^^^
|
|
@@ -42,16 +42,16 @@ from the Python shell looks like this:
|
|
|
42
42
|
|
|
43
43
|
>>> from gremlin_python.process.anonymous_traversal import traversal
|
|
44
44
|
>>> from gremlin_python.driver.driver_remote_connection import DriverRemoteConnection
|
|
45
|
-
>>> g = traversal().
|
|
45
|
+
>>> g = traversal().with_remote(DriverRemoteConnection('ws://localhost:8182/gremlin','g'))
|
|
46
46
|
|
|
47
47
|
Once "g" has been created using a connection, it is then possible to start writing Gremlin traversals to query the
|
|
48
48
|
remote graph:
|
|
49
49
|
|
|
50
|
-
>>> g.V().both()[1:3].
|
|
50
|
+
>>> g.V().both()[1:3].to_list()
|
|
51
51
|
[v[2], v[4]]
|
|
52
|
-
>>> g.V().both()[1].
|
|
52
|
+
>>> g.V().both()[1].to_list()
|
|
53
53
|
[v[2]]
|
|
54
|
-
>>> g.V().both().name.
|
|
54
|
+
>>> g.V().both().name.to_list()
|
|
55
55
|
[lop, vadas, josh, marko, marko, josh, peter, ripple, lop, marko, josh, lop]
|
|
56
56
|
|
|
57
57
|
-----------------
|
|
@@ -84,7 +84,7 @@ Create Vertex
|
|
|
84
84
|
|
|
85
85
|
def create_vertex(self, vid, vlabel):
|
|
86
86
|
# default database cardinality is used when Cardinality argument is not specified
|
|
87
|
-
g.
|
|
87
|
+
g.add_v(vlabel).property(id, vid). \
|
|
88
88
|
property(single, 'name', 'Apache'). \
|
|
89
89
|
property('lastname', 'Tinkerpop'). \
|
|
90
90
|
next()
|
|
@@ -95,13 +95,13 @@ Find Vertices
|
|
|
95
95
|
.. code:: python
|
|
96
96
|
|
|
97
97
|
def list_all(self, limit=500):
|
|
98
|
-
g.V().limit(limit).
|
|
98
|
+
g.V().limit(limit).element_map().to_list()
|
|
99
99
|
|
|
100
100
|
def find_vertex(self, vid):
|
|
101
|
-
g.V(vid).
|
|
101
|
+
g.V(vid).element_map().next()
|
|
102
102
|
|
|
103
103
|
def list_by_label_name(self, vlabel, name):
|
|
104
|
-
g.V().has(vlabel, 'name', name).
|
|
104
|
+
g.V().has(vlabel, 'name', name).element_map().to_list()
|
|
105
105
|
|
|
106
106
|
Update Vertex
|
|
107
107
|
^^^^^^^^^^^^^
|
|
@@ -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:
|
|
@@ -161,6 +176,9 @@ class Client:
|
|
|
161
176
|
return self.submit_async(message, bindings, request_options)
|
|
162
177
|
|
|
163
178
|
def submit_async(self, message, bindings=None, request_options=None):
|
|
179
|
+
if self.is_closed():
|
|
180
|
+
raise Exception("Client is closed")
|
|
181
|
+
|
|
164
182
|
log.debug("message '%s'", str(message))
|
|
165
183
|
args = {'gremlin': message, 'aliases': {'g': self._traversal_source}}
|
|
166
184
|
processor = ''
|
|
@@ -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,20 +18,16 @@
|
|
|
18
18
|
#
|
|
19
19
|
import platform
|
|
20
20
|
|
|
21
|
+
gremlin_version = "3.6.6" # DO NOT MODIFY - Configured automatically by Maven Replacer Plugin
|
|
21
22
|
|
|
22
23
|
def _generate_user_agent():
|
|
23
24
|
application_name = "NotAvailable"
|
|
24
|
-
try:
|
|
25
|
-
from gremlin_python import __version__
|
|
26
|
-
driver_version = __version__.version.replace(" ", "_")
|
|
27
|
-
except ImportError:
|
|
28
|
-
driver_version = "NotAvailable"
|
|
29
25
|
runtime_version = platform.python_version().replace(" ", "_")
|
|
30
26
|
os_name = platform.system().replace(" ", "_")
|
|
31
27
|
os_version = platform.release().replace(" ", "_")
|
|
32
28
|
architecture = platform.machine().replace(" ", "_")
|
|
33
29
|
user_agent = "{appName} Gremlin-Python.{driverVersion} {runtimeVersion} {osName}.{osVersion} {cpuArch}".format(
|
|
34
|
-
appName=application_name, driverVersion=
|
|
30
|
+
appName=application_name, driverVersion=gremlin_version, runtimeVersion=runtime_version,
|
|
35
31
|
osName=os_name, osVersion=os_version, cpuArch=architecture)
|
|
36
32
|
|
|
37
33
|
return user_agent
|
|
@@ -134,7 +134,7 @@ class GraphTraversalSource(object):
|
|
|
134
134
|
val = True if v is None else v
|
|
135
135
|
if options_strategy is None:
|
|
136
136
|
options_strategy = OptionsStrategy({k: val})
|
|
137
|
-
source = self.
|
|
137
|
+
source = self.with_strategies(options_strategy)
|
|
138
138
|
else:
|
|
139
139
|
options_strategy[1].configuration[k] = val
|
|
140
140
|
|
|
@@ -1587,7 +1587,7 @@ def V(*args):
|
|
|
1587
1587
|
|
|
1588
1588
|
|
|
1589
1589
|
def addE(*args):
|
|
1590
|
-
return __.
|
|
1590
|
+
return __.add_e(*args)
|
|
1591
1591
|
|
|
1592
1592
|
|
|
1593
1593
|
def add_e(*args):
|
|
@@ -1595,7 +1595,7 @@ def add_e(*args):
|
|
|
1595
1595
|
|
|
1596
1596
|
|
|
1597
1597
|
def addV(*args):
|
|
1598
|
-
return __.
|
|
1598
|
+
return __.add_v(*args)
|
|
1599
1599
|
|
|
1600
1600
|
|
|
1601
1601
|
def add_v(*args):
|
|
@@ -1623,7 +1623,7 @@ def both(*args):
|
|
|
1623
1623
|
|
|
1624
1624
|
|
|
1625
1625
|
def bothE(*args):
|
|
1626
|
-
return __.
|
|
1626
|
+
return __.both_e(*args)
|
|
1627
1627
|
|
|
1628
1628
|
|
|
1629
1629
|
def both_e(*args):
|
|
@@ -1631,7 +1631,7 @@ def both_e(*args):
|
|
|
1631
1631
|
|
|
1632
1632
|
|
|
1633
1633
|
def bothV(*args):
|
|
1634
|
-
return __.
|
|
1634
|
+
return __.both_v(*args)
|
|
1635
1635
|
|
|
1636
1636
|
|
|
1637
1637
|
def both_v(*args):
|
|
@@ -1671,7 +1671,7 @@ def count(*args):
|
|
|
1671
1671
|
|
|
1672
1672
|
|
|
1673
1673
|
def cyclicPath(*args):
|
|
1674
|
-
return __.
|
|
1674
|
+
return __.cyclic_path(*args)
|
|
1675
1675
|
|
|
1676
1676
|
|
|
1677
1677
|
def cyclic_path(*args):
|
|
@@ -1691,7 +1691,7 @@ def element(*args):
|
|
|
1691
1691
|
|
|
1692
1692
|
|
|
1693
1693
|
def elementMap(*args):
|
|
1694
|
-
return __.
|
|
1694
|
+
return __.element_map(*args)
|
|
1695
1695
|
|
|
1696
1696
|
|
|
1697
1697
|
def element_map(*args):
|
|
@@ -1711,7 +1711,7 @@ def filter_(*args):
|
|
|
1711
1711
|
|
|
1712
1712
|
|
|
1713
1713
|
def flatMap(*args):
|
|
1714
|
-
return __.
|
|
1714
|
+
return __.flat_map(*args)
|
|
1715
1715
|
|
|
1716
1716
|
|
|
1717
1717
|
def flat_map(*args):
|
|
@@ -1727,7 +1727,7 @@ def group(*args):
|
|
|
1727
1727
|
|
|
1728
1728
|
|
|
1729
1729
|
def groupCount(*args):
|
|
1730
|
-
return __.
|
|
1730
|
+
return __.group_count(*args)
|
|
1731
1731
|
|
|
1732
1732
|
|
|
1733
1733
|
def group_count(*args):
|
|
@@ -1739,7 +1739,7 @@ def has(*args):
|
|
|
1739
1739
|
|
|
1740
1740
|
|
|
1741
1741
|
def hasId(*args):
|
|
1742
|
-
return __.
|
|
1742
|
+
return __.has_id(*args)
|
|
1743
1743
|
|
|
1744
1744
|
|
|
1745
1745
|
def has_id(*args):
|
|
@@ -1747,7 +1747,7 @@ def has_id(*args):
|
|
|
1747
1747
|
|
|
1748
1748
|
|
|
1749
1749
|
def hasKey(*args):
|
|
1750
|
-
return __.
|
|
1750
|
+
return __.has_key_(*args)
|
|
1751
1751
|
|
|
1752
1752
|
|
|
1753
1753
|
def has_key_(*args):
|
|
@@ -1755,7 +1755,7 @@ def has_key_(*args):
|
|
|
1755
1755
|
|
|
1756
1756
|
|
|
1757
1757
|
def hasLabel(*args):
|
|
1758
|
-
return __.
|
|
1758
|
+
return __.has_label(*args)
|
|
1759
1759
|
|
|
1760
1760
|
|
|
1761
1761
|
def has_label(*args):
|
|
@@ -1763,7 +1763,7 @@ def has_label(*args):
|
|
|
1763
1763
|
|
|
1764
1764
|
|
|
1765
1765
|
def hasNot(*args):
|
|
1766
|
-
return __.
|
|
1766
|
+
return __.has_not(*args)
|
|
1767
1767
|
|
|
1768
1768
|
|
|
1769
1769
|
def has_not(*args):
|
|
@@ -1771,7 +1771,7 @@ def has_not(*args):
|
|
|
1771
1771
|
|
|
1772
1772
|
|
|
1773
1773
|
def hasValue(*args):
|
|
1774
|
-
return __.
|
|
1774
|
+
return __.has_value(*args)
|
|
1775
1775
|
|
|
1776
1776
|
|
|
1777
1777
|
def has_value(*args):
|
|
@@ -1787,7 +1787,7 @@ def identity(*args):
|
|
|
1787
1787
|
|
|
1788
1788
|
|
|
1789
1789
|
def inE(*args):
|
|
1790
|
-
return __.
|
|
1790
|
+
return __.in_e(*args)
|
|
1791
1791
|
|
|
1792
1792
|
|
|
1793
1793
|
def in_e(*args):
|
|
@@ -1795,7 +1795,7 @@ def in_e(*args):
|
|
|
1795
1795
|
|
|
1796
1796
|
|
|
1797
1797
|
def inV(*args):
|
|
1798
|
-
return __.
|
|
1798
|
+
return __.in_v(*args)
|
|
1799
1799
|
|
|
1800
1800
|
|
|
1801
1801
|
def in_v(*args):
|
|
@@ -1887,7 +1887,7 @@ def order(*args):
|
|
|
1887
1887
|
|
|
1888
1888
|
|
|
1889
1889
|
def otherV(*args):
|
|
1890
|
-
return __.
|
|
1890
|
+
return __.other_v(*args)
|
|
1891
1891
|
|
|
1892
1892
|
|
|
1893
1893
|
def other_v(*args):
|
|
@@ -1899,7 +1899,7 @@ def out(*args):
|
|
|
1899
1899
|
|
|
1900
1900
|
|
|
1901
1901
|
def outE(*args):
|
|
1902
|
-
return __.
|
|
1902
|
+
return __.out_e(*args)
|
|
1903
1903
|
|
|
1904
1904
|
|
|
1905
1905
|
def out_e(*args):
|
|
@@ -1907,7 +1907,7 @@ def out_e(*args):
|
|
|
1907
1907
|
|
|
1908
1908
|
|
|
1909
1909
|
def outV(*args):
|
|
1910
|
-
return __.
|
|
1910
|
+
return __.out_v(*args)
|
|
1911
1911
|
|
|
1912
1912
|
|
|
1913
1913
|
def out_v(*args):
|
|
@@ -1931,7 +1931,7 @@ def property(*args):
|
|
|
1931
1931
|
|
|
1932
1932
|
|
|
1933
1933
|
def propertyMap(*args):
|
|
1934
|
-
return __.
|
|
1934
|
+
return __.property_map(*args)
|
|
1935
1935
|
|
|
1936
1936
|
|
|
1937
1937
|
def property_map(*args):
|
|
@@ -1959,7 +1959,7 @@ def select(*args):
|
|
|
1959
1959
|
|
|
1960
1960
|
|
|
1961
1961
|
def sideEffect(*args):
|
|
1962
|
-
return __.
|
|
1962
|
+
return __.side_effect(*args)
|
|
1963
1963
|
|
|
1964
1964
|
|
|
1965
1965
|
def side_effect(*args):
|
|
@@ -1967,7 +1967,7 @@ def side_effect(*args):
|
|
|
1967
1967
|
|
|
1968
1968
|
|
|
1969
1969
|
def simplePath(*args):
|
|
1970
|
-
return __.
|
|
1970
|
+
return __.simple_path(*args)
|
|
1971
1971
|
|
|
1972
1972
|
|
|
1973
1973
|
def simple_path(*args):
|
|
@@ -1995,7 +1995,7 @@ def tail(*args):
|
|
|
1995
1995
|
|
|
1996
1996
|
|
|
1997
1997
|
def timeLimit(*args):
|
|
1998
|
-
return __.
|
|
1998
|
+
return __.time_limit(*args)
|
|
1999
1999
|
|
|
2000
2000
|
|
|
2001
2001
|
def time_limit(*args):
|
|
@@ -2011,7 +2011,7 @@ def to(*args):
|
|
|
2011
2011
|
|
|
2012
2012
|
|
|
2013
2013
|
def toE(*args):
|
|
2014
|
-
return __.
|
|
2014
|
+
return __.to_e(*args)
|
|
2015
2015
|
|
|
2016
2016
|
|
|
2017
2017
|
def to_e(*args):
|
|
@@ -2019,7 +2019,7 @@ def to_e(*args):
|
|
|
2019
2019
|
|
|
2020
2020
|
|
|
2021
2021
|
def toV(*args):
|
|
2022
|
-
return __.
|
|
2022
|
+
return __.to_v(*args)
|
|
2023
2023
|
|
|
2024
2024
|
|
|
2025
2025
|
def to_v(*args):
|
|
@@ -2047,7 +2047,7 @@ def value(*args):
|
|
|
2047
2047
|
|
|
2048
2048
|
|
|
2049
2049
|
def valueMap(*args):
|
|
2050
|
-
return __.
|
|
2050
|
+
return __.value_map(*args)
|
|
2051
2051
|
|
|
2052
2052
|
|
|
2053
2053
|
def value_map(*args):
|
|
@@ -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.
|
|
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
|
|
@@ -59,16 +59,16 @@ from the Python shell looks like this:
|
|
|
59
59
|
|
|
60
60
|
>>> from gremlin_python.process.anonymous_traversal import traversal
|
|
61
61
|
>>> from gremlin_python.driver.driver_remote_connection import DriverRemoteConnection
|
|
62
|
-
>>> g = traversal().
|
|
62
|
+
>>> g = traversal().with_remote(DriverRemoteConnection('ws://localhost:8182/gremlin','g'))
|
|
63
63
|
|
|
64
64
|
Once "g" has been created using a connection, it is then possible to start writing Gremlin traversals to query the
|
|
65
65
|
remote graph:
|
|
66
66
|
|
|
67
|
-
>>> g.V().both()[1:3].
|
|
67
|
+
>>> g.V().both()[1:3].to_list()
|
|
68
68
|
[v[2], v[4]]
|
|
69
|
-
>>> g.V().both()[1].
|
|
69
|
+
>>> g.V().both()[1].to_list()
|
|
70
70
|
[v[2]]
|
|
71
|
-
>>> g.V().both().name.
|
|
71
|
+
>>> g.V().both().name.to_list()
|
|
72
72
|
[lop, vadas, josh, marko, marko, josh, peter, ripple, lop, marko, josh, lop]
|
|
73
73
|
|
|
74
74
|
-----------------
|
|
@@ -101,7 +101,7 @@ Create Vertex
|
|
|
101
101
|
|
|
102
102
|
def create_vertex(self, vid, vlabel):
|
|
103
103
|
# default database cardinality is used when Cardinality argument is not specified
|
|
104
|
-
g.
|
|
104
|
+
g.add_v(vlabel).property(id, vid). \
|
|
105
105
|
property(single, 'name', 'Apache'). \
|
|
106
106
|
property('lastname', 'Tinkerpop'). \
|
|
107
107
|
next()
|
|
@@ -112,13 +112,13 @@ Find Vertices
|
|
|
112
112
|
.. code:: python
|
|
113
113
|
|
|
114
114
|
def list_all(self, limit=500):
|
|
115
|
-
g.V().limit(limit).
|
|
115
|
+
g.V().limit(limit).element_map().to_list()
|
|
116
116
|
|
|
117
117
|
def find_vertex(self, vid):
|
|
118
|
-
g.V(vid).
|
|
118
|
+
g.V(vid).element_map().next()
|
|
119
119
|
|
|
120
120
|
def list_by_label_name(self, vlabel, name):
|
|
121
|
-
g.V().has(vlabel, 'name', name).
|
|
121
|
+
g.V().has(vlabel, 'name', name).element_map().to_list()
|
|
122
122
|
|
|
123
123
|
Update Vertex
|
|
124
124
|
^^^^^^^^^^^^^
|
|
@@ -1,173 +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 type(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 type(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 type(p) == str:
|
|
150
|
-
script += f'\'{p}\''
|
|
151
|
-
elif type(p) == bool:
|
|
152
|
-
script += 'true' if p else 'false'
|
|
153
|
-
else:
|
|
154
|
-
script += str(p)
|
|
155
|
-
c += 1
|
|
156
|
-
script += ')'
|
|
157
|
-
return script
|
|
158
|
-
|
|
159
|
-
# Translation starts here. There are two main parts to a
|
|
160
|
-
# traversal. Source instructions such as "withSideEffect"
|
|
161
|
-
# and "withStrategies", and step instructions such as
|
|
162
|
-
# "addV" and "repeat". If child is True we will generate
|
|
163
|
-
# anonymous traversal style syntax.
|
|
164
|
-
def translate(self, bytecode, child=False):
|
|
165
|
-
script = '__' if child else self.traversal_source
|
|
166
|
-
|
|
167
|
-
for step in bytecode.source_instructions:
|
|
168
|
-
script += self.do_translation(step)
|
|
169
|
-
|
|
170
|
-
for step in bytecode.step_instructions:
|
|
171
|
-
script += self.do_translation(step)
|
|
172
|
-
|
|
173
|
-
return script
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{gremlinpython-3.6.4 → 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
|