e6data-python-connector 2.2.6__tar.gz → 2.3.6__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (77) hide show
  1. {e6data_python_connector-2.2.6/e6data_python_connector.egg-info → e6data_python_connector-2.3.6}/PKG-INFO +2 -2
  2. {e6data_python_connector-2.2.6 → e6data_python_connector-2.3.6}/README.md +1 -1
  3. {e6data_python_connector-2.2.6 → e6data_python_connector-2.3.6}/e6data_python_connector/cluster_manager.py +22 -41
  4. {e6data_python_connector-2.2.6 → e6data_python_connector-2.3.6}/e6data_python_connector/e6data_grpc.py +45 -47
  5. {e6data_python_connector-2.2.6 → e6data_python_connector-2.3.6/e6data_python_connector.egg-info}/PKG-INFO +2 -2
  6. {e6data_python_connector-2.2.6 → e6data_python_connector-2.3.6}/setup.py +1 -1
  7. {e6data_python_connector-2.2.6 → e6data_python_connector-2.3.6}/LICENSE +0 -0
  8. {e6data_python_connector-2.2.6 → e6data_python_connector-2.3.6}/MANIFEST.in +0 -0
  9. {e6data_python_connector-2.2.6 → e6data_python_connector-2.3.6}/e6data_python_connector/__init__.py +0 -0
  10. {e6data_python_connector-2.2.6 → e6data_python_connector-2.3.6}/e6data_python_connector/cluster_server/__init__.py +0 -0
  11. {e6data_python_connector-2.2.6 → e6data_python_connector-2.3.6}/e6data_python_connector/cluster_server/cluster_pb2.py +0 -0
  12. {e6data_python_connector-2.2.6 → e6data_python_connector-2.3.6}/e6data_python_connector/cluster_server/cluster_pb2_grpc.py +0 -0
  13. {e6data_python_connector-2.2.6 → e6data_python_connector-2.3.6}/e6data_python_connector/common.py +0 -0
  14. {e6data_python_connector-2.2.6 → e6data_python_connector-2.3.6}/e6data_python_connector/constants.py +0 -0
  15. {e6data_python_connector-2.2.6 → e6data_python_connector-2.3.6}/e6data_python_connector/datainputstream.py +0 -0
  16. {e6data_python_connector-2.2.6 → e6data_python_connector-2.3.6}/e6data_python_connector/date_time_utils.py +0 -0
  17. {e6data_python_connector-2.2.6 → e6data_python_connector-2.3.6}/e6data_python_connector/dialect.py +0 -0
  18. {e6data_python_connector-2.2.6 → e6data_python_connector-2.3.6}/e6data_python_connector/e6x_vector/__init__.py +0 -0
  19. {e6data_python_connector-2.2.6 → e6data_python_connector-2.3.6}/e6data_python_connector/e6x_vector/constants.py +0 -0
  20. {e6data_python_connector-2.2.6 → e6data_python_connector-2.3.6}/e6data_python_connector/e6x_vector/ttypes.py +0 -0
  21. {e6data_python_connector-2.2.6 → e6data_python_connector-2.3.6}/e6data_python_connector/exceptions.py +0 -0
  22. {e6data_python_connector-2.2.6 → e6data_python_connector-2.3.6}/e6data_python_connector/server/QueryEngineService.py +0 -0
  23. {e6data_python_connector-2.2.6 → e6data_python_connector-2.3.6}/e6data_python_connector/server/__init__.py +0 -0
  24. {e6data_python_connector-2.2.6 → e6data_python_connector-2.3.6}/e6data_python_connector/server/constants.py +0 -0
  25. {e6data_python_connector-2.2.6 → e6data_python_connector-2.3.6}/e6data_python_connector/server/e6x_engine_pb2.py +0 -0
  26. {e6data_python_connector-2.2.6 → e6data_python_connector-2.3.6}/e6data_python_connector/server/e6x_engine_pb2.pyi +0 -0
  27. {e6data_python_connector-2.2.6 → e6data_python_connector-2.3.6}/e6data_python_connector/server/e6x_engine_pb2_grpc.py +0 -0
  28. {e6data_python_connector-2.2.6 → e6data_python_connector-2.3.6}/e6data_python_connector/server/ttypes.py +0 -0
  29. {e6data_python_connector-2.2.6 → e6data_python_connector-2.3.6}/e6data_python_connector/strategy.py +0 -0
  30. {e6data_python_connector-2.2.6 → e6data_python_connector-2.3.6}/e6data_python_connector/typeId.py +0 -0
  31. {e6data_python_connector-2.2.6 → e6data_python_connector-2.3.6}/e6data_python_connector.egg-info/SOURCES.txt +0 -0
  32. {e6data_python_connector-2.2.6 → e6data_python_connector-2.3.6}/e6data_python_connector.egg-info/dependency_links.txt +0 -0
  33. {e6data_python_connector-2.2.6 → e6data_python_connector-2.3.6}/e6data_python_connector.egg-info/entry_points.txt +0 -0
  34. {e6data_python_connector-2.2.6 → e6data_python_connector-2.3.6}/e6data_python_connector.egg-info/requires.txt +0 -0
  35. {e6data_python_connector-2.2.6 → e6data_python_connector-2.3.6}/e6data_python_connector.egg-info/top_level.txt +0 -0
  36. {e6data_python_connector-2.2.6 → e6data_python_connector-2.3.6}/pyproject.toml +0 -0
  37. {e6data_python_connector-2.2.6 → e6data_python_connector-2.3.6}/setup.cfg +0 -0
  38. {e6data_python_connector-2.2.6 → e6data_python_connector-2.3.6}/test/__init__.py +0 -0
  39. {e6data_python_connector-2.2.6 → e6data_python_connector-2.3.6}/test/analyze_38_nines.py +0 -0
  40. {e6data_python_connector-2.2.6 → e6data_python_connector-2.3.6}/test/analyze_all_cases.py +0 -0
  41. {e6data_python_connector-2.2.6 → e6data_python_connector-2.3.6}/test/analyze_binary.py +0 -0
  42. {e6data_python_connector-2.2.6 → e6data_python_connector-2.3.6}/test/analyze_correct_value.py +0 -0
  43. {e6data_python_connector-2.2.6 → e6data_python_connector-2.3.6}/test/analyze_fields.py +0 -0
  44. {e6data_python_connector-2.2.6 → e6data_python_connector-2.3.6}/test/check_decimal_errors.py +0 -0
  45. {e6data_python_connector-2.2.6 → e6data_python_connector-2.3.6}/test/cleanup_test_files.py +0 -0
  46. {e6data_python_connector-2.2.6 → e6data_python_connector-2.3.6}/test/debug_38_nines.py +0 -0
  47. {e6data_python_connector-2.2.6 → e6data_python_connector-2.3.6}/test/debug_binary.py +0 -0
  48. {e6data_python_connector-2.2.6 → e6data_python_connector-2.3.6}/test/final_test.py +0 -0
  49. {e6data_python_connector-2.2.6 → e6data_python_connector-2.3.6}/test/move_tests.py +0 -0
  50. {e6data_python_connector-2.2.6 → e6data_python_connector-2.3.6}/test/quick_test.py +0 -0
  51. {e6data_python_connector-2.2.6 → e6data_python_connector-2.3.6}/test/test_38_nines.py +0 -0
  52. {e6data_python_connector-2.2.6 → e6data_python_connector-2.3.6}/test/test_all_decimal128_cases.py +0 -0
  53. {e6data_python_connector-2.2.6 → e6data_python_connector-2.3.6}/test/test_cluster_manager_efficiency.py +0 -0
  54. {e6data_python_connector-2.2.6 → e6data_python_connector-2.3.6}/test/test_cluster_manager_none_strategy.py +0 -0
  55. {e6data_python_connector-2.2.6 → e6data_python_connector-2.3.6}/test/test_cluster_manager_strategy.py +0 -0
  56. {e6data_python_connector-2.2.6 → e6data_python_connector-2.3.6}/test/test_comprehensive.py +0 -0
  57. {e6data_python_connector-2.2.6 → e6data_python_connector-2.3.6}/test/test_current_implementation.py +0 -0
  58. {e6data_python_connector-2.2.6 → e6data_python_connector-2.3.6}/test/test_decimal128_binary_parsing.py +0 -0
  59. {e6data_python_connector-2.2.6 → e6data_python_connector-2.3.6}/test/test_decimal128_parsing.py +0 -0
  60. {e6data_python_connector-2.2.6 → e6data_python_connector-2.3.6}/test/test_fix.py +0 -0
  61. {e6data_python_connector-2.2.6 → e6data_python_connector-2.3.6}/test/test_improved_parsing.py +0 -0
  62. {e6data_python_connector-2.2.6 → e6data_python_connector-2.3.6}/test/test_known_case.py +0 -0
  63. {e6data_python_connector-2.2.6 → e6data_python_connector-2.3.6}/test/test_manual_analysis.py +0 -0
  64. {e6data_python_connector-2.2.6 → e6data_python_connector-2.3.6}/test/test_mock_server.py +0 -0
  65. {e6data_python_connector-2.2.6 → e6data_python_connector-2.3.6}/test/test_multiprocessing_fix.py +0 -0
  66. {e6data_python_connector-2.2.6 → e6data_python_connector-2.3.6}/test/test_new_implementation.py +0 -0
  67. {e6data_python_connector-2.2.6 → e6data_python_connector-2.3.6}/test/test_specific_binary.py +0 -0
  68. {e6data_python_connector-2.2.6 → e6data_python_connector-2.3.6}/test/test_strategy.py +0 -0
  69. {e6data_python_connector-2.2.6 → e6data_python_connector-2.3.6}/test/test_strategy_logic.py +0 -0
  70. {e6data_python_connector-2.2.6 → e6data_python_connector-2.3.6}/test/test_strategy_persistence_fix.py +0 -0
  71. {e6data_python_connector-2.2.6 → e6data_python_connector-2.3.6}/test/test_strategy_sharing_fix.py +0 -0
  72. {e6data_python_connector-2.2.6 → e6data_python_connector-2.3.6}/test/test_user_binary_value.py +0 -0
  73. {e6data_python_connector-2.2.6 → e6data_python_connector-2.3.6}/test/tests.py +0 -0
  74. {e6data_python_connector-2.2.6 → e6data_python_connector-2.3.6}/test/tests_grpc.py +0 -0
  75. {e6data_python_connector-2.2.6 → e6data_python_connector-2.3.6}/test/validate_decimal128.py +0 -0
  76. {e6data_python_connector-2.2.6 → e6data_python_connector-2.3.6}/test/validate_implementation.py +0 -0
  77. {e6data_python_connector-2.2.6 → e6data_python_connector-2.3.6}/test/verify_decimal_fix.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: e6data-python-connector
3
- Version: 2.2.6
3
+ Version: 2.3.6
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: e6data, Inc.
@@ -38,7 +38,7 @@ Dynamic: summary
38
38
 
39
39
  # e6data Python Connector
40
40
 
41
- ![version](https://img.shields.io/badge/version-2.2.6-blue.svg)
41
+ ![version](https://img.shields.io/badge/version-2.3.6-blue.svg)
42
42
 
43
43
  ## Introduction
44
44
 
@@ -1,6 +1,6 @@
1
1
  # e6data Python Connector
2
2
 
3
- ![version](https://img.shields.io/badge/version-2.2.6-blue.svg)
3
+ ![version](https://img.shields.io/badge/version-2.3.6-blue.svg)
4
4
 
5
5
  ## Introduction
6
6
 
@@ -5,13 +5,9 @@ import e6data_python_connector.cluster_server.cluster_pb2_grpc as cluster_pb2_gr
5
5
  import grpc
6
6
  from grpc._channel import _InactiveRpcError
7
7
  import multiprocessing
8
- import logging
9
8
 
10
- # Import strategy management functions
11
- from e6data_python_connector.strategy import _get_active_strategy, _set_active_strategy, _set_pending_strategy, _get_grpc_header as _get_strategy_header
12
-
13
- # Set up logging
14
- _logger = logging.getLogger(__name__)
9
+ from e6data_python_connector.strategy import _get_active_strategy, _set_active_strategy, _set_pending_strategy, \
10
+ _get_grpc_header as _get_strategy_header
15
11
 
16
12
 
17
13
  def _get_grpc_header(engine_ip=None, cluster=None, strategy=None):
@@ -140,7 +136,8 @@ class ClusterManager:
140
136
  cluster_uuid (str): The unique identifier for the target cluster.
141
137
  """
142
138
 
143
- def __init__(self, host: str, port: int, user: str, password: str, secure_channel: bool = False, timeout=60 * 5, cluster_uuid=None, grpc_options=None):
139
+ def __init__(self, host: str, port: int, user: str, password: str, secure_channel: bool = False, timeout=60 * 5,
140
+ cluster_uuid=None, grpc_options=None):
144
141
  """
145
142
  Initializes a new instance of the ClusterManager class.
146
143
 
@@ -208,7 +205,7 @@ class ClusterManager:
208
205
  The response from the successful request
209
206
  """
210
207
  current_strategy = _get_active_strategy()
211
-
208
+
212
209
  # Create payload if not provided
213
210
  if payload is None:
214
211
  if request_type == "status":
@@ -221,12 +218,10 @@ class ClusterManager:
221
218
  user=self._user,
222
219
  password=self._password
223
220
  )
224
-
221
+
225
222
  # If we have an active strategy, use it first
226
223
  if current_strategy is not None:
227
224
  try:
228
- _logger.info(f"ClusterManager: Trying {request_type} with established strategy: {current_strategy}")
229
-
230
225
  if request_type == "status":
231
226
  response = self._get_connection.status(
232
227
  payload,
@@ -239,22 +234,20 @@ class ClusterManager:
239
234
  )
240
235
  else:
241
236
  raise ValueError(f"Unknown request type: {request_type}")
242
-
237
+
243
238
  # Check for new strategy in response
244
239
  if hasattr(response, 'new_strategy') and response.new_strategy:
245
240
  new_strategy = response.new_strategy.lower()
246
241
  if new_strategy != current_strategy:
247
- _logger.info(f"ClusterManager: Server indicated new strategy during {request_type}: {new_strategy}")
248
242
  _set_pending_strategy(new_strategy)
249
-
243
+
250
244
  return response
251
-
245
+
252
246
  except _InactiveRpcError as e:
253
247
  if e.code() == grpc.StatusCode.UNKNOWN and 'status: 456' in e.details():
254
248
  # 456 error - switch to alternative strategy
255
249
  alternative_strategy = 'green' if current_strategy == 'blue' else 'blue'
256
- _logger.info(f"ClusterManager: {request_type} failed with 456 error on {current_strategy}, switching to: {alternative_strategy}")
257
-
250
+
258
251
  try:
259
252
  if request_type == "status":
260
253
  response = self._get_connection.status(
@@ -266,36 +259,30 @@ class ClusterManager:
266
259
  payload,
267
260
  metadata=_get_grpc_header(cluster=self.cluster_uuid, strategy=alternative_strategy)
268
261
  )
269
-
262
+
270
263
  # Update active strategy since the alternative worked
271
264
  _set_active_strategy(alternative_strategy)
272
- _logger.info(f"ClusterManager: {request_type} succeeded with alternative strategy: {alternative_strategy}")
273
-
265
+
274
266
  # Check for new strategy in response
275
267
  if hasattr(response, 'new_strategy') and response.new_strategy:
276
268
  new_strategy = response.new_strategy.lower()
277
269
  if new_strategy != alternative_strategy:
278
- _logger.info(f"ClusterManager: Server indicated new strategy during {request_type}: {new_strategy}")
279
270
  _set_pending_strategy(new_strategy)
280
-
271
+
281
272
  return response
282
-
273
+
283
274
  except _InactiveRpcError as e2:
284
- _logger.error(f"ClusterManager: Both strategies failed for {request_type}. Original error: {e}, Alternative error: {e2}")
285
275
  raise e # Raise the original error
286
276
  else:
287
277
  # Non-456 error - don't retry
288
- _logger.error(f"ClusterManager: {request_type} failed with non-456 error: {e}")
289
278
  raise e
290
-
279
+
291
280
  # No active strategy - start with authentication logic (blue first, then green)
292
- _logger.info(f"ClusterManager: No active strategy, starting authentication sequence for {request_type}")
293
281
  strategies_to_try = ['blue', 'green']
294
-
282
+
295
283
  for i, strategy in enumerate(strategies_to_try):
296
284
  try:
297
- _logger.info(f"ClusterManager: Trying {request_type} with strategy: {strategy}")
298
-
285
+
299
286
  if request_type == "status":
300
287
  response = self._get_connection.status(
301
288
  payload,
@@ -308,36 +295,30 @@ class ClusterManager:
308
295
  )
309
296
  else:
310
297
  raise ValueError(f"Unknown request type: {request_type}")
311
-
298
+
312
299
  # Set the working strategy as active
313
300
  _set_active_strategy(strategy)
314
- _logger.info(f"ClusterManager: {request_type} succeeded with strategy: {strategy}")
315
-
301
+
316
302
  # Check for new strategy in response
317
303
  if hasattr(response, 'new_strategy') and response.new_strategy:
318
304
  new_strategy = response.new_strategy.lower()
319
305
  if new_strategy != strategy:
320
- _logger.info(f"ClusterManager: Server indicated new strategy during {request_type}: {new_strategy}")
321
306
  _set_pending_strategy(new_strategy)
322
-
307
+
323
308
  return response
324
-
309
+
325
310
  except _InactiveRpcError as e:
326
311
  if e.code() == grpc.StatusCode.UNKNOWN and 'status: 456' in e.details():
327
312
  # 456 error - try next strategy
328
313
  if i < len(strategies_to_try) - 1:
329
- _logger.info(f"ClusterManager: {request_type} failed with 456 error on {strategy}, trying next strategy: {strategies_to_try[i + 1]}")
330
314
  continue
331
315
  else:
332
- _logger.error(f"ClusterManager: {request_type} failed with 456 error on all strategies")
333
316
  raise e
334
317
  else:
335
318
  # Non-456 error - don't retry
336
- _logger.error(f"ClusterManager: {request_type} failed with non-456 error: {e}")
337
319
  raise e
338
-
320
+
339
321
  # If we get here, all strategies failed
340
- _logger.error(f"ClusterManager: All strategies failed for {request_type}")
341
322
  raise e
342
323
 
343
324
  def _check_cluster_status(self):
@@ -7,7 +7,6 @@ from __future__ import absolute_import
7
7
  from __future__ import unicode_literals
8
8
 
9
9
  import datetime
10
- import logging
11
10
  import re
12
11
  import sys
13
12
  import time
@@ -32,9 +31,6 @@ apilevel = '2.0'
32
31
  threadsafety = 2 # Threads may share the e6xdb and connections.
33
32
  paramstyle = 'pyformat' # Python extended format codes, e.g. ...WHERE name=%(name)s
34
33
 
35
- logging.basicConfig(level=logging.DEBUG)
36
- _logger = logging.getLogger(__name__)
37
-
38
34
  _TIMESTAMP_PATTERN = re.compile(r'(\d+-\d+-\d+ \d+:\d+:\d+(\.\d{,6})?)')
39
35
 
40
36
  ssl_cert_parameter_map = {
@@ -121,10 +117,10 @@ _strategy_lock = threading.Lock()
121
117
  _strategy_manager = None
122
118
  _shared_strategy = None
123
119
  _local_strategy_cache = {
124
- 'active_strategy': None,
120
+ 'active_strategy': None,
125
121
  'last_check_time': 0,
126
122
  'pending_strategy': None, # Strategy to use for next query
127
- 'query_strategy_map': {}, # Map of query_id to strategy used
123
+ 'query_strategy_map': {}, # Map of query_id to strategy used
128
124
  'last_transition_time': 0, # Timestamp of last strategy transition
129
125
  'session_invalidated': False # Flag to invalidate all sessions
130
126
  }
@@ -132,10 +128,11 @@ _local_strategy_cache = {
132
128
  # Strategy cache timeout in seconds (5 minutes)
133
129
  STRATEGY_CACHE_TIMEOUT = 300
134
130
 
131
+
135
132
  def _get_shared_strategy():
136
133
  """Get or create the shared strategy storage."""
137
134
  global _strategy_manager, _shared_strategy
138
-
135
+
139
136
  # Try to use multiprocessing.Manager for process-safe storage
140
137
  try:
141
138
  if _strategy_manager is None:
@@ -174,15 +171,15 @@ def _set_active_strategy(strategy):
174
171
  normalized_strategy = strategy.lower()
175
172
  if normalized_strategy not in ['blue', 'green']:
176
173
  return
177
-
174
+
178
175
  with _strategy_lock:
179
176
  shared_strategy = _get_shared_strategy()
180
177
  current_time = time.time()
181
-
178
+
182
179
  # Only update transition time if strategy actually changed
183
180
  if shared_strategy['active_strategy'] != normalized_strategy:
184
181
  shared_strategy['last_transition_time'] = current_time
185
-
182
+
186
183
  shared_strategy['active_strategy'] = normalized_strategy
187
184
  shared_strategy['last_check_time'] = current_time
188
185
 
@@ -204,11 +201,11 @@ def _set_pending_strategy(strategy):
204
201
  normalized_strategy = strategy.lower()
205
202
  if normalized_strategy not in ['blue', 'green']:
206
203
  return
207
-
204
+
208
205
  with _strategy_lock:
209
206
  shared_strategy = _get_shared_strategy()
210
207
  current_active = shared_strategy['active_strategy']
211
-
208
+
212
209
  if normalized_strategy != current_active:
213
210
  shared_strategy['pending_strategy'] = normalized_strategy
214
211
 
@@ -221,13 +218,13 @@ def _apply_pending_strategy():
221
218
  old_strategy = shared_strategy['active_strategy']
222
219
  new_strategy = shared_strategy['pending_strategy']
223
220
  current_time = time.time()
224
-
221
+
225
222
  shared_strategy['active_strategy'] = new_strategy
226
223
  shared_strategy['pending_strategy'] = None
227
224
  shared_strategy['last_check_time'] = current_time
228
225
  shared_strategy['last_transition_time'] = current_time
229
226
  shared_strategy['session_invalidated'] = True # Invalidate all sessions
230
-
227
+
231
228
  return new_strategy
232
229
  return None
233
230
 
@@ -247,7 +244,7 @@ def _register_query_strategy(query_id, strategy):
247
244
  normalized_strategy = strategy.lower()
248
245
  if normalized_strategy not in ['blue', 'green']:
249
246
  return
250
-
247
+
251
248
  with _strategy_lock:
252
249
  shared_strategy = _get_shared_strategy()
253
250
  query_map = shared_strategy.get('query_strategy_map', {})
@@ -407,7 +404,8 @@ class Connection(object):
407
404
  "keepalive_permit_without_calls": 1, # Allow keep-alives with no active RPCs.
408
405
  "http2.max_pings_without_data": 0, # Unlimited pings without data.
409
406
  "http2.min_time_between_pings_ms": 15000, # Minimum time between pings (15 seconds).
410
- "http2.min_ping_interval_without_data_ms": 15000, # Minimum interval between pings without data (15 seconds).
407
+ "http2.min_ping_interval_without_data_ms": 15000,
408
+ # Minimum interval between pings without data (15 seconds).
411
409
  }
412
410
  if grpc_options:
413
411
  for key, value in grpc_options.items():
@@ -481,7 +479,7 @@ class Connection(object):
481
479
  self._create_client()
482
480
  # Clear the invalidation flag
483
481
  shared_strategy['session_invalidated'] = False
484
-
482
+
485
483
  # Only create fresh connection if we have no active queries
486
484
  elif self._session_id and pending_strategy and pending_strategy != active_strategy:
487
485
  query_map = shared_strategy.get('query_strategy_map', {})
@@ -531,7 +529,7 @@ class Connection(object):
531
529
  elif pending_strategy:
532
530
  # If there's a pending strategy, force re-authentication with new strategy
533
531
  active_strategy = None
534
-
532
+
535
533
  if not active_strategy:
536
534
  # Check if we have a pending strategy to use
537
535
  if pending_strategy:
@@ -555,7 +553,8 @@ class Connection(object):
555
553
  _set_active_strategy(strategy)
556
554
 
557
555
  # Check for new strategy in authenticate response
558
- if hasattr(authenticate_response, 'new_strategy') and authenticate_response.new_strategy:
556
+ if hasattr(authenticate_response,
557
+ 'new_strategy') and authenticate_response.new_strategy:
559
558
  new_strategy = authenticate_response.new_strategy.lower()
560
559
  if new_strategy != strategy:
561
560
  _set_pending_strategy(new_strategy)
@@ -569,11 +568,11 @@ class Connection(object):
569
568
  else:
570
569
  # Different error, handle it normally
571
570
  raise e
572
-
571
+
573
572
  if not self._session_id and last_error:
574
573
  # Neither strategy worked
575
574
  raise last_error
576
-
575
+
577
576
  if not self._session_id:
578
577
  raise ValueError("Invalid credentials.")
579
578
  except _InactiveRpcError as e:
@@ -671,7 +670,7 @@ class Connection(object):
671
670
  pending_strategy = shared_strategy.get('pending_strategy')
672
671
  active_strategy = shared_strategy.get('active_strategy')
673
672
  query_map = shared_strategy.get('query_strategy_map', {})
674
-
673
+
675
674
  if pending_strategy and pending_strategy != active_strategy and len(query_map) == 0:
676
675
  _apply_pending_strategy()
677
676
  # Force new authentication with new strategy
@@ -690,11 +689,11 @@ class Connection(object):
690
689
  pending_strategy = shared_strategy.get('pending_strategy')
691
690
  active_strategy = shared_strategy.get('active_strategy')
692
691
  query_map = shared_strategy.get('query_strategy_map', {})
693
-
692
+
694
693
  # Create new connection if:
695
694
  # 1. No session exists
696
695
  # 2. There's a pending strategy change and no active queries
697
- return (not self._session_id or
696
+ return (not self._session_id or
698
697
  (pending_strategy and pending_strategy != active_strategy and len(query_map) == 0))
699
698
 
700
699
  def clear(self, query_id, engine_ip=None):
@@ -714,7 +713,7 @@ class Connection(object):
714
713
  clear_request,
715
714
  metadata=_get_grpc_header(engine_ip=engine_ip, cluster=self.cluster_uuid, strategy=_get_active_strategy())
716
715
  )
717
-
716
+
718
717
  # Check for new strategy in clear response
719
718
  if hasattr(clear_response, 'new_strategy') and clear_response.new_strategy:
720
719
  _set_pending_strategy(clear_response.new_strategy)
@@ -745,7 +744,7 @@ class Connection(object):
745
744
  cancel_query_request,
746
745
  metadata=_get_grpc_header(engine_ip=engine_ip, cluster=self.cluster_uuid, strategy=_get_active_strategy())
747
746
  )
748
-
747
+
749
748
  # Check for new strategy in cancel response
750
749
  if hasattr(cancel_response, 'new_strategy') and cancel_response.new_strategy:
751
750
  _set_pending_strategy(cancel_response.new_strategy)
@@ -791,7 +790,7 @@ class Connection(object):
791
790
  get_table_request,
792
791
  metadata=_get_grpc_header(cluster=self.cluster_uuid, strategy=_get_active_strategy())
793
792
  )
794
-
793
+
795
794
  # Check for new strategy in get tables response
796
795
  if hasattr(get_table_response, 'new_strategy') and get_table_response.new_strategy:
797
796
  _set_pending_strategy(get_table_response.new_strategy)
@@ -819,7 +818,7 @@ class Connection(object):
819
818
  get_columns_request,
820
819
  metadata=_get_grpc_header(cluster=self.cluster_uuid, strategy=_get_active_strategy())
821
820
  )
822
-
821
+
823
822
  # Check for new strategy in get columns response
824
823
  if hasattr(get_columns_response, 'new_strategy') and get_columns_response.new_strategy:
825
824
  _set_pending_strategy(get_columns_response.new_strategy)
@@ -843,11 +842,11 @@ class Connection(object):
843
842
  get_schema_request,
844
843
  metadata=_get_grpc_header(cluster=self.cluster_uuid, strategy=_get_active_strategy())
845
844
  )
846
-
845
+
847
846
  # Check for new strategy in get schema names response
848
847
  if hasattr(get_schema_response, 'new_strategy') and get_schema_response.new_strategy:
849
848
  _set_pending_strategy(get_schema_response.new_strategy)
850
-
849
+
851
850
  return list(get_schema_response.schemas)
852
851
 
853
852
  def commit(self):
@@ -1072,7 +1071,7 @@ class Cursor(DBAPICursor):
1072
1071
  """
1073
1072
  if not query_id:
1074
1073
  query_id = self._query_id
1075
-
1074
+
1076
1075
  clear_request = e6x_engine_pb2.ClearOrCancelQueryRequest(
1077
1076
  sessionId=self.connection.get_session_id,
1078
1077
  queryId=query_id,
@@ -1081,7 +1080,7 @@ class Cursor(DBAPICursor):
1081
1080
  # Get fresh client after session access (may have been invalidated)
1082
1081
  client = self.connection.client
1083
1082
  clear_response = client.clearOrCancelQuery(clear_request, metadata=self.metadata)
1084
-
1083
+
1085
1084
  # Check for new strategy in clear response
1086
1085
  if hasattr(clear_response, 'new_strategy') and clear_response.new_strategy:
1087
1086
  _set_pending_strategy(clear_response.new_strategy)
@@ -1094,7 +1093,7 @@ class Cursor(DBAPICursor):
1094
1093
  shared_strategy = _get_shared_strategy()
1095
1094
  pending_strategy = shared_strategy.get('pending_strategy')
1096
1095
  query_map = shared_strategy.get('query_strategy_map', {})
1097
-
1096
+
1098
1097
  if pending_strategy and len(query_map) == 0:
1099
1098
  _apply_pending_strategy()
1100
1099
 
@@ -1117,7 +1116,7 @@ class Cursor(DBAPICursor):
1117
1116
  shared_strategy = _get_shared_strategy()
1118
1117
  pending_strategy = shared_strategy.get('pending_strategy')
1119
1118
  query_map = shared_strategy.get('query_strategy_map', {})
1120
-
1119
+
1121
1120
  if pending_strategy and len(query_map) == 0:
1122
1121
  _apply_pending_strategy()
1123
1122
 
@@ -1137,11 +1136,11 @@ class Cursor(DBAPICursor):
1137
1136
  engineIP=self._engine_ip
1138
1137
  )
1139
1138
  status_response = self.connection.client.status(status_request, metadata=self.metadata)
1140
-
1139
+
1141
1140
  # Check for new strategy in status response
1142
1141
  if hasattr(status_response, 'new_strategy') and status_response.new_strategy:
1143
1142
  _set_pending_strategy(status_response.new_strategy)
1144
-
1143
+
1145
1144
  return status_response
1146
1145
 
1147
1146
  @re_auth
@@ -1183,18 +1182,17 @@ class Cursor(DBAPICursor):
1183
1182
  self._query_id = prepare_statement_response.queryId
1184
1183
  self._engine_ip = prepare_statement_response.engineIP
1185
1184
 
1186
-
1187
1185
  # Check for new strategy in prepare response
1188
1186
  if hasattr(prepare_statement_response, 'new_strategy') and prepare_statement_response.new_strategy:
1189
1187
  new_strategy = prepare_statement_response.new_strategy.lower()
1190
1188
  if new_strategy != _get_active_strategy():
1191
1189
  _set_pending_strategy(new_strategy)
1192
-
1190
+
1193
1191
  # Register this query with the current strategy
1194
1192
  current_strategy = _get_active_strategy()
1195
1193
  if current_strategy:
1196
1194
  _register_query_strategy(self._query_id, current_strategy)
1197
-
1195
+
1198
1196
  execute_statement_request = e6x_engine_pb2.ExecuteStatementRequest(
1199
1197
  engineIP=self._engine_ip,
1200
1198
  sessionId=self.connection.get_session_id,
@@ -1206,7 +1204,7 @@ class Cursor(DBAPICursor):
1206
1204
  execute_statement_request,
1207
1205
  metadata=self.metadata
1208
1206
  )
1209
-
1207
+
1210
1208
  # Check for new strategy in execute response
1211
1209
  if hasattr(execute_response, 'new_strategy') and execute_response.new_strategy:
1212
1210
  new_strategy = execute_response.new_strategy.lower()
@@ -1235,7 +1233,7 @@ class Cursor(DBAPICursor):
1235
1233
  new_strategy = prepare_statement_response.new_strategy.lower()
1236
1234
  if new_strategy != _get_active_strategy():
1237
1235
  _set_pending_strategy(new_strategy)
1238
-
1236
+
1239
1237
  # Register this query with the current strategy
1240
1238
  current_strategy = _get_active_strategy()
1241
1239
 
@@ -1288,13 +1286,13 @@ class Cursor(DBAPICursor):
1288
1286
  result_meta_data_request,
1289
1287
  metadata=self.metadata
1290
1288
  )
1291
-
1289
+
1292
1290
  # Check for new strategy in metadata response
1293
1291
  if hasattr(get_result_metadata_response, 'new_strategy') and get_result_metadata_response.new_strategy:
1294
1292
  new_strategy = get_result_metadata_response.new_strategy.lower()
1295
1293
  if new_strategy != _get_active_strategy():
1296
1294
  _set_pending_strategy(new_strategy)
1297
-
1295
+
1298
1296
  buffer = BytesIO(get_result_metadata_response.resultMetaData)
1299
1297
  self._rowcount, self._query_columns_description = get_query_columns_info(buffer)
1300
1298
  self._is_metadata_updated = True
@@ -1368,13 +1366,13 @@ class Cursor(DBAPICursor):
1368
1366
  get_next_result_batch_request,
1369
1367
  metadata=self.metadata
1370
1368
  )
1371
-
1369
+
1372
1370
  # Check for new strategy in batch response
1373
1371
  if hasattr(get_next_result_batch_response, 'new_strategy') and get_next_result_batch_response.new_strategy:
1374
1372
  new_strategy = get_next_result_batch_response.new_strategy.lower()
1375
1373
  if new_strategy != _get_active_strategy():
1376
1374
  _set_pending_strategy(new_strategy)
1377
-
1375
+
1378
1376
  buffer = get_next_result_batch_response.resultBatch
1379
1377
  if not self._is_metadata_updated:
1380
1378
  self.update_mete_data()
@@ -1467,11 +1465,11 @@ class Cursor(DBAPICursor):
1467
1465
  explain_analyze_request,
1468
1466
  metadata=self.metadata
1469
1467
  )
1470
-
1468
+
1471
1469
  # Check for new strategy in explain analyze response
1472
1470
  if hasattr(explain_analyze_response, 'new_strategy') and explain_analyze_response.new_strategy:
1473
1471
  _set_pending_strategy(explain_analyze_response.new_strategy)
1474
-
1472
+
1475
1473
  return dict(
1476
1474
  is_cached=explain_analyze_response.isCached,
1477
1475
  parsing_time=explain_analyze_response.parsingTime,
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: e6data-python-connector
3
- Version: 2.2.6
3
+ Version: 2.3.6
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: e6data, Inc.
@@ -38,7 +38,7 @@ Dynamic: summary
38
38
 
39
39
  # e6data Python Connector
40
40
 
41
- ![version](https://img.shields.io/badge/version-2.2.6-blue.svg)
41
+ ![version](https://img.shields.io/badge/version-2.3.6-blue.svg)
42
42
 
43
43
  ## Introduction
44
44
 
@@ -12,7 +12,7 @@
12
12
 
13
13
  import setuptools
14
14
 
15
- VERSION = (2, 2, 6,)
15
+ VERSION = (2, 3, 6,)
16
16
 
17
17
 
18
18
  def get_long_desc():