gremlinpython 3.8.1__tar.gz → 4.0.0b2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (45) hide show
  1. {gremlinpython-3.8.1/gremlinpython.egg-info → gremlinpython-4.0.0b2}/PKG-INFO +6 -4
  2. {gremlinpython-3.8.1 → gremlinpython-4.0.0b2}/README.rst +1 -1
  3. {gremlinpython-3.8.1 → gremlinpython-4.0.0b2}/gremlin_python/__init__.py +1 -1
  4. gremlinpython-4.0.0b2/gremlin_python/driver/aiohttp/transport.py +182 -0
  5. gremlinpython-4.0.0b2/gremlin_python/driver/auth.py +55 -0
  6. {gremlinpython-3.8.1 → gremlinpython-4.0.0b2}/gremlin_python/driver/client.py +45 -70
  7. {gremlinpython-3.8.1 → gremlinpython-4.0.0b2}/gremlin_python/driver/connection.py +20 -18
  8. gremlinpython-4.0.0b2/gremlin_python/driver/driver_remote_connection.py +132 -0
  9. gremlinpython-4.0.0b2/gremlin_python/driver/protocol.py +175 -0
  10. {gremlinpython-3.8.1 → gremlinpython-4.0.0b2}/gremlin_python/driver/remote_connection.py +3 -9
  11. {gremlinpython-3.8.1 → gremlinpython-4.0.0b2}/gremlin_python/driver/request.py +4 -2
  12. {gremlinpython-3.8.1 → gremlinpython-4.0.0b2}/gremlin_python/driver/resultset.py +1 -15
  13. gremlinpython-4.0.0b2/gremlin_python/driver/serializer.py +210 -0
  14. {gremlinpython-3.8.1 → gremlinpython-4.0.0b2}/gremlin_python/driver/useragent.py +1 -1
  15. {gremlinpython-3.8.1 → gremlinpython-4.0.0b2}/gremlin_python/process/graph_traversal.py +387 -383
  16. {gremlinpython-3.8.1 → gremlinpython-4.0.0b2}/gremlin_python/process/traversal.py +397 -150
  17. {gremlinpython-3.8.1 → gremlinpython-4.0.0b2}/gremlin_python/statics.py +0 -9
  18. {gremlinpython-3.8.1 → gremlinpython-4.0.0b2}/gremlin_python/structure/graph.py +24 -4
  19. gremlinpython-3.8.1/gremlin_python/structure/io/graphbinaryV1.py → gremlinpython-4.0.0b2/gremlin_python/structure/io/graphbinaryV4.py +203 -499
  20. gremlinpython-3.8.1/gremlin_python/structure/io/graphsonV3d0.py → gremlinpython-4.0.0b2/gremlin_python/structure/io/graphsonV4.py +66 -262
  21. {gremlinpython-3.8.1 → gremlinpython-4.0.0b2}/gremlin_python/structure/io/util.py +38 -0
  22. {gremlinpython-3.8.1 → gremlinpython-4.0.0b2/gremlinpython.egg-info}/PKG-INFO +6 -4
  23. {gremlinpython-3.8.1 → gremlinpython-4.0.0b2}/gremlinpython.egg-info/SOURCES.txt +3 -4
  24. {gremlinpython-3.8.1 → gremlinpython-4.0.0b2}/gremlinpython.egg-info/requires.txt +7 -1
  25. {gremlinpython-3.8.1 → gremlinpython-4.0.0b2}/pyproject.toml +4 -2
  26. gremlinpython-3.8.1/gremlin_python/driver/aiohttp/transport.py +0 -244
  27. gremlinpython-3.8.1/gremlin_python/driver/driver_remote_connection.py +0 -183
  28. gremlinpython-3.8.1/gremlin_python/driver/protocol.py +0 -259
  29. gremlinpython-3.8.1/gremlin_python/driver/serializer.py +0 -294
  30. gremlinpython-3.8.1/gremlin_python/process/translator.py +0 -297
  31. gremlinpython-3.8.1/gremlin_python/structure/io/graphsonV2d0.py +0 -669
  32. {gremlinpython-3.8.1 → gremlinpython-4.0.0b2}/LICENSE +0 -0
  33. {gremlinpython-3.8.1 → gremlinpython-4.0.0b2}/MANIFEST.in +0 -0
  34. {gremlinpython-3.8.1 → gremlinpython-4.0.0b2}/NOTICE +0 -0
  35. {gremlinpython-3.8.1 → gremlinpython-4.0.0b2}/gremlin_python/driver/__init__.py +0 -0
  36. {gremlinpython-3.8.1 → gremlinpython-4.0.0b2}/gremlin_python/driver/aiohttp/__init__.py +0 -0
  37. {gremlinpython-3.8.1 → gremlinpython-4.0.0b2}/gremlin_python/driver/transport.py +0 -0
  38. {gremlinpython-3.8.1 → gremlinpython-4.0.0b2}/gremlin_python/process/__init__.py +0 -0
  39. {gremlinpython-3.8.1 → gremlinpython-4.0.0b2}/gremlin_python/process/anonymous_traversal.py +0 -0
  40. {gremlinpython-3.8.1 → gremlinpython-4.0.0b2}/gremlin_python/process/strategies.py +0 -0
  41. {gremlinpython-3.8.1 → gremlinpython-4.0.0b2}/gremlin_python/structure/__init__.py +0 -0
  42. {gremlinpython-3.8.1 → gremlinpython-4.0.0b2}/gremlin_python/structure/io/__init__.py +0 -0
  43. {gremlinpython-3.8.1 → gremlinpython-4.0.0b2}/gremlinpython.egg-info/dependency_links.txt +0 -0
  44. {gremlinpython-3.8.1 → gremlinpython-4.0.0b2}/gremlinpython.egg-info/top_level.txt +0 -0
  45. {gremlinpython-3.8.1 → gremlinpython-4.0.0b2}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: gremlinpython
3
- Version: 3.8.1
3
+ Version: 4.0.0b2
4
4
  Summary: Gremlin-Python for Apache TinkerPop
5
5
  Maintainer-email: Apache TinkerPop <dev@tinkerpop.apache.org>
6
6
  License: Apache 2
@@ -17,9 +17,11 @@ Requires-Dist: nest_asyncio
17
17
  Requires-Dist: aiohttp<4.0.0,>=3.8.0
18
18
  Requires-Dist: aenum<4.0.0,>=1.4.5
19
19
  Requires-Dist: isodate<1.0.0,>=0.6.0
20
- Requires-Dist: async-timeout<5.0.0,>=4.0.3
20
+ Requires-Dist: boto3
21
+ Requires-Dist: botocore
22
+ Requires-Dist: async-timeout<5.0,>=4.0.3; python_version < "3.11"
21
23
  Provides-Extra: kerberos
22
- Requires-Dist: kerberos<2.0.0,>=1.3.0; extra == "kerberos"
24
+ Requires-Dist: kerberos<2.0.0,>=1.3.0; sys_platform != "win32" and extra == "kerberos"
23
25
  Provides-Extra: ujson
24
26
  Requires-Dist: ujson>=2.0.0; extra == "ujson"
25
27
  Provides-Extra: test
@@ -38,7 +40,7 @@ Dynamic: license-file
38
40
  .. "License"); you may not use this file except in compliance
39
41
  .. with the License. You may obtain a copy of the License at
40
42
  ..
41
- .. http://www.apache.org/licenses/LICENSE-2.0
43
+ .. http://www.apache.org/licenses/LICENSE-2.0
42
44
  ..
43
45
  .. Unless required by applicable law or agreed to in writing,
44
46
  .. software distributed under the License is distributed on an
@@ -6,7 +6,7 @@
6
6
  .. "License"); you may not use this file except in compliance
7
7
  .. with the License. You may obtain a copy of the License at
8
8
  ..
9
- .. http://www.apache.org/licenses/LICENSE-2.0
9
+ .. http://www.apache.org/licenses/LICENSE-2.0
10
10
  ..
11
11
  .. Unless required by applicable law or agreed to in writing,
12
12
  .. software distributed under the License is distributed on an
@@ -18,4 +18,4 @@
18
18
  #
19
19
 
20
20
  __author__ = 'Marko A. Rodriguez (http://markorodriguez.com)'
21
- __version__ = '3.8.1'
21
+ __version__ = '4.0.0b2'
@@ -0,0 +1,182 @@
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
+
21
+ import aiohttp
22
+ import asyncio
23
+ import sys
24
+
25
+ if sys.version_info >= (3, 11):
26
+ import asyncio as async_timeout
27
+ else:
28
+ import async_timeout
29
+ from aiohttp import ClientPayloadError
30
+ from gremlin_python.driver.protocol import GremlinServerError
31
+ from gremlin_python.driver.transport import AbstractBaseTransport
32
+
33
+ __author__ = 'Lyndon Bauto (lyndonb@bitquilltech.com)'
34
+
35
+
36
+ class AiohttpHTTPTransport(AbstractBaseTransport):
37
+ nest_asyncio_applied = False
38
+
39
+ def __init__(self, call_from_event_loop=None, read_timeout=None, write_timeout=None, **kwargs):
40
+ if call_from_event_loop is not None and call_from_event_loop and not AiohttpHTTPTransport.nest_asyncio_applied:
41
+ """
42
+ The AiohttpTransport implementation uses the asyncio event loop. Because of this, it cannot be called
43
+ within an event loop without nest_asyncio. If the code is ever refactored so that it can be called
44
+ within an event loop this import and call can be removed. Without this, applications which use the
45
+ event loop to call gremlin-python (such as Jupyter) will not work.
46
+ """
47
+ import nest_asyncio
48
+ nest_asyncio.apply()
49
+ AiohttpHTTPTransport.nest_asyncio_applied = True
50
+
51
+ # Start event loop and initialize client session and response to None
52
+ self._loop = asyncio.new_event_loop()
53
+ self._client_session = None
54
+ self._http_req_resp = None
55
+ self._enable_ssl = False
56
+ self._url = None
57
+
58
+ # Set all inner variables to parameters passed in.
59
+ self._aiohttp_kwargs = kwargs
60
+ self._write_timeout = write_timeout
61
+ self._read_timeout = read_timeout
62
+ if "max_content_length" in self._aiohttp_kwargs:
63
+ self._max_content_len = self._aiohttp_kwargs.pop("max_content_length")
64
+ else:
65
+ self._max_content_len = 10 * 1024 * 1024
66
+ if "ssl_options" in self._aiohttp_kwargs:
67
+ self._ssl_context = self._aiohttp_kwargs.pop("ssl_options")
68
+ self._enable_ssl = True
69
+
70
+ def __del__(self):
71
+ # Close will only actually close if things are left open, so this is safe to call.
72
+ # Clean up any connection resources and close the event loop.
73
+ self.close()
74
+
75
+ def connect(self, url, headers=None):
76
+ self._url = url
77
+ # Inner function to perform async connect.
78
+ async def async_connect():
79
+ # Start client session and use it to send all HTTP requests. Headers can be set here.
80
+ if self._enable_ssl:
81
+ # ssl context is established through tcp connector
82
+ tcp_conn = aiohttp.TCPConnector(ssl_context=self._ssl_context)
83
+ self._client_session = aiohttp.ClientSession(connector=tcp_conn,
84
+ headers=headers, loop=self._loop)
85
+ else:
86
+ self._client_session = aiohttp.ClientSession(headers=headers, loop=self._loop)
87
+
88
+ # Execute the async connect synchronously.
89
+ self._loop.run_until_complete(async_connect())
90
+
91
+ def write(self, message):
92
+ # Inner function to perform async write.
93
+ async def async_write():
94
+ # To pass url into message for request authentication processing
95
+ message.update({'url': self._url})
96
+ if message['auth']:
97
+ message['auth'](message)
98
+
99
+ async with async_timeout.timeout(self._write_timeout):
100
+ self._http_req_resp = await self._client_session.post(url=self._url,
101
+ data=message['payload'],
102
+ headers=message['headers'],
103
+ **self._aiohttp_kwargs)
104
+
105
+ # Execute the async write synchronously.
106
+ self._loop.run_until_complete(async_write())
107
+
108
+ def read(self, stream_chunk=None):
109
+ if not stream_chunk:
110
+ '''
111
+ GraphSON does not support streaming deserialization, we are aggregating data and bypassing streamed
112
+ deserialization while GraphSON is enabled for testing. Remove after GraphSON is removed.
113
+ '''
114
+ async def async_read():
115
+ async with async_timeout.timeout(self._read_timeout):
116
+ data_buffer = b""
117
+ async for data, end_of_http_chunk in self._http_req_resp.content.iter_chunks():
118
+ try:
119
+ data_buffer += data
120
+ except ClientPayloadError:
121
+ # server disconnect during streaming will cause ClientPayLoadError from aiohttp
122
+ raise GremlinServerError({'code': 500,
123
+ 'message': 'Server disconnected - please try to reconnect',
124
+ 'exception': ClientPayloadError})
125
+ if self._max_content_len and len(
126
+ data_buffer) > self._max_content_len:
127
+ raise Exception(f'Response size {len(data_buffer)} exceeds limit {self._max_content_len} bytes')
128
+ if self._http_req_resp.headers.get('content-type') == 'application/json':
129
+ message = json.loads(data_buffer.decode('utf-8'))
130
+ err = message.get('message')
131
+ raise Exception(f'Server disconnected with error message: "{err}" - please try to reconnect')
132
+ return data_buffer
133
+ return self._loop.run_until_complete(async_read())
134
+ # raise Exception('missing handling of streamed responses to protocol')
135
+
136
+ # Inner function to perform async read.
137
+ async def async_read():
138
+ # TODO: potentially refactor to just use streaming and remove transport/protocol
139
+ async with async_timeout.timeout(self._read_timeout):
140
+ read_completed = False
141
+ # aiohttp streaming may not iterate through one whole chunk if it's too large, need to buffer it
142
+ data_buffer = b""
143
+ async for data, end_of_http_chunk in self._http_req_resp.content.iter_chunks():
144
+ try:
145
+ data_buffer += data
146
+ if end_of_http_chunk:
147
+ if self._max_content_len and len(
148
+ data_buffer) > self._max_content_len:
149
+ raise Exception( # TODO: do we need proper exception class for this?
150
+ f'Response size {len(data_buffer)} exceeds limit {self._max_content_len} bytes')
151
+ stream_chunk(data_buffer, read_completed, self._http_req_resp.ok)
152
+ data_buffer = b""
153
+ except ClientPayloadError:
154
+ # server disconnect during streaming will cause ClientPayLoadError from aiohttp
155
+ # TODO: double check during refactoring
156
+ raise GremlinServerError({'code': 500,
157
+ 'message': 'Server disconnected - please try to reconnect',
158
+ 'exception': ClientPayloadError})
159
+ read_completed = True
160
+ stream_chunk(data_buffer, read_completed, self._http_req_resp.ok)
161
+
162
+ return self._loop.run_until_complete(async_read())
163
+
164
+ def close(self):
165
+ # Inner function to perform async close.
166
+ async def async_close():
167
+ if self._client_session is not None and not self._client_session.closed:
168
+ await self._client_session.close()
169
+ self._client_session = None
170
+
171
+ # If the loop is not closed (connection hasn't already been closed)
172
+ if not self._loop.is_closed():
173
+ # Execute the async close synchronously.
174
+ self._loop.run_until_complete(async_close())
175
+
176
+ # Close the event loop.
177
+ self._loop.close()
178
+
179
+ @property
180
+ def closed(self):
181
+ # Connection is closed when client session is closed.
182
+ return self._client_session.closed
@@ -0,0 +1,55 @@
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
+ def basic(username, password):
22
+ from aiohttp import BasicAuth as aiohttpBasicAuth
23
+
24
+ def apply(request):
25
+ return request['headers'].update({'authorization': aiohttpBasicAuth(username, password).encode()})
26
+
27
+ return apply
28
+
29
+
30
+ def sigv4(region, service):
31
+ import os
32
+ from boto3 import Session
33
+ from botocore.auth import SigV4Auth
34
+ from botocore.awsrequest import AWSRequest
35
+
36
+ def apply(request):
37
+ access_key = os.environ.get('AWS_ACCESS_KEY_ID', '')
38
+ secret_key = os.environ.get('AWS_SECRET_ACCESS_KEY', '')
39
+ session_token = os.environ.get('AWS_SESSION_TOKEN', '')
40
+
41
+ session = Session(
42
+ aws_access_key_id=access_key,
43
+ aws_secret_access_key=secret_key,
44
+ aws_session_token=session_token,
45
+ region_name=region
46
+ )
47
+
48
+ sigv4_request = AWSRequest(method="POST", url=request['url'], data=request['payload'])
49
+ SigV4Auth(session.get_credentials(), service, region).add_auth(sigv4_request)
50
+ request['headers'].update(sigv4_request.headers)
51
+ request['payload'] = sigv4_request.data
52
+ return request
53
+
54
+ return apply
55
+
@@ -19,11 +19,9 @@
19
19
  import logging
20
20
  import warnings
21
21
  import queue
22
- import re
23
22
  from concurrent.futures import ThreadPoolExecutor
24
23
 
25
24
  from gremlin_python.driver import connection, protocol, request, serializer
26
- from gremlin_python.process import traversal
27
25
 
28
26
  log = logging.getLogger("gremlinpython")
29
27
 
@@ -38,69 +36,51 @@ except ImportError:
38
36
  __author__ = 'David M. Brown (davebshow@gmail.com), Lyndon Bauto (lyndonb@bitquilltech.com)'
39
37
 
40
38
 
39
+ # TODO: remove session, update connection pooling, etc.
41
40
  class Client:
42
41
 
43
42
  def __init__(self, url, traversal_source, protocol_factory=None,
44
43
  transport_factory=None, pool_size=None, max_workers=None,
45
- message_serializer=None, username="", password="",
46
- kerberized_service="", headers=None, session=None,
47
- enable_user_agent_on_connect=True, enable_compression=False,
48
- **transport_kwargs):
44
+ request_serializer=serializer.GraphBinarySerializersV4(),
45
+ response_serializer=None, interceptors=None, auth=None,
46
+ headers=None, enable_user_agent_on_connect=True,
47
+ bulk_results=False, **transport_kwargs):
49
48
  log.info("Creating Client with url '%s'", url)
50
49
 
51
- # check via url that we are using http protocol
52
- self._use_http = re.search('^http', url)
53
-
54
50
  self._closed = False
55
51
  self._url = url
56
52
  self._headers = headers
57
53
  self._enable_user_agent_on_connect = enable_user_agent_on_connect
54
+ self._bulk_results = bulk_results
58
55
  self._traversal_source = traversal_source
59
- self._enable_compression = enable_compression
60
- if not self._use_http and "max_content_length" not in transport_kwargs:
56
+ if "max_content_length" not in transport_kwargs:
61
57
  transport_kwargs["max_content_length"] = 10 * 1024 * 1024
62
- if message_serializer is None:
63
- message_serializer = serializer.GraphBinarySerializersV1()
64
-
65
- self._message_serializer = message_serializer
66
- self._username = username
67
- self._password = password
68
- self._session = session
69
- self._session_enabled = (session is not None and session != "")
58
+ if response_serializer is None:
59
+ response_serializer = serializer.GraphBinarySerializersV4()
60
+
61
+ self._auth = auth
62
+ self._response_serializer = response_serializer
63
+
70
64
  if transport_factory is None:
71
65
  try:
72
- from gremlin_python.driver.aiohttp.transport import (
73
- AiohttpTransport, AiohttpHTTPTransport)
66
+ from gremlin_python.driver.aiohttp.transport import AiohttpHTTPTransport
74
67
  except ImportError:
75
68
  raise Exception("Please install AIOHTTP or pass "
76
69
  "custom transport factory")
77
70
  else:
78
71
  def transport_factory():
79
- if self._use_http:
80
- return AiohttpHTTPTransport(**transport_kwargs)
81
- else:
82
- return AiohttpTransport(enable_compression=enable_compression, **transport_kwargs)
72
+ if self._protocol_factory is None:
73
+ self._protocol_factory = protocol_factory
74
+ return AiohttpHTTPTransport(**transport_kwargs)
83
75
  self._transport_factory = transport_factory
76
+
84
77
  if protocol_factory is None:
85
78
  def protocol_factory():
86
- if self._use_http:
87
- return protocol.GremlinServerHTTPProtocol(
88
- self._message_serializer,
89
- username=self._username,
90
- password=self._password)
91
- else:
92
- return protocol.GremlinServerWSProtocol(
93
- self._message_serializer,
94
- username=self._username,
95
- password=self._password,
96
- kerberized_service=kerberized_service,
97
- max_content_length=transport_kwargs["max_content_length"])
79
+ return protocol.GremlinServerHTTPProtocol(
80
+ request_serializer, response_serializer, auth=self._auth,
81
+ interceptors=interceptors)
98
82
  self._protocol_factory = protocol_factory
99
- if self._session_enabled:
100
- if pool_size is None:
101
- pool_size = 1
102
- elif pool_size != 1:
103
- raise Exception("PoolSize must be 1 on session mode!")
83
+
104
84
  if pool_size is None:
105
85
  pool_size = 8
106
86
  self._pool_size = pool_size
@@ -117,6 +97,9 @@ class Client:
117
97
  @property
118
98
  def available_pool_size(self):
119
99
  return self._pool.qsize()
100
+
101
+ def response_serializer(self):
102
+ return self._response_serializer
120
103
 
121
104
  @property
122
105
  def executor(self):
@@ -140,8 +123,6 @@ class Client:
140
123
  if self._closed:
141
124
  return
142
125
 
143
- if self._session_enabled:
144
- self._close_session()
145
126
  log.info("Closing Client with url '%s'", self._url)
146
127
  while not self._pool.empty():
147
128
  conn = self._pool.get(True)
@@ -149,17 +130,6 @@ class Client:
149
130
  self._executor.shutdown()
150
131
  self._closed = True
151
132
 
152
- def _close_session(self):
153
- message = request.RequestMessage(
154
- processor='session', op='close',
155
- args={'session': str(self._session)})
156
- conn = self._pool.get(True)
157
- try:
158
- write_result_set = conn.write(message).result()
159
- return write_result_set.all().result() # wait for _receive() to finish
160
- except protocol.GremlinServerError:
161
- pass
162
-
163
133
  def _get_connection(self):
164
134
  protocol = self._protocol_factory()
165
135
  return connection.Connection(
@@ -182,25 +152,30 @@ class Client:
182
152
  raise Exception("Client is closed")
183
153
 
184
154
  log.debug("message '%s'", str(message))
185
- args = {'gremlin': message, 'aliases': {'g': self._traversal_source}}
186
- processor = ''
187
- op = 'eval'
188
- if isinstance(message, traversal.Bytecode):
189
- op = 'bytecode'
190
- processor = 'traversal'
155
+ fields = {'g': self._traversal_source}
191
156
 
157
+ # TODO: bindings is now part of request_options, evaluate the need to keep it separate in python.
158
+ # Note this bindings parameter only applies to string script submissions
192
159
  if isinstance(message, str) and bindings:
193
- args['bindings'] = bindings
194
-
195
- if self._session_enabled:
196
- args['session'] = str(self._session)
197
- processor = 'session'
160
+ fields['bindings'] = bindings
198
161
 
199
- if isinstance(message, traversal.Bytecode) or isinstance(message, str):
200
- log.debug("processor='%s', op='%s', args='%s'", str(processor), str(op), str(args))
201
- message = request.RequestMessage(processor=processor, op=op, args=args)
162
+ if isinstance(message, str):
163
+ log.debug("fields='%s', gremlin='%s'", str(fields), str(message))
164
+ message = request.RequestMessage(fields=fields, gremlin=message)
202
165
 
203
166
  conn = self._pool.get(True)
204
167
  if request_options:
205
- message.args.update(request_options)
168
+ message.fields.update({token: request_options[token] for token in request.Tokens
169
+ if token in request_options and token != 'bindings'})
170
+ if 'bindings' in request_options:
171
+ if 'bindings' in message.fields:
172
+ message.fields['bindings'].update(request_options['bindings'])
173
+ else:
174
+ message.fields['bindings'] = request_options['bindings']
175
+ if 'params' in request_options:
176
+ if 'bindings' in message.fields:
177
+ message.fields['bindings'].update(request_options['params'])
178
+ else:
179
+ message.fields['bindings'] = request_options['params']
180
+
206
181
  return conn.write(message)
@@ -14,7 +14,6 @@
14
14
  # KIND, either express or implied. See the License for the
15
15
  # specific language governing permissions and limitations
16
16
  # under the License.
17
- import uuid
18
17
  import queue
19
18
  from concurrent.futures import Future
20
19
 
@@ -26,7 +25,8 @@ __author__ = 'David M. Brown (davebshow@gmail.com)'
26
25
  class Connection:
27
26
 
28
27
  def __init__(self, url, traversal_source, protocol, transport_factory,
29
- executor, pool, headers=None, enable_user_agent_on_connect=True):
28
+ executor, pool, headers=None, enable_user_agent_on_connect=True,
29
+ bulk_results=False):
30
30
  self._url = url
31
31
  self._headers = headers
32
32
  self._traversal_source = traversal_source
@@ -35,11 +35,14 @@ class Connection:
35
35
  self._executor = executor
36
36
  self._transport = None
37
37
  self._pool = pool
38
- self._results = {}
38
+ self._result_set = None
39
39
  self._inited = False
40
40
  self._enable_user_agent_on_connect = enable_user_agent_on_connect
41
41
  if self._enable_user_agent_on_connect:
42
42
  self.__add_header(useragent.userAgentHeader, useragent.userAgent)
43
+ self._bulk_results = bulk_results
44
+ if self._bulk_results:
45
+ self.__add_header("bulkResults", "true")
43
46
 
44
47
  def connect(self):
45
48
  if self._transport:
@@ -56,17 +59,11 @@ class Connection:
56
59
  def write(self, request_message):
57
60
  if not self._inited:
58
61
  self.connect()
59
- if request_message.args.get("requestId"):
60
- request_id = str(request_message.args.get("requestId"))
61
- uuid.UUID(request_id) # Checks for proper UUID or else server will return an error.
62
- else:
63
- request_id = str(uuid.uuid4())
64
- result_set = resultset.ResultSet(queue.Queue(), request_id)
65
- self._results[request_id] = result_set
62
+ self._result_set = resultset.ResultSet(queue.Queue())
66
63
  # Create write task
67
64
  future = Future()
68
65
  future_write = self._executor.submit(
69
- self._protocol.write, request_id, request_message)
66
+ self._protocol.write, request_message)
70
67
 
71
68
  def cb(f):
72
69
  try:
@@ -77,22 +74,27 @@ class Connection:
77
74
  else:
78
75
  # Start receive task
79
76
  done = self._executor.submit(self._receive)
80
- result_set.done = done
81
- future.set_result(result_set)
77
+ self._result_set.done = done
78
+ future.set_result(self._result_set)
82
79
 
83
80
  future_write.add_done_callback(cb)
84
81
  return future
85
82
 
86
83
  def _receive(self):
87
84
  try:
88
- while True:
89
- data = self._transport.read()
90
- status_code = self._protocol.data_received(data, self._results)
91
- if status_code != 206:
92
- break
85
+ '''
86
+ GraphSON does not support streaming deserialization, we are aggregating data and bypassing streamed
87
+ deserialization while GraphSON is enabled for testing. Remove after GraphSON is removed.
88
+ '''
89
+ self._protocol.data_received_aggregate(self._transport.read(), self._result_set)
90
+ # re-enable streaming after graphSON removal
91
+ # self._transport.read(self.stream_chunk)
93
92
  finally:
94
93
  self._pool.put_nowait(self)
95
94
 
95
+ def stream_chunk(self, chunk_data, read_completed=None, http_req_resp=None):
96
+ self._protocol.data_received(chunk_data, self._result_set, read_completed, http_req_resp)
97
+
96
98
  def __add_header(self, key, value):
97
99
  if self._headers is None:
98
100
  self._headers = dict()