e6data-python-connector 2.1.21__tar.gz → 2.1.21.dev0__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 (36) hide show
  1. {e6data_python_connector-2.1.21/e6data_python_connector.egg-info → e6data_python_connector-2.1.21.dev0}/PKG-INFO +1 -1
  2. e6data_python_connector-2.1.21.dev0/e6data_python_connector/cluster_manager.py +117 -0
  3. e6data_python_connector-2.1.21.dev0/e6data_python_connector/cluster_server/__init__.py +0 -0
  4. e6data_python_connector-2.1.21.dev0/e6data_python_connector/cluster_server/cluster_pb2.py +38 -0
  5. e6data_python_connector-2.1.21.dev0/e6data_python_connector/cluster_server/cluster_pb2_grpc.py +132 -0
  6. {e6data_python_connector-2.1.21 → e6data_python_connector-2.1.21.dev0}/e6data_python_connector/e6data_grpc.py +35 -1
  7. {e6data_python_connector-2.1.21 → e6data_python_connector-2.1.21.dev0/e6data_python_connector.egg-info}/PKG-INFO +1 -1
  8. {e6data_python_connector-2.1.21 → e6data_python_connector-2.1.21.dev0}/e6data_python_connector.egg-info/SOURCES.txt +4 -0
  9. {e6data_python_connector-2.1.21 → e6data_python_connector-2.1.21.dev0}/setup.py +1 -1
  10. {e6data_python_connector-2.1.21 → e6data_python_connector-2.1.21.dev0}/LICENSE +0 -0
  11. {e6data_python_connector-2.1.21 → e6data_python_connector-2.1.21.dev0}/MANIFEST.in +0 -0
  12. {e6data_python_connector-2.1.21 → e6data_python_connector-2.1.21.dev0}/README.md +0 -0
  13. {e6data_python_connector-2.1.21 → e6data_python_connector-2.1.21.dev0}/e6data_python_connector/__init__.py +0 -0
  14. {e6data_python_connector-2.1.21 → e6data_python_connector-2.1.21.dev0}/e6data_python_connector/common.py +0 -0
  15. {e6data_python_connector-2.1.21 → e6data_python_connector-2.1.21.dev0}/e6data_python_connector/constants.py +0 -0
  16. {e6data_python_connector-2.1.21 → e6data_python_connector-2.1.21.dev0}/e6data_python_connector/datainputstream.py +0 -0
  17. {e6data_python_connector-2.1.21 → e6data_python_connector-2.1.21.dev0}/e6data_python_connector/date_time_utils.py +0 -0
  18. {e6data_python_connector-2.1.21 → e6data_python_connector-2.1.21.dev0}/e6data_python_connector/dialect.py +0 -0
  19. {e6data_python_connector-2.1.21 → e6data_python_connector-2.1.21.dev0}/e6data_python_connector/e6x_vector/__init__.py +0 -0
  20. {e6data_python_connector-2.1.21 → e6data_python_connector-2.1.21.dev0}/e6data_python_connector/e6x_vector/constants.py +0 -0
  21. {e6data_python_connector-2.1.21 → e6data_python_connector-2.1.21.dev0}/e6data_python_connector/e6x_vector/ttypes.py +0 -0
  22. {e6data_python_connector-2.1.21 → e6data_python_connector-2.1.21.dev0}/e6data_python_connector/exceptions.py +0 -0
  23. {e6data_python_connector-2.1.21 → e6data_python_connector-2.1.21.dev0}/e6data_python_connector/server/QueryEngineService.py +0 -0
  24. {e6data_python_connector-2.1.21 → e6data_python_connector-2.1.21.dev0}/e6data_python_connector/server/__init__.py +0 -0
  25. {e6data_python_connector-2.1.21 → e6data_python_connector-2.1.21.dev0}/e6data_python_connector/server/constants.py +0 -0
  26. {e6data_python_connector-2.1.21 → e6data_python_connector-2.1.21.dev0}/e6data_python_connector/server/e6x_engine_pb2.py +0 -0
  27. {e6data_python_connector-2.1.21 → e6data_python_connector-2.1.21.dev0}/e6data_python_connector/server/e6x_engine_pb2.pyi +0 -0
  28. {e6data_python_connector-2.1.21 → e6data_python_connector-2.1.21.dev0}/e6data_python_connector/server/e6x_engine_pb2_grpc.py +0 -0
  29. {e6data_python_connector-2.1.21 → e6data_python_connector-2.1.21.dev0}/e6data_python_connector/server/ttypes.py +0 -0
  30. {e6data_python_connector-2.1.21 → e6data_python_connector-2.1.21.dev0}/e6data_python_connector/typeId.py +0 -0
  31. {e6data_python_connector-2.1.21 → e6data_python_connector-2.1.21.dev0}/e6data_python_connector.egg-info/dependency_links.txt +0 -0
  32. {e6data_python_connector-2.1.21 → e6data_python_connector-2.1.21.dev0}/e6data_python_connector.egg-info/entry_points.txt +0 -0
  33. {e6data_python_connector-2.1.21 → e6data_python_connector-2.1.21.dev0}/e6data_python_connector.egg-info/requires.txt +0 -0
  34. {e6data_python_connector-2.1.21 → e6data_python_connector-2.1.21.dev0}/e6data_python_connector.egg-info/top_level.txt +0 -0
  35. {e6data_python_connector-2.1.21 → e6data_python_connector-2.1.21.dev0}/pyproject.toml +0 -0
  36. {e6data_python_connector-2.1.21 → e6data_python_connector-2.1.21.dev0}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: e6data-python-connector
3
- Version: 2.1.21
3
+ Version: 2.1.21.dev0
4
4
  Summary: Client for the e6data distributed SQL Engine.
5
5
  Home-page: https://github.com/e6x-labs/e6data-python-connector
6
6
  Author: Uniphi, Inc.
@@ -0,0 +1,117 @@
1
+ import threading
2
+ import time
3
+ import e6data_python_connector.cluster_server.cluster_pb2 as cluster_pb2
4
+ import e6data_python_connector.cluster_server.cluster_pb2_grpc as cluster_pb2_grpc
5
+ import grpc
6
+ from grpc._channel import _InactiveRpcError
7
+ import multiprocessing
8
+
9
+
10
+ class _StatusLock:
11
+ _LOCK_TIMEOUT = 500
12
+
13
+ def __init__(self):
14
+ self._status_thread_lock = threading.Lock()
15
+ self._status_multiprocessing_lock = multiprocessing.Semaphore()
16
+ self._is_active = False
17
+
18
+ @property
19
+ def is_active(self) -> bool:
20
+ return self._is_active
21
+
22
+ def set_active(self):
23
+ self._is_active = True
24
+
25
+ def __enter__(self):
26
+ self._status_thread_lock.acquire(timeout=self._LOCK_TIMEOUT)
27
+ self._status_multiprocessing_lock.acquire(timeout=self._LOCK_TIMEOUT)
28
+ print('Locked threads and processes.')
29
+ return self
30
+
31
+ def __exit__(self, exc_type, exc_val, exc_tb):
32
+ self._status_thread_lock.release()
33
+ self._status_multiprocessing_lock.release()
34
+ print('Released threads and processes.')
35
+
36
+
37
+ status_lock = _StatusLock()
38
+
39
+
40
+ class ClusterManager:
41
+ def __init__(self, host: str, port: int, user: str, password: str, secure_channel: bool = False, timeout=60 * 3):
42
+ self._host = host
43
+ self._port = port
44
+ self._user = user
45
+ self._password = password
46
+ self._timeout = time.time() + timeout
47
+ self._secure_channel = secure_channel
48
+
49
+ @property
50
+ def _get_connection(self):
51
+ if self._secure_channel:
52
+ self._channel = grpc.secure_channel(
53
+ target='{}:{}'.format(self._host, self._port),
54
+ credentials=grpc.ssl_channel_credentials()
55
+ )
56
+ else:
57
+ self._channel = grpc.insecure_channel(
58
+ target='{}:{}'.format(self._host, self._port)
59
+ )
60
+ return cluster_pb2_grpc.ClusterServiceStub(self._channel)
61
+
62
+ def resume(self) -> bool:
63
+ """
64
+ Return True if cluster came to active state else False.
65
+ """
66
+ with status_lock as lock:
67
+ if lock.is_active:
68
+ print('Cluster is already active')
69
+ return True
70
+ status_payload = cluster_pb2.ClusterStatusRequest(
71
+ user=self._user,
72
+ password=self._password
73
+ )
74
+ current_status = self._get_connection.status(status_payload)
75
+ print('Current status', current_status)
76
+ if current_status.status == 'suspended':
77
+ payload = cluster_pb2.ResumeRequest(
78
+ user=self._user,
79
+ password=self._password
80
+ )
81
+ response = self._get_connection.resume(payload)
82
+ print('resuming response', response.status)
83
+ elif current_status.status == 'active':
84
+ print('Cluster is already active.')
85
+ return True
86
+ elif current_status.status != 'resuming':
87
+ """
88
+ If cluster is in resuming state already, start watching for the status.
89
+ Cluster is in different state, cannot resume.
90
+ """
91
+ print('Cluster is in different state, cannot resume.')
92
+ return False
93
+ while True:
94
+ try:
95
+ status_payload = cluster_pb2.ClusterStatusRequest(
96
+ user=self._user,
97
+ password=self._password
98
+ )
99
+ response = self._get_connection.status(status_payload)
100
+ print(response)
101
+ if response.status == 'active':
102
+ lock.set_active()
103
+ print('Breaking because of status:', response.status)
104
+ return True
105
+ if response.status in ['suspended', 'failed']:
106
+ print('Breaking because of status:', response.status)
107
+ return False
108
+ if time.time() > self._timeout:
109
+ print('Breaking because of timeout')
110
+ return False
111
+ except _InactiveRpcError as e:
112
+ print('On resume error', e)
113
+ print('Retrying')
114
+ time.sleep(5)
115
+
116
+ def suspend(self):
117
+ pass
@@ -0,0 +1,38 @@
1
+ # -*- coding: utf-8 -*-
2
+ # Generated by the protocol buffer compiler. DO NOT EDIT!
3
+ # source: cluster.proto
4
+ """Generated protocol buffer code."""
5
+ from google.protobuf import descriptor as _descriptor
6
+ from google.protobuf import descriptor_pool as _descriptor_pool
7
+ from google.protobuf import symbol_database as _symbol_database
8
+ from google.protobuf.internal import builder as _builder
9
+ # @@protoc_insertion_point(imports)
10
+
11
+ _sym_db = _symbol_database.Default()
12
+
13
+
14
+
15
+
16
+ DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\rcluster.proto\"/\n\rResumeRequest\x12\x0c\n\x04user\x18\x01 \x01(\t\x12\x10\n\x08password\x18\x02 \x01(\t\" \n\x0eResumeResponse\x12\x0e\n\x06status\x18\x01 \x01(\t\"0\n\x0eSuspendRequest\x12\x0c\n\x04user\x18\x01 \x01(\t\x12\x10\n\x08password\x18\x02 \x01(\t\"!\n\x0fSuspendResponse\x12\x0e\n\x06status\x18\x01 \x01(\t\"6\n\x14\x43lusterStatusRequest\x12\x0c\n\x04user\x18\x01 \x01(\t\x12\x10\n\x08password\x18\x02 \x01(\t\"\'\n\x15\x43lusterStatusResponse\x12\x0e\n\x06status\x18\x01 \x01(\t2\xa2\x01\n\x0e\x43lusterService\x12)\n\x06resume\x12\x0e.ResumeRequest\x1a\x0f.ResumeResponse\x12,\n\x07suspend\x12\x0f.SuspendRequest\x1a\x10.SuspendResponse\x12\x37\n\x06status\x12\x15.ClusterStatusRequest\x1a\x16.ClusterStatusResponseB\x02P\x01\x62\x06proto3')
17
+
18
+ _globals = globals()
19
+ _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)
20
+ _builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'cluster_pb2', _globals)
21
+ if _descriptor._USE_C_DESCRIPTORS == False:
22
+ DESCRIPTOR._options = None
23
+ DESCRIPTOR._serialized_options = b'P\001'
24
+ _globals['_RESUMEREQUEST']._serialized_start=17
25
+ _globals['_RESUMEREQUEST']._serialized_end=64
26
+ _globals['_RESUMERESPONSE']._serialized_start=66
27
+ _globals['_RESUMERESPONSE']._serialized_end=98
28
+ _globals['_SUSPENDREQUEST']._serialized_start=100
29
+ _globals['_SUSPENDREQUEST']._serialized_end=148
30
+ _globals['_SUSPENDRESPONSE']._serialized_start=150
31
+ _globals['_SUSPENDRESPONSE']._serialized_end=183
32
+ _globals['_CLUSTERSTATUSREQUEST']._serialized_start=185
33
+ _globals['_CLUSTERSTATUSREQUEST']._serialized_end=239
34
+ _globals['_CLUSTERSTATUSRESPONSE']._serialized_start=241
35
+ _globals['_CLUSTERSTATUSRESPONSE']._serialized_end=280
36
+ _globals['_CLUSTERSERVICE']._serialized_start=283
37
+ _globals['_CLUSTERSERVICE']._serialized_end=445
38
+ # @@protoc_insertion_point(module_scope)
@@ -0,0 +1,132 @@
1
+ # Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT!
2
+ """Client and server classes corresponding to protobuf-defined services."""
3
+ import grpc
4
+
5
+ import e6data_python_connector.cluster_server.cluster_pb2 as cluster__pb2
6
+
7
+
8
+ class ClusterServiceStub(object):
9
+ """Missing associated documentation comment in .proto file."""
10
+
11
+ def __init__(self, channel):
12
+ """Constructor.
13
+
14
+ Args:
15
+ channel: A grpc.Channel.
16
+ """
17
+ self.resume = channel.unary_unary(
18
+ '/ClusterService/resume',
19
+ request_serializer=cluster__pb2.ResumeRequest.SerializeToString,
20
+ response_deserializer=cluster__pb2.ResumeResponse.FromString,
21
+ )
22
+ self.suspend = channel.unary_unary(
23
+ '/ClusterService/suspend',
24
+ request_serializer=cluster__pb2.SuspendRequest.SerializeToString,
25
+ response_deserializer=cluster__pb2.SuspendResponse.FromString,
26
+ )
27
+ self.status = channel.unary_unary(
28
+ '/ClusterService/status',
29
+ request_serializer=cluster__pb2.ClusterStatusRequest.SerializeToString,
30
+ response_deserializer=cluster__pb2.ClusterStatusResponse.FromString,
31
+ )
32
+
33
+
34
+ class ClusterServiceServicer(object):
35
+ """Missing associated documentation comment in .proto file."""
36
+
37
+ def resume(self, request, context):
38
+ """Missing associated documentation comment in .proto file."""
39
+ context.set_code(grpc.StatusCode.UNIMPLEMENTED)
40
+ context.set_details('Method not implemented!')
41
+ raise NotImplementedError('Method not implemented!')
42
+
43
+ def suspend(self, request, context):
44
+ """Missing associated documentation comment in .proto file."""
45
+ context.set_code(grpc.StatusCode.UNIMPLEMENTED)
46
+ context.set_details('Method not implemented!')
47
+ raise NotImplementedError('Method not implemented!')
48
+
49
+ def status(self, request, context):
50
+ """Missing associated documentation comment in .proto file."""
51
+ context.set_code(grpc.StatusCode.UNIMPLEMENTED)
52
+ context.set_details('Method not implemented!')
53
+ raise NotImplementedError('Method not implemented!')
54
+
55
+
56
+ def add_ClusterServiceServicer_to_server(servicer, server):
57
+ rpc_method_handlers = {
58
+ 'resume': grpc.unary_unary_rpc_method_handler(
59
+ servicer.resume,
60
+ request_deserializer=cluster__pb2.ResumeRequest.FromString,
61
+ response_serializer=cluster__pb2.ResumeResponse.SerializeToString,
62
+ ),
63
+ 'suspend': grpc.unary_unary_rpc_method_handler(
64
+ servicer.suspend,
65
+ request_deserializer=cluster__pb2.SuspendRequest.FromString,
66
+ response_serializer=cluster__pb2.SuspendResponse.SerializeToString,
67
+ ),
68
+ 'status': grpc.unary_unary_rpc_method_handler(
69
+ servicer.status,
70
+ request_deserializer=cluster__pb2.ClusterStatusRequest.FromString,
71
+ response_serializer=cluster__pb2.ClusterStatusResponse.SerializeToString,
72
+ ),
73
+ }
74
+ generic_handler = grpc.method_handlers_generic_handler(
75
+ 'ClusterService', rpc_method_handlers)
76
+ server.add_generic_rpc_handlers((generic_handler,))
77
+
78
+
79
+ # This class is part of an EXPERIMENTAL API.
80
+ class ClusterService(object):
81
+ """Missing associated documentation comment in .proto file."""
82
+
83
+ @staticmethod
84
+ def resume(request,
85
+ target,
86
+ options=(),
87
+ channel_credentials=None,
88
+ call_credentials=None,
89
+ insecure=False,
90
+ compression=None,
91
+ wait_for_ready=None,
92
+ timeout=None,
93
+ metadata=None):
94
+ return grpc.experimental.unary_unary(request, target, '/ClusterService/resume',
95
+ cluster__pb2.ResumeRequest.SerializeToString,
96
+ cluster__pb2.ResumeResponse.FromString,
97
+ options, channel_credentials,
98
+ insecure, call_credentials, compression, wait_for_ready, timeout, metadata)
99
+
100
+ @staticmethod
101
+ def suspend(request,
102
+ target,
103
+ options=(),
104
+ channel_credentials=None,
105
+ call_credentials=None,
106
+ insecure=False,
107
+ compression=None,
108
+ wait_for_ready=None,
109
+ timeout=None,
110
+ metadata=None):
111
+ return grpc.experimental.unary_unary(request, target, '/ClusterService/suspend',
112
+ cluster__pb2.SuspendRequest.SerializeToString,
113
+ cluster__pb2.SuspendResponse.FromString,
114
+ options, channel_credentials,
115
+ insecure, call_credentials, compression, wait_for_ready, timeout, metadata)
116
+
117
+ @staticmethod
118
+ def status(request,
119
+ target,
120
+ options=(),
121
+ channel_credentials=None,
122
+ call_credentials=None,
123
+ insecure=False,
124
+ compression=None,
125
+ wait_for_ready=None,
126
+ timeout=None,
127
+ metadata=None):
128
+ return grpc.experimental.unary_unary(request, target, '/ClusterService/status',
129
+ cluster__pb2.ClusterStatusRequest.SerializeToString,
130
+ cluster__pb2.ClusterStatusResponse.FromString,
131
+ options, channel_credentials,
132
+ insecure, call_credentials, compression, wait_for_ready, timeout, metadata)
@@ -16,7 +16,9 @@ from io import BytesIO
16
16
  from ssl import CERT_NONE, CERT_OPTIONAL, CERT_REQUIRED
17
17
 
18
18
  import grpc
19
+ from grpc._channel import _InactiveRpcError
19
20
 
21
+ from e6data_python_connector.cluster_manager import ClusterManager
20
22
  from e6data_python_connector.common import DBAPITypeObject, ParamEscaper, DBAPICursor
21
23
  from e6data_python_connector.constants import *
22
24
  from e6data_python_connector.datainputstream import get_query_columns_info, read_rows_from_chunk
@@ -114,6 +116,7 @@ class Connection(object):
114
116
  database: str = None,
115
117
  cluster_uuid: str = None,
116
118
  secure: bool = False,
119
+ auto_resume: bool = False,
117
120
  grpc_options: dict = None,
118
121
  ):
119
122
  """
@@ -137,6 +140,8 @@ class Connection(object):
137
140
  Cluster's uuid
138
141
  secure: bool, Optional
139
142
  Flag to use a secure channel for data transfer
143
+ auto_resume: bool, Optional
144
+ Flag to enable auto resume of the cluster before the query execution
140
145
  grpc_options: dict, Optional
141
146
  Specify gRPC configuration
142
147
  - keepalive_timeout_ms: This parameter defines the time, in milliseconds, that a gRPC connection should remain idle before sending a keepalive ping to check if the connection is still alive.
@@ -160,6 +165,8 @@ class Connection(object):
160
165
 
161
166
  self.catalog_name = catalog
162
167
 
168
+ self._auto_resume = auto_resume
169
+
163
170
  self._keepalive_timeout_ms = 900000
164
171
  self._max_receive_message_length = -1
165
172
  self._max_send_message_length = 300 * 1024 * 1024 # mb
@@ -208,11 +215,38 @@ class Connection(object):
208
215
  )
209
216
  authenticate_response = self._client.authenticate(
210
217
  authenticate_request,
211
- metadata=_get_grpc_header(cluster=self.cluster_uuid)
218
+ metadata=_get_grpc_header(cluster=self.cluster_uuid),
219
+ # timeout=3
212
220
  )
213
221
  self._session_id = authenticate_response.sessionId
214
222
  if not self._session_id:
215
223
  raise ValueError("Invalid credentials.")
224
+ except _InactiveRpcError as e:
225
+ if self._auto_resume:
226
+ if e.code() == grpc.StatusCode.UNKNOWN and 'Stream removed' in e.details():
227
+ status = ClusterManager(
228
+ host=self._host,
229
+ port=self._port,
230
+ user=self.__username,
231
+ password=self.__password,
232
+ secure_channel=self._secure_channel
233
+ ).resume()
234
+ if status:
235
+ authenticate_request = e6x_engine_pb2.AuthenticateRequest(
236
+ user=self.__username,
237
+ password=self.__password
238
+ )
239
+ authenticate_response = self._client.authenticate(
240
+ authenticate_request,
241
+ metadata=_get_grpc_header(cluster=self.cluster_uuid)
242
+ )
243
+ self._session_id = authenticate_response.sessionId
244
+ else:
245
+ raise e
246
+ else:
247
+ raise e
248
+ else:
249
+ raise e
216
250
  except Exception as e:
217
251
  self._channel.close()
218
252
  raise e
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: e6data-python-connector
3
- Version: 2.1.21
3
+ Version: 2.1.21.dev0
4
4
  Summary: Client for the e6data distributed SQL Engine.
5
5
  Home-page: https://github.com/e6x-labs/e6data-python-connector
6
6
  Author: Uniphi, Inc.
@@ -5,6 +5,7 @@ pyproject.toml
5
5
  setup.cfg
6
6
  setup.py
7
7
  e6data_python_connector/__init__.py
8
+ e6data_python_connector/cluster_manager.py
8
9
  e6data_python_connector/common.py
9
10
  e6data_python_connector/constants.py
10
11
  e6data_python_connector/datainputstream.py
@@ -19,6 +20,9 @@ e6data_python_connector.egg-info/dependency_links.txt
19
20
  e6data_python_connector.egg-info/entry_points.txt
20
21
  e6data_python_connector.egg-info/requires.txt
21
22
  e6data_python_connector.egg-info/top_level.txt
23
+ e6data_python_connector/cluster_server/__init__.py
24
+ e6data_python_connector/cluster_server/cluster_pb2.py
25
+ e6data_python_connector/cluster_server/cluster_pb2_grpc.py
22
26
  e6data_python_connector/e6x_vector/__init__.py
23
27
  e6data_python_connector/e6x_vector/constants.py
24
28
  e6data_python_connector/e6x_vector/ttypes.py
@@ -12,7 +12,7 @@
12
12
 
13
13
  import setuptools
14
14
 
15
- VERSION = [2, 1, 21]
15
+ VERSION = [2, 1, 21, 'dev']
16
16
 
17
17
 
18
18
  def get_long_desc():