gremlinpython 3.6.8__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (37) hide show
  1. gremlin_python/__init__.py +20 -0
  2. gremlin_python/__version__.py +20 -0
  3. gremlin_python/driver/__init__.py +20 -0
  4. gremlin_python/driver/aiohttp/__init__.py +18 -0
  5. gremlin_python/driver/aiohttp/transport.py +242 -0
  6. gremlin_python/driver/client.py +206 -0
  7. gremlin_python/driver/connection.py +106 -0
  8. gremlin_python/driver/driver_remote_connection.py +183 -0
  9. gremlin_python/driver/protocol.py +259 -0
  10. gremlin_python/driver/remote_connection.py +84 -0
  11. gremlin_python/driver/request.py +25 -0
  12. gremlin_python/driver/resultset.py +100 -0
  13. gremlin_python/driver/serializer.py +294 -0
  14. gremlin_python/driver/transport.py +45 -0
  15. gremlin_python/driver/useragent.py +37 -0
  16. gremlin_python/process/__init__.py +20 -0
  17. gremlin_python/process/anonymous_traversal.py +64 -0
  18. gremlin_python/process/graph_traversal.py +2301 -0
  19. gremlin_python/process/strategies.py +239 -0
  20. gremlin_python/process/translator.py +297 -0
  21. gremlin_python/process/traversal.py +875 -0
  22. gremlin_python/statics.py +117 -0
  23. gremlin_python/structure/__init__.py +20 -0
  24. gremlin_python/structure/graph.py +134 -0
  25. gremlin_python/structure/io/__init__.py +20 -0
  26. gremlin_python/structure/io/graphbinaryV1.py +1153 -0
  27. gremlin_python/structure/io/graphsonV2d0.py +646 -0
  28. gremlin_python/structure/io/graphsonV3d0.py +766 -0
  29. gremlin_python/structure/io/util.py +60 -0
  30. gremlinpython-3.6.8.data/data/LICENSE +202 -0
  31. gremlinpython-3.6.8.data/data/NOTICE +5 -0
  32. gremlinpython-3.6.8.dist-info/LICENSE +202 -0
  33. gremlinpython-3.6.8.dist-info/METADATA +147 -0
  34. gremlinpython-3.6.8.dist-info/NOTICE +5 -0
  35. gremlinpython-3.6.8.dist-info/RECORD +37 -0
  36. gremlinpython-3.6.8.dist-info/WHEEL +5 -0
  37. gremlinpython-3.6.8.dist-info/top_level.txt +1 -0
@@ -0,0 +1,183 @@
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
+ import logging
20
+ from concurrent.futures import Future
21
+ import warnings
22
+
23
+ from gremlin_python.driver import client, serializer
24
+ from gremlin_python.driver.remote_connection import (
25
+ RemoteConnection, RemoteTraversal)
26
+ from gremlin_python.process.strategies import OptionsStrategy
27
+ from gremlin_python.process.traversal import Bytecode
28
+ import uuid
29
+
30
+ log = logging.getLogger("gremlinpython")
31
+
32
+ __author__ = 'David M. Brown (davebshow@gmail.com), Lyndon Bauto (lyndonb@bitquilltech.com)'
33
+
34
+
35
+ class DriverRemoteConnection(RemoteConnection):
36
+
37
+ def __init__(self, url, traversal_source="g", protocol_factory=None,
38
+ transport_factory=None, pool_size=None, max_workers=None,
39
+ username="", password="", kerberized_service='',
40
+ message_serializer=None, graphson_reader=None,
41
+ graphson_writer=None, headers=None, session=None,
42
+ enable_user_agent_on_connect=True, enable_compression=False, **transport_kwargs):
43
+ log.info("Creating DriverRemoteConnection with url '%s'", str(url))
44
+ self.__url = url
45
+ self.__traversal_source = traversal_source
46
+ self.__protocol_factory = protocol_factory
47
+ self.__transport_factory = transport_factory
48
+ self.__pool_size = pool_size
49
+ self.__max_workers = max_workers
50
+ self.__username = username
51
+ self.__password = password
52
+ self.__kerberized_service = kerberized_service
53
+ self.__message_serializer = message_serializer
54
+ self.__graphson_reader = graphson_reader
55
+ self.__graphson_writer = graphson_writer
56
+ self.__headers = headers
57
+ self.__session = session
58
+ self.__enable_user_agent_on_connect = enable_user_agent_on_connect
59
+ self.__enable_compression = enable_compression
60
+ self.__transport_kwargs = transport_kwargs
61
+
62
+ # keeps a list of sessions that have been spawned from this DriverRemoteConnection
63
+ # so that they can be closed if this parent session is closed.
64
+ self.__spawned_sessions = []
65
+
66
+ if message_serializer is None and graphson_reader is not None and graphson_writer is not None:
67
+ message_serializer = serializer.GraphSONMessageSerializer(
68
+ reader=graphson_reader,
69
+ writer=graphson_writer)
70
+ self._client = client.Client(url, traversal_source,
71
+ protocol_factory=protocol_factory,
72
+ transport_factory=transport_factory,
73
+ pool_size=pool_size,
74
+ max_workers=max_workers,
75
+ message_serializer=message_serializer,
76
+ username=username,
77
+ password=password,
78
+ kerberized_service=kerberized_service,
79
+ headers=headers,
80
+ session=session,
81
+ enable_user_agent_on_connect=enable_user_agent_on_connect,
82
+ enable_compression=enable_compression,
83
+ **transport_kwargs)
84
+ self._url = self._client._url
85
+ self._traversal_source = self._client._traversal_source
86
+
87
+ def close(self):
88
+ # close this client and any DriverRemoteConnection instances spawned from this one
89
+ # for a session
90
+ if len(self.__spawned_sessions) > 0:
91
+ log.info("closing spawned sessions from DriverRemoteConnection with url '%s'", str(self._url))
92
+ for spawned_session in self.__spawned_sessions:
93
+ spawned_session.close()
94
+ self.__spawned_sessions.clear()
95
+
96
+ if self.__session:
97
+ log.info("closing DriverRemoteConnection with url '%s' with session '%s'",
98
+ str(self._url), str(self.__session))
99
+ else:
100
+ log.info("closing DriverRemoteConnection with url '%s'", str(self._url))
101
+
102
+ self._client.close()
103
+
104
+ def submit(self, bytecode):
105
+ log.debug("submit with bytecode '%s'", str(bytecode))
106
+ result_set = self._client.submit(bytecode, request_options=self._extract_request_options(bytecode))
107
+ results = result_set.all().result()
108
+ return RemoteTraversal(iter(results))
109
+
110
+ def submitAsync(self, message, bindings=None, request_options=None):
111
+ warnings.warn(
112
+ "gremlin_python.driver.driver_remote_connection.DriverRemoteConnection.submitAsync will be replaced by "
113
+ "gremlin_python.driver.driver_remote_connection.DriverRemoteConnection.submit_async.",
114
+ DeprecationWarning)
115
+ self.submit_async(message, bindings, request_options)
116
+
117
+ def submit_async(self, bytecode):
118
+ log.debug("submit_async with bytecode '%s'", str(bytecode))
119
+ future = Future()
120
+ future_result_set = self._client.submit_async(bytecode, request_options=self._extract_request_options(bytecode))
121
+
122
+ def cb(f):
123
+ try:
124
+ result_set = f.result()
125
+ results = result_set.all().result()
126
+ future.set_result(RemoteTraversal(iter(results)))
127
+ except Exception as e:
128
+ future.set_exception(e)
129
+
130
+ future_result_set.add_done_callback(cb)
131
+ return future
132
+
133
+ def is_closed(self):
134
+ return self._client.is_closed()
135
+
136
+ def is_session_bound(self):
137
+ return self.__session is not None
138
+
139
+ def create_session(self):
140
+ log.info("Creating session based connection")
141
+ if self.is_session_bound():
142
+ raise Exception('Connection is already bound to a session - child sessions are not allowed')
143
+ conn = DriverRemoteConnection(self.__url,
144
+ traversal_source=self.__traversal_source,
145
+ protocol_factory=self.__protocol_factory,
146
+ transport_factory=self.__transport_factory,
147
+ pool_size=self.__pool_size,
148
+ max_workers=self.__max_workers,
149
+ username=self.__username,
150
+ password=self.__password,
151
+ kerberized_service=self.__kerberized_service,
152
+ message_serializer=self.__message_serializer,
153
+ graphson_reader=self.__graphson_reader,
154
+ graphson_writer=self.__graphson_writer,
155
+ headers=self.__headers,
156
+ session=uuid.uuid4(),
157
+ enable_user_agent_on_connect=self.__enable_user_agent_on_connect,
158
+ **self.__transport_kwargs)
159
+ self.__spawned_sessions.append(conn)
160
+ return conn
161
+
162
+ def remove_session(self, session_based_connection):
163
+ session_based_connection.close()
164
+ self.__spawned_sessions.remove(session_based_connection)
165
+
166
+ def commit(self):
167
+ log.info("Submitting commit graph operation.")
168
+ return self._client.submit(Bytecode.GraphOp.commit())
169
+
170
+ def rollback(self):
171
+ log.info("Submitting rollback graph operation.")
172
+ return self._client.submit(Bytecode.GraphOp.rollback())
173
+
174
+ @staticmethod
175
+ def _extract_request_options(bytecode):
176
+ options_strategy = next((x for x in bytecode.source_instructions
177
+ if x[0] == "withStrategies" and type(x[1]) is OptionsStrategy), None)
178
+ request_options = None
179
+ if options_strategy:
180
+ allowed_keys = ['evaluationTimeout', 'scriptEvaluationTimeout', 'batchSize', 'requestId', 'userAgent']
181
+ request_options = {allowed: options_strategy[1].configuration[allowed] for allowed in allowed_keys
182
+ if allowed in options_strategy[1].configuration}
183
+ return request_options
@@ -0,0 +1,259 @@
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
+ import json
20
+ import logging
21
+ import abc
22
+ import base64
23
+ import struct
24
+
25
+ # import kerberos Optional dependency imported in relevant codeblock
26
+
27
+ from gremlin_python.driver import request
28
+ from gremlin_python.driver.resultset import ResultSet
29
+ from gremlin_python.process.translator import Translator
30
+ from gremlin_python.process.traversal import Bytecode
31
+
32
+ log = logging.getLogger("gremlinpython")
33
+
34
+ __author__ = 'David M. Brown (davebshow@gmail.com)'
35
+
36
+
37
+ class GremlinServerError(Exception):
38
+ def __init__(self, status):
39
+ super(GremlinServerError, self).__init__('{0}: {1}'.format(status['code'], status['message']))
40
+ self._status_attributes = status['attributes']
41
+ self.status_code = status['code']
42
+ self.status_message = status['message']
43
+
44
+ @property
45
+ def status_attributes(self):
46
+ return self._status_attributes
47
+
48
+
49
+ class ConfigurationError(Exception):
50
+ pass
51
+
52
+
53
+ class AbstractBaseProtocol(metaclass=abc.ABCMeta):
54
+
55
+ @abc.abstractmethod
56
+ def connection_made(self, transport):
57
+ self._transport = transport
58
+
59
+ @abc.abstractmethod
60
+ def data_received(self, message, results_dict):
61
+ pass
62
+
63
+ @abc.abstractmethod
64
+ def write(self, request_id, request_message):
65
+ pass
66
+
67
+
68
+ class GremlinServerWSProtocol(AbstractBaseProtocol):
69
+ QOP_AUTH_BIT = 1
70
+ _kerberos_context = None
71
+ _max_content_length = 10 * 1024 * 1024
72
+
73
+ def __init__(self,
74
+ message_serializer,
75
+ username='', password='',
76
+ kerberized_service='',
77
+ max_content_length=10 * 1024 * 1024):
78
+ self._message_serializer = message_serializer
79
+ self._username = username
80
+ self._password = password
81
+ self._kerberized_service = kerberized_service
82
+ self._max_content_length = max_content_length
83
+
84
+ def connection_made(self, transport):
85
+ super(GremlinServerWSProtocol, self).connection_made(transport)
86
+
87
+ def write(self, request_id, request_message):
88
+ message = self._message_serializer.serialize_message(request_id, request_message)
89
+ self._transport.write(message)
90
+
91
+ def data_received(self, message, results_dict):
92
+ # if Gremlin Server cuts off then we get a None for the message
93
+ if message is None:
94
+ log.error("Received empty message from server.")
95
+ raise GremlinServerError({'code': 500,
96
+ 'message': 'Server disconnected - please try to reconnect', 'attributes': {}})
97
+
98
+ message = self._message_serializer.deserialize_message(message)
99
+ request_id = message['requestId']
100
+ result_set = results_dict[request_id] if request_id in results_dict else ResultSet(None, None)
101
+ status_code = message['status']['code']
102
+ aggregate_to = message['result']['meta'].get('aggregateTo', 'list')
103
+ data = message['result']['data']
104
+ result_set.aggregate_to = aggregate_to
105
+ if status_code == 407:
106
+ if self._username and self._password:
107
+ auth_bytes = b''.join([b'\x00', self._username.encode('utf-8'),
108
+ b'\x00', self._password.encode('utf-8')])
109
+ auth = base64.b64encode(auth_bytes)
110
+ request_message = request.RequestMessage(
111
+ 'traversal', 'authentication', {'sasl': auth.decode()})
112
+ elif self._kerberized_service:
113
+ request_message = self._kerberos_received(message)
114
+ else:
115
+ error_message = 'Gremlin server requires authentication credentials in DriverRemoteConnection. ' \
116
+ 'For basic authentication provide username and password. ' \
117
+ 'For kerberos authentication provide the kerberized_service parameter.'
118
+ log.error(error_message)
119
+ raise ConfigurationError(error_message)
120
+ self.write(request_id, request_message)
121
+ data = self._transport.read()
122
+ # Allow for auth handshake with multiple steps
123
+ return self.data_received(data, results_dict)
124
+ elif status_code == 204:
125
+ result_set.stream.put_nowait([])
126
+ del results_dict[request_id]
127
+ return status_code
128
+ elif status_code in [200, 206]:
129
+ result_set.stream.put_nowait(data)
130
+ if status_code == 200:
131
+ result_set.status_attributes = message['status']['attributes']
132
+ del results_dict[request_id]
133
+ return status_code
134
+ else:
135
+ # This message is going to be huge and kind of hard to read, but in the event of an error,
136
+ # it can provide invaluable info, so space it out appropriately.
137
+ log.error("\r\nReceived error message '%s'\r\n\r\nWith results dictionary '%s'",
138
+ str(message), str(results_dict))
139
+ del results_dict[request_id]
140
+ raise GremlinServerError(message['status'])
141
+
142
+ def _kerberos_received(self, message):
143
+ # Inspired by: https://github.com/thobbs/pure-sasl/blob/0.6.2/puresasl/mechanisms.py
144
+ # https://github.com/thobbs/pure-sasl/blob/0.6.2/LICENSE
145
+ try:
146
+ import kerberos
147
+ except ImportError:
148
+ raise ImportError('Please install gremlinpython[kerberos].')
149
+
150
+ # First pass: get service granting ticket and return it to gremlin-server
151
+ if not self._kerberos_context:
152
+ try:
153
+ _, kerberos_context = kerberos.authGSSClientInit(
154
+ self._kerberized_service, gssflags=kerberos.GSS_C_MUTUAL_FLAG)
155
+ kerberos.authGSSClientStep(kerberos_context, '')
156
+ auth = kerberos.authGSSClientResponse(kerberos_context)
157
+ self._kerberos_context = kerberos_context
158
+ except kerberos.KrbError as e:
159
+ raise ConfigurationError(
160
+ 'Kerberos authentication requires a valid service name in DriverRemoteConnection, '
161
+ 'as well as a valid tgt (export KRB5CCNAME) or keytab (export KRB5_KTNAME): ' + str(e))
162
+ return request.RequestMessage('', 'authentication', {'sasl': auth})
163
+
164
+ # Second pass: completion of authentication
165
+ sasl_response = message['status']['attributes']['sasl']
166
+ if not self._username:
167
+ result_code = kerberos.authGSSClientStep(self._kerberos_context, sasl_response)
168
+ if result_code == kerberos.AUTH_GSS_COMPLETE:
169
+ self._username = kerberos.authGSSClientUserName(self._kerberos_context)
170
+ return request.RequestMessage('', 'authentication', {'sasl': ''})
171
+
172
+ # Third pass: sasl quality of protection (qop) handshake
173
+
174
+ # Gremlin-server Krb5Authenticator only supports qop=QOP_AUTH; use ssl for confidentiality.
175
+ # Handshake content format:
176
+ # byte 0: the selected qop. 1==auth, 2==auth-int, 4==auth-conf
177
+ # byte 1-3: the max length for any buffer sent back and forth on this connection. (big endian)
178
+ # the rest of the buffer: the authorization user name in UTF-8 - not null terminated.
179
+ kerberos.authGSSClientUnwrap(self._kerberos_context, sasl_response)
180
+ data = kerberos.authGSSClientResponse(self._kerberos_context)
181
+ plaintext_data = base64.b64decode(data)
182
+ assert len(plaintext_data) == 4, "Unexpected response from gremlin server sasl handshake"
183
+ word, = struct.unpack('!I', plaintext_data)
184
+ qop_bits = word >> 24
185
+ assert self.QOP_AUTH_BIT & qop_bits, "Unexpected sasl qop level received from gremlin server"
186
+
187
+ name_length = len(self._username)
188
+ fmt = '!I' + str(name_length) + 's'
189
+ word = self.QOP_AUTH_BIT << 24 | self._max_content_length
190
+ out = struct.pack(fmt, word, self._username.encode("utf-8"), )
191
+ encoded = base64.b64encode(out).decode('ascii')
192
+ kerberos.authGSSClientWrap(self._kerberos_context, encoded)
193
+ auth = kerberos.authGSSClientResponse(self._kerberos_context)
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': {}})
@@ -0,0 +1,84 @@
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
+ import abc
20
+
21
+ from gremlin_python.process import traversal
22
+
23
+ __author__ = 'Marko A. Rodriguez (http://markorodriguez.com), Lyndon Bauto (lyndonb@bitquilltech.com)'
24
+
25
+
26
+ class RemoteConnection(object, metaclass=abc.ABCMeta):
27
+ def __init__(self, url, traversal_source):
28
+ self._url = url
29
+ self._traversal_source = traversal_source
30
+
31
+ @property
32
+ def url(self):
33
+ return self._url
34
+
35
+ @property
36
+ def traversal_source(self):
37
+ return self._traversal_source
38
+
39
+ @abc.abstractmethod
40
+ def submit(self, bytecode):
41
+ pass
42
+
43
+ def is_closed(self):
44
+ raise Exception('is_closed() must be implemented')
45
+
46
+ def is_session_bound(self):
47
+ return False
48
+
49
+ def create_session(self):
50
+ raise Exception('createSession() must be implemented');
51
+
52
+ def commit(self):
53
+ raise Exception('commit() must be implemented')
54
+
55
+ def rollback(self):
56
+ raise Exception('rollback() must be implemented')
57
+
58
+ def __repr__(self):
59
+ return "remoteconnection[" + self._url + "," + self._traversal_source + "]"
60
+
61
+
62
+ class RemoteTraversal(traversal.Traversal):
63
+ def __init__(self, traversers):
64
+ super(RemoteTraversal, self).__init__(None, None, None)
65
+ self.traversers = traversers
66
+
67
+
68
+ class RemoteStrategy(traversal.TraversalStrategy):
69
+ def __init__(self, remote_connection):
70
+ # Gave this a fqcn that has a local "py:" prefix since this strategy isn't sent as bytecode to the server.
71
+ # this is a sort of local-only strategy that actually executes client side. not sure if this prefix is the
72
+ # right way to name this or not, but it should have a name to identify it.
73
+ traversal.TraversalStrategy.__init__(self, fqcn="py:RemoteStrategy")
74
+ self.remote_connection = remote_connection
75
+
76
+ def apply(self, traversal):
77
+ if traversal.traversers is None:
78
+ remote_traversal = self.remote_connection.submit(traversal.bytecode)
79
+ traversal.remote_results = remote_traversal
80
+ traversal.traversers = remote_traversal.traversers
81
+
82
+ def apply_async(self, traversal):
83
+ if traversal.traversers is None:
84
+ traversal.remote_results = self.remote_connection.submit_async(traversal.bytecode)
@@ -0,0 +1,25 @@
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
+ import collections
20
+
21
+ __author__ = 'David M. Brown (davebshow@gmail.com)'
22
+
23
+
24
+ RequestMessage = collections.namedtuple(
25
+ 'RequestMessage', ['processor', 'op', 'args'])
@@ -0,0 +1,100 @@
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
+ from concurrent.futures import Future
20
+
21
+ __author__ = 'David M. Brown (davebshow@gmail.com)'
22
+
23
+
24
+ class ResultSet:
25
+
26
+ def __init__(self, stream, request_id):
27
+ self._stream = stream
28
+ self._request_id = request_id
29
+ self._done = None
30
+ self._aggregate_to = None
31
+ self._status_attributes = {}
32
+
33
+ @property
34
+ def aggregate_to(self):
35
+ return self._aggregate_to
36
+
37
+ @aggregate_to.setter
38
+ def aggregate_to(self, val):
39
+ self._aggregate_to = val
40
+
41
+ @property
42
+ def status_attributes(self):
43
+ return self._status_attributes
44
+
45
+ @status_attributes.setter
46
+ def status_attributes(self, val):
47
+ self._status_attributes = val
48
+
49
+ @property
50
+ def request_id(self):
51
+ return self._request_id
52
+
53
+ @property
54
+ def stream(self):
55
+ return self._stream
56
+
57
+ def __iter__(self):
58
+ return self
59
+
60
+ def __next__(self):
61
+ result = self.one()
62
+ if not result:
63
+ raise StopIteration
64
+ return result
65
+
66
+ def next(self):
67
+ return self.__next__()
68
+
69
+ @property
70
+ def done(self):
71
+ return self._done
72
+
73
+ @done.setter
74
+ def done(self, future):
75
+ self._done = future
76
+
77
+ def one(self):
78
+ while not self.done.done():
79
+ if not self.stream.empty():
80
+ return self.stream.get_nowait()
81
+ if not self.stream.empty():
82
+ return self.stream.get_nowait()
83
+ return self.done.result()
84
+
85
+ def all(self):
86
+ future = Future()
87
+
88
+ def cb(f):
89
+ try:
90
+ f.result()
91
+ except Exception as e:
92
+ future.set_exception(e)
93
+ else:
94
+ results = []
95
+ while not self.stream.empty():
96
+ results += self.stream.get_nowait()
97
+ future.set_result(results)
98
+
99
+ self.done.add_done_callback(cb)
100
+ return future