e6data-python-connector 3.0.2__tar.gz → 3.0.3rc1__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.
- {e6data_python_connector-3.0.2/e6data_python_connector.egg-info → e6data_python_connector-3.0.3rc1}/PKG-INFO +1 -1
- {e6data_python_connector-3.0.2 → e6data_python_connector-3.0.3rc1}/e6data_python_connector/async_cluster_manager.py +7 -0
- e6data_python_connector-3.0.3rc1/e6data_python_connector/calendar_interval.py +29 -0
- {e6data_python_connector-3.0.2 → e6data_python_connector-3.0.3rc1}/e6data_python_connector/cluster_manager.py +2 -0
- {e6data_python_connector-3.0.2 → e6data_python_connector-3.0.3rc1}/e6data_python_connector/datainputstream.py +15 -0
- {e6data_python_connector-3.0.2 → e6data_python_connector-3.0.3rc1}/e6data_python_connector/e6x_vector/ttypes.py +133 -1
- {e6data_python_connector-3.0.2 → e6data_python_connector-3.0.3rc1/e6data_python_connector.egg-info}/PKG-INFO +1 -1
- {e6data_python_connector-3.0.2 → e6data_python_connector-3.0.3rc1}/e6data_python_connector.egg-info/SOURCES.txt +2 -0
- {e6data_python_connector-3.0.2 → e6data_python_connector-3.0.3rc1}/setup.py +1 -1
- e6data_python_connector-3.0.3rc1/test/integration/test_cluster_resume_errors.py +179 -0
- {e6data_python_connector-3.0.2 → e6data_python_connector-3.0.3rc1}/LICENSE +0 -0
- {e6data_python_connector-3.0.2 → e6data_python_connector-3.0.3rc1}/MANIFEST.in +0 -0
- {e6data_python_connector-3.0.2 → e6data_python_connector-3.0.3rc1}/README.md +0 -0
- {e6data_python_connector-3.0.2 → e6data_python_connector-3.0.3rc1}/e6data_python_connector/__init__.py +0 -0
- {e6data_python_connector-3.0.2 → e6data_python_connector-3.0.3rc1}/e6data_python_connector/aio.py +0 -0
- {e6data_python_connector-3.0.2 → e6data_python_connector-3.0.3rc1}/e6data_python_connector/async_connection.py +0 -0
- {e6data_python_connector-3.0.2 → e6data_python_connector-3.0.3rc1}/e6data_python_connector/async_connection_pool.py +0 -0
- {e6data_python_connector-3.0.2 → e6data_python_connector-3.0.3rc1}/e6data_python_connector/async_cursor.py +0 -0
- {e6data_python_connector-3.0.2 → e6data_python_connector-3.0.3rc1}/e6data_python_connector/async_dialect.py +0 -0
- {e6data_python_connector-3.0.2 → e6data_python_connector-3.0.3rc1}/e6data_python_connector/async_oauth.py +0 -0
- {e6data_python_connector-3.0.2 → e6data_python_connector-3.0.3rc1}/e6data_python_connector/async_work.py +0 -0
- {e6data_python_connector-3.0.2 → e6data_python_connector-3.0.3rc1}/e6data_python_connector/cluster_server/__init__.py +0 -0
- {e6data_python_connector-3.0.2 → e6data_python_connector-3.0.3rc1}/e6data_python_connector/cluster_server/cluster_pb2.py +0 -0
- {e6data_python_connector-3.0.2 → e6data_python_connector-3.0.3rc1}/e6data_python_connector/cluster_server/cluster_pb2_grpc.py +0 -0
- {e6data_python_connector-3.0.2 → e6data_python_connector-3.0.3rc1}/e6data_python_connector/common.py +0 -0
- {e6data_python_connector-3.0.2 → e6data_python_connector-3.0.3rc1}/e6data_python_connector/connection_pool.py +0 -0
- {e6data_python_connector-3.0.2 → e6data_python_connector-3.0.3rc1}/e6data_python_connector/constants.py +0 -0
- {e6data_python_connector-3.0.2 → e6data_python_connector-3.0.3rc1}/e6data_python_connector/date_time_utils.py +0 -0
- {e6data_python_connector-3.0.2 → e6data_python_connector-3.0.3rc1}/e6data_python_connector/dialect.py +0 -0
- {e6data_python_connector-3.0.2 → e6data_python_connector-3.0.3rc1}/e6data_python_connector/e6data_grpc.py +0 -0
- {e6data_python_connector-3.0.2 → e6data_python_connector-3.0.3rc1}/e6data_python_connector/e6x_vector/__init__.py +0 -0
- {e6data_python_connector-3.0.2 → e6data_python_connector-3.0.3rc1}/e6data_python_connector/e6x_vector/constants.py +0 -0
- {e6data_python_connector-3.0.2 → e6data_python_connector-3.0.3rc1}/e6data_python_connector/exceptions.py +0 -0
- {e6data_python_connector-3.0.2 → e6data_python_connector-3.0.3rc1}/e6data_python_connector/oauth.py +0 -0
- {e6data_python_connector-3.0.2 → e6data_python_connector-3.0.3rc1}/e6data_python_connector/oauth_cache.py +0 -0
- {e6data_python_connector-3.0.2 → e6data_python_connector-3.0.3rc1}/e6data_python_connector/oauth_common.py +0 -0
- {e6data_python_connector-3.0.2 → e6data_python_connector-3.0.3rc1}/e6data_python_connector/result_batch.py +0 -0
- {e6data_python_connector-3.0.2 → e6data_python_connector-3.0.3rc1}/e6data_python_connector/result_decode.py +0 -0
- {e6data_python_connector-3.0.2 → e6data_python_connector-3.0.3rc1}/e6data_python_connector/result_decode_worker.py +0 -0
- {e6data_python_connector-3.0.2 → e6data_python_connector-3.0.3rc1}/e6data_python_connector/result_prefetch.py +0 -0
- {e6data_python_connector-3.0.2 → e6data_python_connector-3.0.3rc1}/e6data_python_connector/server/QueryEngineService.py +0 -0
- {e6data_python_connector-3.0.2 → e6data_python_connector-3.0.3rc1}/e6data_python_connector/server/__init__.py +0 -0
- {e6data_python_connector-3.0.2 → e6data_python_connector-3.0.3rc1}/e6data_python_connector/server/constants.py +0 -0
- {e6data_python_connector-3.0.2 → e6data_python_connector-3.0.3rc1}/e6data_python_connector/server/e6x_engine_pb2.py +0 -0
- {e6data_python_connector-3.0.2 → e6data_python_connector-3.0.3rc1}/e6data_python_connector/server/e6x_engine_pb2.pyi +0 -0
- {e6data_python_connector-3.0.2 → e6data_python_connector-3.0.3rc1}/e6data_python_connector/server/e6x_engine_pb2_grpc.py +0 -0
- {e6data_python_connector-3.0.2 → e6data_python_connector-3.0.3rc1}/e6data_python_connector/server/ttypes.py +0 -0
- {e6data_python_connector-3.0.2 → e6data_python_connector-3.0.3rc1}/e6data_python_connector/strategy.py +0 -0
- {e6data_python_connector-3.0.2 → e6data_python_connector-3.0.3rc1}/e6data_python_connector/typeId.py +0 -0
- {e6data_python_connector-3.0.2 → e6data_python_connector-3.0.3rc1}/e6data_python_connector.egg-info/dependency_links.txt +0 -0
- {e6data_python_connector-3.0.2 → e6data_python_connector-3.0.3rc1}/e6data_python_connector.egg-info/entry_points.txt +0 -0
- {e6data_python_connector-3.0.2 → e6data_python_connector-3.0.3rc1}/e6data_python_connector.egg-info/requires.txt +0 -0
- {e6data_python_connector-3.0.2 → e6data_python_connector-3.0.3rc1}/e6data_python_connector.egg-info/top_level.txt +0 -0
- {e6data_python_connector-3.0.2 → e6data_python_connector-3.0.3rc1}/pyproject.toml +0 -0
- {e6data_python_connector-3.0.2 → e6data_python_connector-3.0.3rc1}/setup.cfg +0 -0
- {e6data_python_connector-3.0.2 → e6data_python_connector-3.0.3rc1}/test/__init__.py +0 -0
- {e6data_python_connector-3.0.2 → e6data_python_connector-3.0.3rc1}/test/analyze_38_nines.py +0 -0
- {e6data_python_connector-3.0.2 → e6data_python_connector-3.0.3rc1}/test/analyze_all_cases.py +0 -0
- {e6data_python_connector-3.0.2 → e6data_python_connector-3.0.3rc1}/test/analyze_binary.py +0 -0
- {e6data_python_connector-3.0.2 → e6data_python_connector-3.0.3rc1}/test/analyze_correct_value.py +0 -0
- {e6data_python_connector-3.0.2 → e6data_python_connector-3.0.3rc1}/test/analyze_fields.py +0 -0
- {e6data_python_connector-3.0.2 → e6data_python_connector-3.0.3rc1}/test/check_decimal_errors.py +0 -0
- {e6data_python_connector-3.0.2 → e6data_python_connector-3.0.3rc1}/test/cleanup_test_files.py +0 -0
- {e6data_python_connector-3.0.2 → e6data_python_connector-3.0.3rc1}/test/conftest.py +0 -0
- {e6data_python_connector-3.0.2 → e6data_python_connector-3.0.3rc1}/test/debug_38_nines.py +0 -0
- {e6data_python_connector-3.0.2 → e6data_python_connector-3.0.3rc1}/test/debug_binary.py +0 -0
- {e6data_python_connector-3.0.2 → e6data_python_connector-3.0.3rc1}/test/final_test.py +0 -0
- {e6data_python_connector-3.0.2 → e6data_python_connector-3.0.3rc1}/test/integration/__init__.py +0 -0
- {e6data_python_connector-3.0.2 → e6data_python_connector-3.0.3rc1}/test/integration/benchmark_preflight.py +0 -0
- {e6data_python_connector-3.0.2 → e6data_python_connector-3.0.3rc1}/test/integration/benchmark_result_batch_v2.py +0 -0
- {e6data_python_connector-3.0.2 → e6data_python_connector-3.0.3rc1}/test/integration/benchmark_sync_async.py +0 -0
- {e6data_python_connector-3.0.2 → e6data_python_connector-3.0.3rc1}/test/integration/expiry_preflight.py +0 -0
- {e6data_python_connector-3.0.2 → e6data_python_connector-3.0.3rc1}/test/integration/result_batch_benchmark_support.py +0 -0
- {e6data_python_connector-3.0.2 → e6data_python_connector-3.0.3rc1}/test/integration/support.py +0 -0
- {e6data_python_connector-3.0.2 → e6data_python_connector-3.0.3rc1}/test/integration/test_async_connection.py +0 -0
- {e6data_python_connector-3.0.2 → e6data_python_connector-3.0.3rc1}/test/integration/test_async_pool.py +0 -0
- {e6data_python_connector-3.0.2 → e6data_python_connector-3.0.3rc1}/test/integration/test_async_queries.py +0 -0
- {e6data_python_connector-3.0.2 → e6data_python_connector-3.0.3rc1}/test/integration/test_async_resume.py +0 -0
- {e6data_python_connector-3.0.2 → e6data_python_connector-3.0.3rc1}/test/integration/test_async_sqlalchemy.py +0 -0
- {e6data_python_connector-3.0.2 → e6data_python_connector-3.0.3rc1}/test/integration/test_async_token_lifecycle.py +0 -0
- {e6data_python_connector-3.0.2 → e6data_python_connector-3.0.3rc1}/test/integration/test_e2e_auth_boundary.py +0 -0
- {e6data_python_connector-3.0.2 → e6data_python_connector-3.0.3rc1}/test/integration/test_e2e_oauth_continuation.py +0 -0
- {e6data_python_connector-3.0.2 → e6data_python_connector-3.0.3rc1}/test/integration/test_e2e_readonly_matrix.py +0 -0
- {e6data_python_connector-3.0.2 → e6data_python_connector-3.0.3rc1}/test/integration/test_result_batch_v2.py +0 -0
- {e6data_python_connector-3.0.2 → e6data_python_connector-3.0.3rc1}/test/integration/test_shared_token_issuance.py +0 -0
- {e6data_python_connector-3.0.2 → e6data_python_connector-3.0.3rc1}/test/integration/test_sync_oauth_lifecycle.py +0 -0
- {e6data_python_connector-3.0.2 → e6data_python_connector-3.0.3rc1}/test/integration/token_observation.py +0 -0
- {e6data_python_connector-3.0.2 → e6data_python_connector-3.0.3rc1}/test/move_tests.py +0 -0
- {e6data_python_connector-3.0.2 → e6data_python_connector-3.0.3rc1}/test/quick_test.py +0 -0
- {e6data_python_connector-3.0.2 → e6data_python_connector-3.0.3rc1}/test/test_38_nines.py +0 -0
- {e6data_python_connector-3.0.2 → e6data_python_connector-3.0.3rc1}/test/test_all_decimal128_cases.py +0 -0
- {e6data_python_connector-3.0.2 → e6data_python_connector-3.0.3rc1}/test/test_cluster_manager_efficiency.py +0 -0
- {e6data_python_connector-3.0.2 → e6data_python_connector-3.0.3rc1}/test/test_cluster_manager_none_strategy.py +0 -0
- {e6data_python_connector-3.0.2 → e6data_python_connector-3.0.3rc1}/test/test_cluster_manager_oauth.py +0 -0
- {e6data_python_connector-3.0.2 → e6data_python_connector-3.0.3rc1}/test/test_cluster_manager_strategy.py +0 -0
- {e6data_python_connector-3.0.2 → e6data_python_connector-3.0.3rc1}/test/test_code_optimizations.py +0 -0
- {e6data_python_connector-3.0.2 → e6data_python_connector-3.0.3rc1}/test/test_comprehensive.py +0 -0
- {e6data_python_connector-3.0.2 → e6data_python_connector-3.0.3rc1}/test/test_connection_pool.py +0 -0
- {e6data_python_connector-3.0.2 → e6data_python_connector-3.0.3rc1}/test/test_connection_pool_e2e.py +0 -0
- {e6data_python_connector-3.0.2 → e6data_python_connector-3.0.3rc1}/test/test_current_implementation.py +0 -0
- {e6data_python_connector-3.0.2 → e6data_python_connector-3.0.3rc1}/test/test_decimal128_binary_parsing.py +0 -0
- {e6data_python_connector-3.0.2 → e6data_python_connector-3.0.3rc1}/test/test_decimal128_parsing.py +0 -0
- {e6data_python_connector-3.0.2 → e6data_python_connector-3.0.3rc1}/test/test_dialect_columns.py +0 -0
- {e6data_python_connector-3.0.2 → e6data_python_connector-3.0.3rc1}/test/test_fix.py +0 -0
- {e6data_python_connector-3.0.2 → e6data_python_connector-3.0.3rc1}/test/test_improved_parsing.py +0 -0
- {e6data_python_connector-3.0.2 → e6data_python_connector-3.0.3rc1}/test/test_known_case.py +0 -0
- {e6data_python_connector-3.0.2 → e6data_python_connector-3.0.3rc1}/test/test_manual_analysis.py +0 -0
- {e6data_python_connector-3.0.2 → e6data_python_connector-3.0.3rc1}/test/test_mock_server.py +0 -0
- {e6data_python_connector-3.0.2 → e6data_python_connector-3.0.3rc1}/test/test_multiprocessing_fix.py +0 -0
- {e6data_python_connector-3.0.2 → e6data_python_connector-3.0.3rc1}/test/test_new_implementation.py +0 -0
- {e6data_python_connector-3.0.2 → e6data_python_connector-3.0.3rc1}/test/test_oauth_authentication.py +0 -0
- {e6data_python_connector-3.0.2 → e6data_python_connector-3.0.3rc1}/test/test_oauth_auto_resume.py +0 -0
- {e6data_python_connector-3.0.2 → e6data_python_connector-3.0.3rc1}/test/test_oauth_resume_integration.py +0 -0
- {e6data_python_connector-3.0.2 → e6data_python_connector-3.0.3rc1}/test/test_pool_concurrency_simple.py +0 -0
- {e6data_python_connector-3.0.2 → e6data_python_connector-3.0.3rc1}/test/test_pool_threading_multiprocessing.py +0 -0
- {e6data_python_connector-3.0.2 → e6data_python_connector-3.0.3rc1}/test/test_simple_connection_pool.py +0 -0
- {e6data_python_connector-3.0.2 → e6data_python_connector-3.0.3rc1}/test/test_specific_binary.py +0 -0
- {e6data_python_connector-3.0.2 → e6data_python_connector-3.0.3rc1}/test/test_strategy.py +0 -0
- {e6data_python_connector-3.0.2 → e6data_python_connector-3.0.3rc1}/test/test_strategy_logic.py +0 -0
- {e6data_python_connector-3.0.2 → e6data_python_connector-3.0.3rc1}/test/test_strategy_persistence_fix.py +0 -0
- {e6data_python_connector-3.0.2 → e6data_python_connector-3.0.3rc1}/test/test_strategy_sharing_fix.py +0 -0
- {e6data_python_connector-3.0.2 → e6data_python_connector-3.0.3rc1}/test/test_user_binary_value.py +0 -0
- {e6data_python_connector-3.0.2 → e6data_python_connector-3.0.3rc1}/test/tests.py +0 -0
- {e6data_python_connector-3.0.2 → e6data_python_connector-3.0.3rc1}/test/tests_grpc.py +0 -0
- {e6data_python_connector-3.0.2 → e6data_python_connector-3.0.3rc1}/test/validate_decimal128.py +0 -0
- {e6data_python_connector-3.0.2 → e6data_python_connector-3.0.3rc1}/test/validate_implementation.py +0 -0
- {e6data_python_connector-3.0.2 → e6data_python_connector-3.0.3rc1}/test/verify_decimal_fix.py +0 -0
|
@@ -50,6 +50,9 @@ class _ResumeState:
|
|
|
50
50
|
def acknowledged(self):
|
|
51
51
|
self.pending = 'acknowledged'
|
|
52
52
|
|
|
53
|
+
def rejected(self):
|
|
54
|
+
self.pending = None
|
|
55
|
+
|
|
53
56
|
|
|
54
57
|
class _ResumeFlight:
|
|
55
58
|
"""One phase deadline, independently bounded shielded waiters."""
|
|
@@ -167,6 +170,8 @@ class AsyncClusterManager:
|
|
|
167
170
|
_remaining(deadline)
|
|
168
171
|
return await self._flight.wait(self._recover, deadline)
|
|
169
172
|
except (TimeoutError, grpc.RpcError) as error:
|
|
173
|
+
if isinstance(error, grpc.RpcError) and error.code() == grpc.StatusCode.FAILED_PRECONDITION:
|
|
174
|
+
raise OperationalError('Cluster recovery failed: {}'.format(error.details())) from error
|
|
170
175
|
raise OperationalError('Cluster recovery failed; a dispatched resume may still complete.') from error
|
|
171
176
|
|
|
172
177
|
async def _metadata(self, strategy, deadline, rejected_token=None):
|
|
@@ -199,6 +204,8 @@ class AsyncClusterManager:
|
|
|
199
204
|
self.last_successful_strategy = strategy
|
|
200
205
|
return response
|
|
201
206
|
except grpc.RpcError as error:
|
|
207
|
+
if operation == 'resume' and error.code() == grpc.StatusCode.FAILED_PRECONDITION:
|
|
208
|
+
self._mutation.rejected()
|
|
202
209
|
if (error.code() == grpc.StatusCode.UNAUTHENTICATED and not refreshed
|
|
203
210
|
and self._metadata_provider is not None):
|
|
204
211
|
refreshed = True
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
"""Lossless calendar intervals returned by MAKE_INTERVAL."""
|
|
2
|
+
from typing import NamedTuple
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class CalendarInterval(NamedTuple):
|
|
6
|
+
"""Months and calendar days are independent of elapsed microseconds."""
|
|
7
|
+
|
|
8
|
+
months: int
|
|
9
|
+
days: int
|
|
10
|
+
microseconds: int
|
|
11
|
+
|
|
12
|
+
def __str__(self):
|
|
13
|
+
month_sign = -1 if self.months < 0 else 1
|
|
14
|
+
time_sign = -1 if self.microseconds < 0 else 1
|
|
15
|
+
months = abs(self.months)
|
|
16
|
+
micros = abs(self.microseconds)
|
|
17
|
+
values = (month_sign * (months // 12), month_sign * (months % 12),
|
|
18
|
+
self.days, time_sign * (micros // 3600000000),
|
|
19
|
+
time_sign * (micros // 60000000 % 60))
|
|
20
|
+
parts = [f"{value} {unit}" for value, unit in
|
|
21
|
+
zip(values, ("years", "months", "days", "hours", "minutes")) if value]
|
|
22
|
+
seconds = micros % 60000000
|
|
23
|
+
if seconds:
|
|
24
|
+
whole, fraction = divmod(seconds, 1000000)
|
|
25
|
+
text = str(whole)
|
|
26
|
+
if fraction:
|
|
27
|
+
text += f".{fraction:06d}".rstrip('0')
|
|
28
|
+
parts.append(f"{'-' if time_sign < 0 else ''}{text} seconds")
|
|
29
|
+
return ' '.join(parts) if parts else '0 seconds'
|
|
@@ -571,6 +571,8 @@ class ClusterManager:
|
|
|
571
571
|
except _InactiveRpcError as e:
|
|
572
572
|
if self._debug:
|
|
573
573
|
logger.error(f"Failed to send resume request: {e}")
|
|
574
|
+
if e.code() == grpc.StatusCode.FAILED_PRECONDITION:
|
|
575
|
+
raise
|
|
574
576
|
return False
|
|
575
577
|
elif current_status.status == 'active':
|
|
576
578
|
if self._debug:
|
|
@@ -10,6 +10,7 @@ from thrift.transport import TTransport
|
|
|
10
10
|
|
|
11
11
|
from e6data_python_connector.e6x_vector.ttypes import Chunk, Vector, VectorType
|
|
12
12
|
from e6data_python_connector.constants import ZONE
|
|
13
|
+
from e6data_python_connector.calendar_interval import CalendarInterval
|
|
13
14
|
from e6data_python_connector.date_time_utils import (
|
|
14
15
|
floor_div,
|
|
15
16
|
floor_mod,
|
|
@@ -478,6 +479,8 @@ def read_values_from_array(query_columns_description: list, dis: DataInputStream
|
|
|
478
479
|
try:
|
|
479
480
|
if dtype == "LONG":
|
|
480
481
|
value_array.append(dis.read_long())
|
|
482
|
+
elif dtype == "CALENDAR_INTERVAL":
|
|
483
|
+
value_array.append(CalendarInterval(dis.read_int(), dis.read_int(), dis.read_long()))
|
|
481
484
|
elif dtype == "INTERVAL_DAY_SECOND":
|
|
482
485
|
value_array.append(timedelta(microseconds=dis.read_long()))
|
|
483
486
|
elif dtype == "DATE":
|
|
@@ -584,6 +587,18 @@ def get_column_from_chunk(vector: Vector, strict=False) -> list:
|
|
|
584
587
|
continue
|
|
585
588
|
value_array.append(vector.data.int64Data.data[
|
|
586
589
|
row] if not vector.isConstantVector else vector.data.numericConstantData.data)
|
|
590
|
+
elif d_type == VectorType.CALENDAR_INTERVAL:
|
|
591
|
+
payload = getattr(vector.data, 'calendarIntervalData', None)
|
|
592
|
+
for row in range(vector.size):
|
|
593
|
+
if get_null(vector, row):
|
|
594
|
+
value_array.append(None)
|
|
595
|
+
continue
|
|
596
|
+
index = 0 if vector.isConstantVector else row
|
|
597
|
+
components = (payload.months, payload.days, payload.microseconds) if payload else ()
|
|
598
|
+
if not components or any(values is None or index >= len(values) or values[index] is None
|
|
599
|
+
for values in components):
|
|
600
|
+
raise ValueError("Incomplete CALENDAR_INTERVAL vector payload")
|
|
601
|
+
value_array.append(CalendarInterval(*(values[index] for values in components)))
|
|
587
602
|
elif d_type == VectorType.INTERVAL_DAY_SECOND:
|
|
588
603
|
for row in range(vector.size):
|
|
589
604
|
if get_null(vector, row):
|
|
@@ -36,6 +36,7 @@ class VectorType(object):
|
|
|
36
36
|
TIMESTAMP_TZ = 15
|
|
37
37
|
DECIMAL128 = 16
|
|
38
38
|
INTERVAL_DAY_SECOND = 17
|
|
39
|
+
CALENDAR_INTERVAL = 18
|
|
39
40
|
|
|
40
41
|
_VALUES_TO_NAMES = {
|
|
41
42
|
0: "BINARY",
|
|
@@ -56,6 +57,7 @@ class VectorType(object):
|
|
|
56
57
|
15: "TIMESTAMP_TZ",
|
|
57
58
|
16: "DECIMAL128",
|
|
58
59
|
17: "INTERVAL_DAY_SECOND",
|
|
60
|
+
18: "CALENDAR_INTERVAL",
|
|
59
61
|
}
|
|
60
62
|
|
|
61
63
|
_NAMES_TO_VALUES = {
|
|
@@ -77,6 +79,7 @@ class VectorType(object):
|
|
|
77
79
|
"TIMESTAMP_TZ": 15,
|
|
78
80
|
"DECIMAL128": 16,
|
|
79
81
|
"INTERVAL_DAY_SECOND": 17,
|
|
82
|
+
"CALENDAR_INTERVAL": 18,
|
|
80
83
|
}
|
|
81
84
|
|
|
82
85
|
|
|
@@ -235,12 +238,13 @@ class Data(object):
|
|
|
235
238
|
- timeData
|
|
236
239
|
- decimal128Data
|
|
237
240
|
- numericDecimal128ConstantData
|
|
241
|
+
- calendarIntervalData
|
|
238
242
|
|
|
239
243
|
"""
|
|
240
244
|
thrift_spec = None
|
|
241
245
|
|
|
242
246
|
|
|
243
|
-
def __init__(self, boolData = None, int32Data = None, int64Data = None, dateData = None, float32Data = None, float64Data = None, varcharData = None, boolConstantData = None, dateConstantData = None, nullConstantData = None, numericConstantData = None, numericDecimalConstantData = None, temporalIntervalConstantData = None, timeConstantData = None, varcharConstantData = None, timeData = None, decimal128Data = None, numericDecimal128ConstantData = None,):
|
|
247
|
+
def __init__(self, boolData = None, int32Data = None, int64Data = None, dateData = None, float32Data = None, float64Data = None, varcharData = None, boolConstantData = None, dateConstantData = None, nullConstantData = None, numericConstantData = None, numericDecimalConstantData = None, temporalIntervalConstantData = None, timeConstantData = None, varcharConstantData = None, timeData = None, decimal128Data = None, numericDecimal128ConstantData = None, calendarIntervalData = None,):
|
|
244
248
|
self.boolData = boolData
|
|
245
249
|
self.int32Data = int32Data
|
|
246
250
|
self.int64Data = int64Data
|
|
@@ -259,6 +263,7 @@ class Data(object):
|
|
|
259
263
|
self.timeData = timeData
|
|
260
264
|
self.decimal128Data = decimal128Data
|
|
261
265
|
self.numericDecimal128ConstantData = numericDecimal128ConstantData
|
|
266
|
+
self.calendarIntervalData = calendarIntervalData
|
|
262
267
|
|
|
263
268
|
def read(self, iprot):
|
|
264
269
|
if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None:
|
|
@@ -377,6 +382,12 @@ class Data(object):
|
|
|
377
382
|
self.numericDecimal128ConstantData.read(iprot)
|
|
378
383
|
else:
|
|
379
384
|
iprot.skip(ftype)
|
|
385
|
+
elif fid == 23:
|
|
386
|
+
if ftype == TType.STRUCT:
|
|
387
|
+
self.calendarIntervalData = CalendarIntervalData()
|
|
388
|
+
self.calendarIntervalData.read(iprot)
|
|
389
|
+
else:
|
|
390
|
+
iprot.skip(ftype)
|
|
380
391
|
else:
|
|
381
392
|
iprot.skip(ftype)
|
|
382
393
|
iprot.readFieldEnd()
|
|
@@ -460,6 +471,10 @@ class Data(object):
|
|
|
460
471
|
oprot.writeFieldBegin('numericDecimal128ConstantData', TType.STRUCT, 18)
|
|
461
472
|
self.numericDecimal128ConstantData.write(oprot)
|
|
462
473
|
oprot.writeFieldEnd()
|
|
474
|
+
if self.calendarIntervalData is not None:
|
|
475
|
+
oprot.writeFieldBegin('calendarIntervalData', TType.STRUCT, 23)
|
|
476
|
+
self.calendarIntervalData.write(oprot)
|
|
477
|
+
oprot.writeFieldEnd()
|
|
463
478
|
oprot.writeFieldStop()
|
|
464
479
|
oprot.writeStructEnd()
|
|
465
480
|
|
|
@@ -1741,6 +1756,111 @@ class Chunk(object):
|
|
|
1741
1756
|
|
|
1742
1757
|
def __ne__(self, other):
|
|
1743
1758
|
return not (self == other)
|
|
1759
|
+
|
|
1760
|
+
|
|
1761
|
+
class CalendarIntervalData(object):
|
|
1762
|
+
"""
|
|
1763
|
+
Attributes:
|
|
1764
|
+
- months
|
|
1765
|
+
- days
|
|
1766
|
+
- microseconds
|
|
1767
|
+
|
|
1768
|
+
"""
|
|
1769
|
+
thrift_spec = None
|
|
1770
|
+
|
|
1771
|
+
|
|
1772
|
+
def __init__(self, months = None, days = None, microseconds = None,):
|
|
1773
|
+
self.months = months
|
|
1774
|
+
self.days = days
|
|
1775
|
+
self.microseconds = microseconds
|
|
1776
|
+
|
|
1777
|
+
def read(self, iprot):
|
|
1778
|
+
if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None:
|
|
1779
|
+
iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec])
|
|
1780
|
+
return
|
|
1781
|
+
iprot.readStructBegin()
|
|
1782
|
+
while True:
|
|
1783
|
+
(fname, ftype, fid) = iprot.readFieldBegin()
|
|
1784
|
+
if ftype == TType.STOP:
|
|
1785
|
+
break
|
|
1786
|
+
if fid == 1:
|
|
1787
|
+
if ftype == TType.LIST:
|
|
1788
|
+
self.months = []
|
|
1789
|
+
(_etype87, _size84) = iprot.readListBegin()
|
|
1790
|
+
for _i88 in range(_size84):
|
|
1791
|
+
_elem89 = iprot.readI32()
|
|
1792
|
+
self.months.append(_elem89)
|
|
1793
|
+
iprot.readListEnd()
|
|
1794
|
+
else:
|
|
1795
|
+
iprot.skip(ftype)
|
|
1796
|
+
elif fid == 2:
|
|
1797
|
+
if ftype == TType.LIST:
|
|
1798
|
+
self.days = []
|
|
1799
|
+
(_etype93, _size90) = iprot.readListBegin()
|
|
1800
|
+
for _i94 in range(_size90):
|
|
1801
|
+
_elem95 = iprot.readI32()
|
|
1802
|
+
self.days.append(_elem95)
|
|
1803
|
+
iprot.readListEnd()
|
|
1804
|
+
else:
|
|
1805
|
+
iprot.skip(ftype)
|
|
1806
|
+
elif fid == 3:
|
|
1807
|
+
if ftype == TType.LIST:
|
|
1808
|
+
self.microseconds = []
|
|
1809
|
+
(_etype99, _size96) = iprot.readListBegin()
|
|
1810
|
+
for _i100 in range(_size96):
|
|
1811
|
+
_elem101 = iprot.readI64()
|
|
1812
|
+
self.microseconds.append(_elem101)
|
|
1813
|
+
iprot.readListEnd()
|
|
1814
|
+
else:
|
|
1815
|
+
iprot.skip(ftype)
|
|
1816
|
+
else:
|
|
1817
|
+
iprot.skip(ftype)
|
|
1818
|
+
iprot.readFieldEnd()
|
|
1819
|
+
iprot.readStructEnd()
|
|
1820
|
+
|
|
1821
|
+
def write(self, oprot):
|
|
1822
|
+
self.validate()
|
|
1823
|
+
if oprot._fast_encode is not None and self.thrift_spec is not None:
|
|
1824
|
+
oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec]))
|
|
1825
|
+
return
|
|
1826
|
+
oprot.writeStructBegin('CalendarIntervalData')
|
|
1827
|
+
if self.months is not None:
|
|
1828
|
+
oprot.writeFieldBegin('months', TType.LIST, 1)
|
|
1829
|
+
oprot.writeListBegin(TType.I32, len(self.months))
|
|
1830
|
+
for iter102 in self.months:
|
|
1831
|
+
oprot.writeI32(iter102)
|
|
1832
|
+
oprot.writeListEnd()
|
|
1833
|
+
oprot.writeFieldEnd()
|
|
1834
|
+
if self.days is not None:
|
|
1835
|
+
oprot.writeFieldBegin('days', TType.LIST, 2)
|
|
1836
|
+
oprot.writeListBegin(TType.I32, len(self.days))
|
|
1837
|
+
for iter103 in self.days:
|
|
1838
|
+
oprot.writeI32(iter103)
|
|
1839
|
+
oprot.writeListEnd()
|
|
1840
|
+
oprot.writeFieldEnd()
|
|
1841
|
+
if self.microseconds is not None:
|
|
1842
|
+
oprot.writeFieldBegin('microseconds', TType.LIST, 3)
|
|
1843
|
+
oprot.writeListBegin(TType.I64, len(self.microseconds))
|
|
1844
|
+
for iter104 in self.microseconds:
|
|
1845
|
+
oprot.writeI64(iter104)
|
|
1846
|
+
oprot.writeListEnd()
|
|
1847
|
+
oprot.writeFieldEnd()
|
|
1848
|
+
oprot.writeFieldStop()
|
|
1849
|
+
oprot.writeStructEnd()
|
|
1850
|
+
|
|
1851
|
+
def validate(self):
|
|
1852
|
+
return
|
|
1853
|
+
|
|
1854
|
+
def __repr__(self):
|
|
1855
|
+
L = ['%s=%r' % (key, value)
|
|
1856
|
+
for key, value in self.__dict__.items()]
|
|
1857
|
+
return '%s(%s)' % (self.__class__.__name__, ', '.join(L))
|
|
1858
|
+
|
|
1859
|
+
def __eq__(self, other):
|
|
1860
|
+
return isinstance(other, self.__class__) and self.__dict__ == other.__dict__
|
|
1861
|
+
|
|
1862
|
+
def __ne__(self, other):
|
|
1863
|
+
return not (self == other)
|
|
1744
1864
|
all_structs.append(Vector)
|
|
1745
1865
|
Vector.thrift_spec = (
|
|
1746
1866
|
None, # 0
|
|
@@ -1773,6 +1893,11 @@ Data.thrift_spec = (
|
|
|
1773
1893
|
(16, TType.STRUCT, 'timeData', [TimeData, None], None, ), # 16
|
|
1774
1894
|
(17, TType.STRUCT, 'decimal128Data', [Decimal128Data, None], None, ), # 17
|
|
1775
1895
|
(18, TType.STRUCT, 'numericDecimal128ConstantData', [NumericDecimal128ConstantData, None], None, ), # 18
|
|
1896
|
+
None, # 19
|
|
1897
|
+
None, # 20
|
|
1898
|
+
None, # 21
|
|
1899
|
+
None, # 22
|
|
1900
|
+
(23, TType.STRUCT, 'calendarIntervalData', [CalendarIntervalData, None], None, ), # 23
|
|
1776
1901
|
)
|
|
1777
1902
|
all_structs.append(BoolData)
|
|
1778
1903
|
BoolData.thrift_spec = (
|
|
@@ -1874,5 +1999,12 @@ Chunk.thrift_spec = (
|
|
|
1874
1999
|
(1, TType.I32, 'size', None, None, ), # 1
|
|
1875
2000
|
(2, TType.LIST, 'vectors', (TType.STRUCT, [Vector, None], False), None, ), # 2
|
|
1876
2001
|
)
|
|
2002
|
+
all_structs.append(CalendarIntervalData)
|
|
2003
|
+
CalendarIntervalData.thrift_spec = (
|
|
2004
|
+
None, # 0
|
|
2005
|
+
(1, TType.LIST, 'months', (TType.I32, None, False), None, ), # 1
|
|
2006
|
+
(2, TType.LIST, 'days', (TType.I32, None, False), None, ), # 2
|
|
2007
|
+
(3, TType.LIST, 'microseconds', (TType.I64, None, False), None, ), # 3
|
|
2008
|
+
)
|
|
1877
2009
|
fix_spec(all_structs)
|
|
1878
2010
|
del all_structs
|
|
@@ -13,6 +13,7 @@ e6data_python_connector/async_cursor.py
|
|
|
13
13
|
e6data_python_connector/async_dialect.py
|
|
14
14
|
e6data_python_connector/async_oauth.py
|
|
15
15
|
e6data_python_connector/async_work.py
|
|
16
|
+
e6data_python_connector/calendar_interval.py
|
|
16
17
|
e6data_python_connector/cluster_manager.py
|
|
17
18
|
e6data_python_connector/common.py
|
|
18
19
|
e6data_python_connector/connection_pool.py
|
|
@@ -115,6 +116,7 @@ test/integration/test_async_queries.py
|
|
|
115
116
|
test/integration/test_async_resume.py
|
|
116
117
|
test/integration/test_async_sqlalchemy.py
|
|
117
118
|
test/integration/test_async_token_lifecycle.py
|
|
119
|
+
test/integration/test_cluster_resume_errors.py
|
|
118
120
|
test/integration/test_e2e_auth_boundary.py
|
|
119
121
|
test/integration/test_e2e_oauth_continuation.py
|
|
120
122
|
test/integration/test_e2e_readonly_matrix.py
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
"""Real RPC regressions on an explicitly prepared, exclusive-use cluster.
|
|
2
|
+
|
|
3
|
+
The operator must suspend the cluster and disable auto-resume before opting in.
|
|
4
|
+
These tests observe real requests and never alter responses or cluster settings.
|
|
5
|
+
"""
|
|
6
|
+
import asyncio
|
|
7
|
+
import os
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
|
|
10
|
+
import grpc
|
|
11
|
+
import pytest
|
|
12
|
+
|
|
13
|
+
from e6data_python_connector import Connection
|
|
14
|
+
from e6data_python_connector.aio import AsyncConnection
|
|
15
|
+
from e6data_python_connector.async_cluster_manager import AsyncClusterManager
|
|
16
|
+
from e6data_python_connector.cluster_manager import ClusterManager
|
|
17
|
+
from e6data_python_connector.exceptions import OperationalError
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
pytestmark = pytest.mark.integration
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
@pytest.fixture
|
|
24
|
+
def rejected_resume_config(live_config):
|
|
25
|
+
if os.environ.get('E6_RESUME_REJECTION_AUTHORIZED') != '1':
|
|
26
|
+
pytest.skip('Requires an exclusive suspended target with auto-resume disabled')
|
|
27
|
+
if not live_config.connection_kwargs.get('password'):
|
|
28
|
+
pytest.skip('This regression requires the legacy password authentication path')
|
|
29
|
+
return live_config
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def _manager_options(config):
|
|
33
|
+
settings = config.connection_kwargs
|
|
34
|
+
return dict(host=settings['host'], port=settings['port'],
|
|
35
|
+
user=settings['username'], password=settings['password'],
|
|
36
|
+
secure_channel=settings.get('secure', False),
|
|
37
|
+
cluster_uuid=settings.get('cluster_name'),
|
|
38
|
+
ssl_cert=settings.get('ssl_cert'),
|
|
39
|
+
grpc_options=settings.get('grpc_options'), timeout=15)
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
class _ObservedSyncManager(ClusterManager):
|
|
43
|
+
"""Observe inherited real RPCs without replacing the transport."""
|
|
44
|
+
|
|
45
|
+
def _try_cluster_request(self, operation):
|
|
46
|
+
self.operations.append(operation)
|
|
47
|
+
try:
|
|
48
|
+
return super()._try_cluster_request(operation)
|
|
49
|
+
except grpc.RpcError as error:
|
|
50
|
+
self.errors.append(error)
|
|
51
|
+
raise
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
class _ObservedAsyncManager(AsyncClusterManager):
|
|
55
|
+
"""Observe inherited real RPCs without replacing the transport."""
|
|
56
|
+
|
|
57
|
+
async def _request(self, client, operation, deadline):
|
|
58
|
+
self.operations.append(operation)
|
|
59
|
+
try:
|
|
60
|
+
return await super()._request(client, operation, deadline)
|
|
61
|
+
except grpc.RpcError as error:
|
|
62
|
+
self.errors.append(error)
|
|
63
|
+
raise
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def _observed_manager(manager_type, config, *, timeout=15, debug=False):
|
|
67
|
+
options = _manager_options(config)
|
|
68
|
+
options.update(timeout=timeout, debug=debug)
|
|
69
|
+
manager = manager_type(**options)
|
|
70
|
+
manager.operations = []
|
|
71
|
+
manager.errors = []
|
|
72
|
+
return manager
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def _assert_disabled(error):
|
|
76
|
+
assert error.code() == grpc.StatusCode.FAILED_PRECONDITION
|
|
77
|
+
assert 'disabled' in error.details().lower()
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def test_sync_active_cluster_does_not_dispatch_resume(live_config):
|
|
81
|
+
if os.environ.get('E6_RESUME_ACTIVE_AUTHORIZED') != '1':
|
|
82
|
+
pytest.skip('Requires an explicitly prepared active target')
|
|
83
|
+
if not live_config.connection_kwargs.get('password'):
|
|
84
|
+
pytest.skip('This regression requires the legacy password authentication path')
|
|
85
|
+
manager = _observed_manager(_ObservedSyncManager, live_config, debug=True)
|
|
86
|
+
try:
|
|
87
|
+
assert manager.resume() is True
|
|
88
|
+
assert manager.operations == ['status']
|
|
89
|
+
assert manager.errors == []
|
|
90
|
+
finally:
|
|
91
|
+
if hasattr(manager, '_channel'):
|
|
92
|
+
manager._channel.close()
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
def test_sync_resume_preserves_original_terminal_error(rejected_resume_config):
|
|
96
|
+
manager = _observed_manager(_ObservedSyncManager, rejected_resume_config)
|
|
97
|
+
try:
|
|
98
|
+
with pytest.raises(grpc.RpcError) as raised:
|
|
99
|
+
manager.resume()
|
|
100
|
+
_assert_disabled(raised.value)
|
|
101
|
+
assert raised.value is manager.errors[-1]
|
|
102
|
+
assert manager.operations == ['status', 'resume']
|
|
103
|
+
finally:
|
|
104
|
+
if hasattr(manager, '_channel'):
|
|
105
|
+
manager._channel.close()
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
def test_sync_query_preserves_disabled_resume_error(rejected_resume_config):
|
|
109
|
+
options = dict(rejected_resume_config.connection_kwargs, auto_resume=True)
|
|
110
|
+
with pytest.raises(grpc.RpcError) as raised:
|
|
111
|
+
with Connection(**options) as connection:
|
|
112
|
+
with connection.cursor() as cursor:
|
|
113
|
+
cursor.execute(rejected_resume_config.read_only_sql)
|
|
114
|
+
_assert_disabled(raised.value)
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
def test_async_resume_preserves_terminal_reason_and_cause(rejected_resume_config):
|
|
118
|
+
async def run():
|
|
119
|
+
manager = _observed_manager(_ObservedAsyncManager, rejected_resume_config)
|
|
120
|
+
try:
|
|
121
|
+
with pytest.raises(OperationalError) as raised:
|
|
122
|
+
await manager.resume()
|
|
123
|
+
cause = raised.value.__cause__
|
|
124
|
+
_assert_disabled(cause)
|
|
125
|
+
assert cause is manager.errors[-1]
|
|
126
|
+
assert cause.details() in str(raised.value)
|
|
127
|
+
assert manager.operations == ['status', 'resume']
|
|
128
|
+
finally:
|
|
129
|
+
await manager.close()
|
|
130
|
+
asyncio.run(run())
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
def test_async_rejected_resume_allows_a_later_attempt(rejected_resume_config):
|
|
134
|
+
async def run():
|
|
135
|
+
manager = _observed_manager(_ObservedAsyncManager, rejected_resume_config)
|
|
136
|
+
try:
|
|
137
|
+
for _ in range(2):
|
|
138
|
+
with pytest.raises(OperationalError) as raised:
|
|
139
|
+
await manager.resume()
|
|
140
|
+
_assert_disabled(raised.value.__cause__)
|
|
141
|
+
assert manager._mutation.pending is None
|
|
142
|
+
assert manager.operations == ['status', 'resume', 'status', 'resume']
|
|
143
|
+
finally:
|
|
144
|
+
await manager.close()
|
|
145
|
+
asyncio.run(run())
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
def test_async_same_manager_recovers_after_reenable(rejected_resume_config):
|
|
149
|
+
signal_value = os.environ.get('E6_RESUME_REENABLE_SIGNAL')
|
|
150
|
+
if not signal_value:
|
|
151
|
+
pytest.skip('Requires operator re-enable coordination through a fresh signal path')
|
|
152
|
+
signal = Path(signal_value)
|
|
153
|
+
assert signal.is_absolute()
|
|
154
|
+
rejected, enabled = Path(str(signal) + '.rejected'), Path(str(signal) + '.enabled')
|
|
155
|
+
assert not rejected.exists() and not enabled.exists(), 'Use a fresh signal path'
|
|
156
|
+
|
|
157
|
+
async def run():
|
|
158
|
+
manager = _observed_manager(_ObservedAsyncManager, rejected_resume_config, timeout=300)
|
|
159
|
+
try:
|
|
160
|
+
with pytest.raises(OperationalError) as raised:
|
|
161
|
+
await manager.resume()
|
|
162
|
+
_assert_disabled(raised.value.__cause__)
|
|
163
|
+
assert manager._mutation.pending is None
|
|
164
|
+
rejected.touch(mode=0o600)
|
|
165
|
+
async with asyncio.timeout(180):
|
|
166
|
+
while not enabled.exists():
|
|
167
|
+
await asyncio.sleep(.25)
|
|
168
|
+
assert await manager.resume()
|
|
169
|
+
assert manager._mutation.pending is None
|
|
170
|
+
assert manager.operations.count('resume') == 2
|
|
171
|
+
async with AsyncConnection(**rejected_resume_config.connection_kwargs) as connection:
|
|
172
|
+
async with connection.cursor() as cursor:
|
|
173
|
+
await cursor.execute(rejected_resume_config.read_only_sql)
|
|
174
|
+
assert [list(row) for row in await cursor.fetchall()] == rejected_resume_config.expected_rows
|
|
175
|
+
finally:
|
|
176
|
+
await manager.close()
|
|
177
|
+
rejected.unlink(missing_ok=True)
|
|
178
|
+
enabled.unlink(missing_ok=True)
|
|
179
|
+
asyncio.run(run())
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{e6data_python_connector-3.0.2 → e6data_python_connector-3.0.3rc1}/e6data_python_connector/aio.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{e6data_python_connector-3.0.2 → e6data_python_connector-3.0.3rc1}/e6data_python_connector/common.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{e6data_python_connector-3.0.2 → e6data_python_connector-3.0.3rc1}/e6data_python_connector/oauth.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{e6data_python_connector-3.0.2 → e6data_python_connector-3.0.3rc1}/e6data_python_connector/typeId.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{e6data_python_connector-3.0.2 → e6data_python_connector-3.0.3rc1}/test/analyze_all_cases.py
RENAMED
|
File without changes
|
|
File without changes
|
{e6data_python_connector-3.0.2 → e6data_python_connector-3.0.3rc1}/test/analyze_correct_value.py
RENAMED
|
File without changes
|
|
File without changes
|
{e6data_python_connector-3.0.2 → e6data_python_connector-3.0.3rc1}/test/check_decimal_errors.py
RENAMED
|
File without changes
|
{e6data_python_connector-3.0.2 → e6data_python_connector-3.0.3rc1}/test/cleanup_test_files.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{e6data_python_connector-3.0.2 → e6data_python_connector-3.0.3rc1}/test/integration/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{e6data_python_connector-3.0.2 → e6data_python_connector-3.0.3rc1}/test/integration/support.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{e6data_python_connector-3.0.2 → e6data_python_connector-3.0.3rc1}/test/test_all_decimal128_cases.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{e6data_python_connector-3.0.2 → e6data_python_connector-3.0.3rc1}/test/test_code_optimizations.py
RENAMED
|
File without changes
|
{e6data_python_connector-3.0.2 → e6data_python_connector-3.0.3rc1}/test/test_comprehensive.py
RENAMED
|
File without changes
|
{e6data_python_connector-3.0.2 → e6data_python_connector-3.0.3rc1}/test/test_connection_pool.py
RENAMED
|
File without changes
|
{e6data_python_connector-3.0.2 → e6data_python_connector-3.0.3rc1}/test/test_connection_pool_e2e.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{e6data_python_connector-3.0.2 → e6data_python_connector-3.0.3rc1}/test/test_decimal128_parsing.py
RENAMED
|
File without changes
|
{e6data_python_connector-3.0.2 → e6data_python_connector-3.0.3rc1}/test/test_dialect_columns.py
RENAMED
|
File without changes
|
|
File without changes
|
{e6data_python_connector-3.0.2 → e6data_python_connector-3.0.3rc1}/test/test_improved_parsing.py
RENAMED
|
File without changes
|
|
File without changes
|
{e6data_python_connector-3.0.2 → e6data_python_connector-3.0.3rc1}/test/test_manual_analysis.py
RENAMED
|
File without changes
|
|
File without changes
|
{e6data_python_connector-3.0.2 → e6data_python_connector-3.0.3rc1}/test/test_multiprocessing_fix.py
RENAMED
|
File without changes
|
{e6data_python_connector-3.0.2 → e6data_python_connector-3.0.3rc1}/test/test_new_implementation.py
RENAMED
|
File without changes
|
{e6data_python_connector-3.0.2 → e6data_python_connector-3.0.3rc1}/test/test_oauth_authentication.py
RENAMED
|
File without changes
|
{e6data_python_connector-3.0.2 → e6data_python_connector-3.0.3rc1}/test/test_oauth_auto_resume.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{e6data_python_connector-3.0.2 → e6data_python_connector-3.0.3rc1}/test/test_specific_binary.py
RENAMED
|
File without changes
|
|
File without changes
|
{e6data_python_connector-3.0.2 → e6data_python_connector-3.0.3rc1}/test/test_strategy_logic.py
RENAMED
|
File without changes
|
|
File without changes
|
{e6data_python_connector-3.0.2 → e6data_python_connector-3.0.3rc1}/test/test_strategy_sharing_fix.py
RENAMED
|
File without changes
|
{e6data_python_connector-3.0.2 → e6data_python_connector-3.0.3rc1}/test/test_user_binary_value.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{e6data_python_connector-3.0.2 → e6data_python_connector-3.0.3rc1}/test/validate_decimal128.py
RENAMED
|
File without changes
|
{e6data_python_connector-3.0.2 → e6data_python_connector-3.0.3rc1}/test/validate_implementation.py
RENAMED
|
File without changes
|
{e6data_python_connector-3.0.2 → e6data_python_connector-3.0.3rc1}/test/verify_decimal_fix.py
RENAMED
|
File without changes
|