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,20 @@
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
+ __author__ = 'Marko A. Rodriguez (http://markorodriguez.com)'
@@ -0,0 +1,20 @@
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
+ version = '3.6.8'
20
+ timestamp = 1730222094
@@ -0,0 +1,20 @@
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
+ __author__ = 'Marko A. Rodriguez (http://markorodriguez.com)'
@@ -0,0 +1,18 @@
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
+ #
@@ -0,0 +1,242 @@
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 aiohttp
20
+ import asyncio
21
+ import async_timeout
22
+ from aiohttp import ClientResponseError
23
+
24
+ from gremlin_python.driver.transport import AbstractBaseTransport
25
+
26
+ __author__ = 'Lyndon Bauto (lyndonb@bitquilltech.com)'
27
+
28
+
29
+ class AiohttpTransport(AbstractBaseTransport):
30
+ nest_asyncio_applied = False
31
+
32
+ def __init__(self, call_from_event_loop=None, read_timeout=None, write_timeout=None, enable_compression=False,
33
+ **kwargs):
34
+ if call_from_event_loop is not None and call_from_event_loop and not AiohttpTransport.nest_asyncio_applied:
35
+ """
36
+ The AiohttpTransport implementation uses the asyncio event loop. Because of this, it cannot be called
37
+ within an event loop without nest_asyncio. If the code is ever refactored so that it can be called
38
+ within an event loop this import and call can be removed. Without this, applications which use the
39
+ event loop to call gremlin-python (such as Jupyter) will not work.
40
+ """
41
+ import nest_asyncio
42
+ nest_asyncio.apply()
43
+ AiohttpTransport.nest_asyncio_applied = True
44
+
45
+ # Start event loop and initialize websocket and client to None
46
+ self._loop = asyncio.new_event_loop()
47
+ self._websocket = None
48
+ self._client_session = None
49
+
50
+ # Set all inner variables to parameters passed in.
51
+ self._aiohttp_kwargs = kwargs
52
+ self._write_timeout = write_timeout
53
+ self._read_timeout = read_timeout
54
+ self._enable_compression = enable_compression
55
+ if "max_content_length" in self._aiohttp_kwargs:
56
+ self._aiohttp_kwargs["max_msg_size"] = self._aiohttp_kwargs.pop("max_content_length")
57
+ if "ssl_options" in self._aiohttp_kwargs:
58
+ self._aiohttp_kwargs["ssl"] = self._aiohttp_kwargs.pop("ssl_options")
59
+ if self._enable_compression and "compress" not in self._aiohttp_kwargs:
60
+ self._aiohttp_kwargs["compress"] = 15 # enable per-message deflate compression with 32k sliding window size
61
+
62
+ def __del__(self):
63
+ # Close will only actually close if things are left open, so this is safe to call.
64
+ # Clean up any connection resources and close the event loop.
65
+ self.close()
66
+
67
+ def connect(self, url, headers=None):
68
+ # Inner function to perform async connect.
69
+ async def async_connect():
70
+ # Start client session and use it to create a websocket with all the connection options provided.
71
+ self._client_session = aiohttp.ClientSession(loop=self._loop)
72
+ try:
73
+ self._websocket = await self._client_session.ws_connect(url, **self._aiohttp_kwargs, headers=headers)
74
+ except ClientResponseError as err:
75
+ # If 403, just send forbidden because in some cases this prints out a huge verbose message
76
+ # that includes credentials.
77
+ if err.status == 403:
78
+ raise Exception('Failed to connect to server: HTTP Error code 403 - Forbidden.')
79
+ else:
80
+ raise
81
+
82
+ # Execute the async connect synchronously.
83
+ self._loop.run_until_complete(async_connect())
84
+
85
+ def write(self, message):
86
+ # Inner function to perform async write.
87
+ async def async_write():
88
+ async with async_timeout.timeout(self._write_timeout):
89
+ await self._websocket.send_bytes(message)
90
+
91
+ # Execute the async write synchronously.
92
+ self._loop.run_until_complete(async_write())
93
+
94
+ def read(self):
95
+ # Inner function to perform async read.
96
+ async def async_read():
97
+ async with async_timeout.timeout(self._read_timeout):
98
+ return await self._websocket.receive()
99
+
100
+ # Execute the async read synchronously.
101
+ msg = self._loop.run_until_complete(async_read())
102
+
103
+ # Need to handle multiple potential message types.
104
+ if msg.type == aiohttp.WSMsgType.close:
105
+ # Server is closing connection, shutdown and throw exception.
106
+ self.close()
107
+ raise RuntimeError("Connection was closed by server.")
108
+ elif msg.type == aiohttp.WSMsgType.closed:
109
+ # Should not be possible since our loop and socket would be closed.
110
+ raise RuntimeError("Connection was already closed.")
111
+ elif msg.type == aiohttp.WSMsgType.error:
112
+ # Error on connection, try to convert message to a string in error.
113
+ raise RuntimeError("Received error on read: '" + str(msg.data) + "'")
114
+ elif msg.type == aiohttp.WSMsgType.text:
115
+ # Convert message to bytes.
116
+ data = msg.data.strip().encode('utf-8')
117
+ else:
118
+ # General handle, return byte data.
119
+ data = msg.data
120
+ return data
121
+
122
+ def close(self):
123
+ # Inner function to perform async close.
124
+ async def async_close():
125
+ if self._websocket is not None and not self._websocket.closed:
126
+ await self._websocket.close()
127
+ self._websocket = None
128
+
129
+ if self._client_session is not None and not self._client_session.closed:
130
+ await self._client_session.close()
131
+ self._client_session = None
132
+
133
+ # If the loop is not closed (connection hasn't already been closed)
134
+ if not self._loop.is_closed():
135
+ # Execute the async close synchronously.
136
+ self._loop.run_until_complete(async_close())
137
+
138
+ # Close the event loop.
139
+ self._loop.close()
140
+
141
+ @property
142
+ def closed(self):
143
+ # Connection is closed if either the websocket or the client session is closed.
144
+ return self._websocket.closed or self._client_session.closed
145
+
146
+
147
+ class AiohttpHTTPTransport(AbstractBaseTransport):
148
+ nest_asyncio_applied = False
149
+
150
+ def __init__(self, call_from_event_loop=None, read_timeout=None, write_timeout=None, **kwargs):
151
+ if call_from_event_loop is not None and call_from_event_loop and not AiohttpTransport.nest_asyncio_applied:
152
+ """
153
+ The AiohttpTransport implementation uses the asyncio event loop. Because of this, it cannot be called
154
+ within an event loop without nest_asyncio. If the code is ever refactored so that it can be called
155
+ within an event loop this import and call can be removed. Without this, applications which use the
156
+ event loop to call gremlin-python (such as Jupyter) will not work.
157
+ """
158
+ import nest_asyncio
159
+ nest_asyncio.apply()
160
+ AiohttpTransport.nest_asyncio_applied = True
161
+
162
+ # Start event loop and initialize client session and response to None
163
+ self._loop = asyncio.new_event_loop()
164
+ self._client_session = None
165
+ self._http_req_resp = None
166
+ self._enable_ssl = False
167
+
168
+ # Set all inner variables to parameters passed in.
169
+ self._aiohttp_kwargs = kwargs
170
+ self._write_timeout = write_timeout
171
+ self._read_timeout = read_timeout
172
+ if "ssl_options" in self._aiohttp_kwargs:
173
+ self._ssl_context = self._aiohttp_kwargs.pop("ssl_options")
174
+ self._enable_ssl = True
175
+
176
+ def __del__(self):
177
+ # Close will only actually close if things are left open, so this is safe to call.
178
+ # Clean up any connection resources and close the event loop.
179
+ self.close()
180
+
181
+ def connect(self, url, headers=None):
182
+ # Inner function to perform async connect.
183
+ async def async_connect():
184
+ # Start client session and use it to send all HTTP requests. Base url is the endpoint, headers are set here
185
+ # Base url can only parse basic url with no path, see https://github.com/aio-libs/aiohttp/issues/6647
186
+ if self._enable_ssl:
187
+ # ssl context is established through tcp connector
188
+ tcp_conn = aiohttp.TCPConnector(ssl_context=self._ssl_context)
189
+ self._client_session = aiohttp.ClientSession(connector=tcp_conn,
190
+ base_url=url, headers=headers, loop=self._loop)
191
+ else:
192
+ self._client_session = aiohttp.ClientSession(base_url=url, headers=headers, loop=self._loop)
193
+
194
+ # Execute the async connect synchronously.
195
+ self._loop.run_until_complete(async_connect())
196
+
197
+ def write(self, message):
198
+ # Inner function to perform async write.
199
+ async def async_write():
200
+ basic_auth = None
201
+ # basic password authentication for https connections
202
+ if message['auth']:
203
+ basic_auth = aiohttp.BasicAuth(message['auth']['username'], message['auth']['password'])
204
+ async with async_timeout.timeout(self._write_timeout):
205
+ self._http_req_resp = await self._client_session.post(url="/gremlin",
206
+ auth=basic_auth,
207
+ data=message['payload'],
208
+ headers=message['headers'],
209
+ **self._aiohttp_kwargs)
210
+
211
+ # Execute the async write synchronously.
212
+ self._loop.run_until_complete(async_write())
213
+
214
+ def read(self):
215
+ # Inner function to perform async read.
216
+ async def async_read():
217
+ async with async_timeout.timeout(self._read_timeout):
218
+ return {"content": await self._http_req_resp.read(),
219
+ "ok": self._http_req_resp.ok,
220
+ "status": self._http_req_resp.status}
221
+
222
+ return self._loop.run_until_complete(async_read())
223
+
224
+ def close(self):
225
+ # Inner function to perform async close.
226
+ async def async_close():
227
+ if self._client_session is not None and not self._client_session.closed:
228
+ await self._client_session.close()
229
+ self._client_session = None
230
+
231
+ # If the loop is not closed (connection hasn't already been closed)
232
+ if not self._loop.is_closed():
233
+ # Execute the async close synchronously.
234
+ self._loop.run_until_complete(async_close())
235
+
236
+ # Close the event loop.
237
+ self._loop.close()
238
+
239
+ @property
240
+ def closed(self):
241
+ # Connection is closed when client session is closed.
242
+ return self._client_session.closed
@@ -0,0 +1,206 @@
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
+ import warnings
21
+ import queue
22
+ import re
23
+ from concurrent.futures import ThreadPoolExecutor
24
+
25
+ from gremlin_python.driver import connection, protocol, request, serializer
26
+ from gremlin_python.process import traversal
27
+
28
+ log = logging.getLogger("gremlinpython")
29
+
30
+ # This is until concurrent.futures backport 3.1.0 release
31
+ try:
32
+ from multiprocessing import cpu_count
33
+ except ImportError:
34
+ # some platforms don't have multiprocessing
35
+ def cpu_count():
36
+ return None
37
+
38
+ __author__ = 'David M. Brown (davebshow@gmail.com), Lyndon Bauto (lyndonb@bitquilltech.com)'
39
+
40
+
41
+ class Client:
42
+
43
+ def __init__(self, url, traversal_source, protocol_factory=None,
44
+ 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):
49
+ log.info("Creating Client with url '%s'", url)
50
+
51
+ # check via url that we are using http protocol
52
+ self._use_http = re.search('^http', url)
53
+
54
+ self._closed = False
55
+ self._url = url
56
+ self._headers = headers
57
+ self._enable_user_agent_on_connect = enable_user_agent_on_connect
58
+ 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:
61
+ 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 != "")
70
+ if transport_factory is None:
71
+ try:
72
+ from gremlin_python.driver.aiohttp.transport import (
73
+ AiohttpTransport, AiohttpHTTPTransport)
74
+ except ImportError:
75
+ raise Exception("Please install AIOHTTP or pass "
76
+ "custom transport factory")
77
+ else:
78
+ def transport_factory():
79
+ if self._use_http:
80
+ return AiohttpHTTPTransport(**transport_kwargs)
81
+ else:
82
+ return AiohttpTransport(enable_compression=enable_compression, **transport_kwargs)
83
+ self._transport_factory = transport_factory
84
+ if protocol_factory is None:
85
+ 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"])
98
+ 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!")
104
+ if pool_size is None:
105
+ pool_size = 8
106
+ self._pool_size = pool_size
107
+ # This is until concurrent.futures backport 3.1.0 release
108
+ if max_workers is None:
109
+ # If your application is overlapping Gremlin I/O on multiple threads
110
+ # consider passing kwarg max_workers = (cpu_count() or 1) * 5
111
+ max_workers = pool_size
112
+ self._executor = ThreadPoolExecutor(max_workers=max_workers)
113
+ # Threadsafe queue
114
+ self._pool = queue.Queue()
115
+ self._fill_pool()
116
+
117
+ @property
118
+ def available_pool_size(self):
119
+ return self._pool.qsize()
120
+
121
+ @property
122
+ def executor(self):
123
+ return self._executor
124
+
125
+ @property
126
+ def traversal_source(self):
127
+ return self._traversal_source
128
+
129
+ def _fill_pool(self):
130
+ for i in range(self._pool_size):
131
+ conn = self._get_connection()
132
+ self._pool.put_nowait(conn)
133
+
134
+ def is_closed(self):
135
+ return self._closed
136
+
137
+ def close(self):
138
+ # prevent the Client from being closed more than once. it raises errors if new jobby jobs
139
+ # get submitted to the executor when it is shutdown
140
+ if self._closed:
141
+ return
142
+
143
+ if self._session_enabled:
144
+ self._close_session()
145
+ log.info("Closing Client with url '%s'", self._url)
146
+ while not self._pool.empty():
147
+ conn = self._pool.get(True)
148
+ conn.close()
149
+ self._executor.shutdown()
150
+ self._closed = True
151
+
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
+ def _get_connection(self):
164
+ protocol = self._protocol_factory()
165
+ return connection.Connection(
166
+ self._url, self._traversal_source, protocol,
167
+ self._transport_factory, self._executor, self._pool,
168
+ headers=self._headers, enable_user_agent_on_connect=self._enable_user_agent_on_connect)
169
+
170
+ def submit(self, message, bindings=None, request_options=None):
171
+ return self.submit_async(message, bindings=bindings, request_options=request_options).result()
172
+
173
+ def submitAsync(self, message, bindings=None, request_options=None):
174
+ warnings.warn(
175
+ "gremlin_python.driver.client.Client.submitAsync will be replaced by "
176
+ "gremlin_python.driver.client.Client.submit_async.",
177
+ DeprecationWarning)
178
+ return self.submit_async(message, bindings, request_options)
179
+
180
+ def submit_async(self, message, bindings=None, request_options=None):
181
+ if self.is_closed():
182
+ raise Exception("Client is closed")
183
+
184
+ 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'
191
+
192
+ 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'
198
+
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)
202
+
203
+ conn = self._pool.get(True)
204
+ if request_options:
205
+ message.args.update(request_options)
206
+ return conn.write(message)
@@ -0,0 +1,106 @@
1
+ # Licensed to the Apache Software Foundation (ASF) under one
2
+ # or more contributor license agreements. See the NOTICE file
3
+ # distributed with this work for additional information
4
+ # regarding copyright ownership. The ASF licenses this file
5
+ # to you under the Apache License, Version 2.0 (the
6
+ # "License"); you may not use this file except in compliance
7
+ # with the License. You may obtain a copy of the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing,
12
+ # software distributed under the License is distributed on an
13
+ # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14
+ # KIND, either express or implied. See the License for the
15
+ # specific language governing permissions and limitations
16
+ # under the License.
17
+ import uuid
18
+ import queue
19
+ from concurrent.futures import Future
20
+
21
+ from gremlin_python.driver import resultset, useragent
22
+
23
+ __author__ = 'David M. Brown (davebshow@gmail.com)'
24
+
25
+
26
+ class Connection:
27
+
28
+ def __init__(self, url, traversal_source, protocol, transport_factory,
29
+ executor, pool, headers=None, enable_user_agent_on_connect=True):
30
+ self._url = url
31
+ self._headers = headers
32
+ self._traversal_source = traversal_source
33
+ self._protocol = protocol
34
+ self._transport_factory = transport_factory
35
+ self._executor = executor
36
+ self._transport = None
37
+ self._pool = pool
38
+ self._results = {}
39
+ self._inited = False
40
+ self._enable_user_agent_on_connect = enable_user_agent_on_connect
41
+ if self._enable_user_agent_on_connect:
42
+ self.__add_header(useragent.userAgentHeader, useragent.userAgent)
43
+
44
+ def connect(self):
45
+ if self._transport:
46
+ self._transport.close()
47
+ self._transport = self._transport_factory()
48
+ self._transport.connect(self._url, self._headers)
49
+ self._protocol.connection_made(self._transport)
50
+ self._inited = True
51
+
52
+ def close(self):
53
+ if self._inited:
54
+ self._transport.close()
55
+
56
+ def write(self, request_message):
57
+ if not self._inited:
58
+ 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
66
+ # Create write task
67
+ future = Future()
68
+ future_write = self._executor.submit(
69
+ self._protocol.write, request_id, request_message)
70
+
71
+ def cb(f):
72
+ try:
73
+ f.result()
74
+ except Exception as e:
75
+ future.set_exception(e)
76
+ self._pool.put_nowait(self)
77
+ else:
78
+ # Start receive task
79
+ done = self._executor.submit(self._receive)
80
+ result_set.done = done
81
+ future.set_result(result_set)
82
+
83
+ future_write.add_done_callback(cb)
84
+ return future
85
+
86
+ def _receive(self):
87
+ 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
93
+ finally:
94
+ self._pool.put_nowait(self)
95
+
96
+ def __add_header(self, key, value):
97
+ if self._headers is None:
98
+ self._headers = dict()
99
+ # Headers may be a list of pairs
100
+ if isinstance(self._headers, list):
101
+ for pair in self._headers:
102
+ if pair[0] == key:
103
+ self._headers.remove(pair)
104
+ self._headers.append((key, value))
105
+ else:
106
+ self._headers[key] = value