e6data-python-connector 2.3.12rc2__tar.gz → 2.3.12rc12__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-2.3.12rc2/e6data_python_connector.egg-info → e6data_python_connector-2.3.12rc12}/PKG-INFO +78 -2
- {e6data_python_connector-2.3.12rc2 → e6data_python_connector-2.3.12rc12}/README.md +77 -1
- {e6data_python_connector-2.3.12rc2 → e6data_python_connector-2.3.12rc12}/e6data_python_connector/cluster_manager.py +76 -2
- {e6data_python_connector-2.3.12rc2 → e6data_python_connector-2.3.12rc12}/e6data_python_connector/e6data_grpc.py +78 -17
- {e6data_python_connector-2.3.12rc2 → e6data_python_connector-2.3.12rc12/e6data_python_connector.egg-info}/PKG-INFO +78 -2
- {e6data_python_connector-2.3.12rc2 → e6data_python_connector-2.3.12rc12}/setup.py +1 -1
- {e6data_python_connector-2.3.12rc2 → e6data_python_connector-2.3.12rc12}/LICENSE +0 -0
- {e6data_python_connector-2.3.12rc2 → e6data_python_connector-2.3.12rc12}/MANIFEST.in +0 -0
- {e6data_python_connector-2.3.12rc2 → e6data_python_connector-2.3.12rc12}/e6data_python_connector/__init__.py +0 -0
- {e6data_python_connector-2.3.12rc2 → e6data_python_connector-2.3.12rc12}/e6data_python_connector/cluster_server/__init__.py +0 -0
- {e6data_python_connector-2.3.12rc2 → e6data_python_connector-2.3.12rc12}/e6data_python_connector/cluster_server/cluster_pb2.py +0 -0
- {e6data_python_connector-2.3.12rc2 → e6data_python_connector-2.3.12rc12}/e6data_python_connector/cluster_server/cluster_pb2_grpc.py +0 -0
- {e6data_python_connector-2.3.12rc2 → e6data_python_connector-2.3.12rc12}/e6data_python_connector/common.py +0 -0
- {e6data_python_connector-2.3.12rc2 → e6data_python_connector-2.3.12rc12}/e6data_python_connector/connection_pool.py +0 -0
- {e6data_python_connector-2.3.12rc2 → e6data_python_connector-2.3.12rc12}/e6data_python_connector/constants.py +0 -0
- {e6data_python_connector-2.3.12rc2 → e6data_python_connector-2.3.12rc12}/e6data_python_connector/datainputstream.py +0 -0
- {e6data_python_connector-2.3.12rc2 → e6data_python_connector-2.3.12rc12}/e6data_python_connector/date_time_utils.py +0 -0
- {e6data_python_connector-2.3.12rc2 → e6data_python_connector-2.3.12rc12}/e6data_python_connector/dialect.py +0 -0
- {e6data_python_connector-2.3.12rc2 → e6data_python_connector-2.3.12rc12}/e6data_python_connector/e6x_vector/__init__.py +0 -0
- {e6data_python_connector-2.3.12rc2 → e6data_python_connector-2.3.12rc12}/e6data_python_connector/e6x_vector/constants.py +0 -0
- {e6data_python_connector-2.3.12rc2 → e6data_python_connector-2.3.12rc12}/e6data_python_connector/e6x_vector/ttypes.py +0 -0
- {e6data_python_connector-2.3.12rc2 → e6data_python_connector-2.3.12rc12}/e6data_python_connector/exceptions.py +0 -0
- {e6data_python_connector-2.3.12rc2 → e6data_python_connector-2.3.12rc12}/e6data_python_connector/server/QueryEngineService.py +0 -0
- {e6data_python_connector-2.3.12rc2 → e6data_python_connector-2.3.12rc12}/e6data_python_connector/server/__init__.py +0 -0
- {e6data_python_connector-2.3.12rc2 → e6data_python_connector-2.3.12rc12}/e6data_python_connector/server/constants.py +0 -0
- {e6data_python_connector-2.3.12rc2 → e6data_python_connector-2.3.12rc12}/e6data_python_connector/server/e6x_engine_pb2.py +0 -0
- {e6data_python_connector-2.3.12rc2 → e6data_python_connector-2.3.12rc12}/e6data_python_connector/server/e6x_engine_pb2.pyi +0 -0
- {e6data_python_connector-2.3.12rc2 → e6data_python_connector-2.3.12rc12}/e6data_python_connector/server/e6x_engine_pb2_grpc.py +0 -0
- {e6data_python_connector-2.3.12rc2 → e6data_python_connector-2.3.12rc12}/e6data_python_connector/server/ttypes.py +0 -0
- {e6data_python_connector-2.3.12rc2 → e6data_python_connector-2.3.12rc12}/e6data_python_connector/strategy.py +0 -0
- {e6data_python_connector-2.3.12rc2 → e6data_python_connector-2.3.12rc12}/e6data_python_connector/typeId.py +0 -0
- {e6data_python_connector-2.3.12rc2 → e6data_python_connector-2.3.12rc12}/e6data_python_connector.egg-info/SOURCES.txt +0 -0
- {e6data_python_connector-2.3.12rc2 → e6data_python_connector-2.3.12rc12}/e6data_python_connector.egg-info/dependency_links.txt +0 -0
- {e6data_python_connector-2.3.12rc2 → e6data_python_connector-2.3.12rc12}/e6data_python_connector.egg-info/entry_points.txt +0 -0
- {e6data_python_connector-2.3.12rc2 → e6data_python_connector-2.3.12rc12}/e6data_python_connector.egg-info/requires.txt +0 -0
- {e6data_python_connector-2.3.12rc2 → e6data_python_connector-2.3.12rc12}/e6data_python_connector.egg-info/top_level.txt +0 -0
- {e6data_python_connector-2.3.12rc2 → e6data_python_connector-2.3.12rc12}/gen-py/__init__.py +0 -0
- {e6data_python_connector-2.3.12rc2 → e6data_python_connector-2.3.12rc12}/gen-py/e6x_vector/__init__.py +0 -0
- {e6data_python_connector-2.3.12rc2 → e6data_python_connector-2.3.12rc12}/gen-py/e6x_vector/constants.py +0 -0
- {e6data_python_connector-2.3.12rc2 → e6data_python_connector-2.3.12rc12}/gen-py/e6x_vector/ttypes.py +0 -0
- {e6data_python_connector-2.3.12rc2 → e6data_python_connector-2.3.12rc12}/pyproject.toml +0 -0
- {e6data_python_connector-2.3.12rc2 → e6data_python_connector-2.3.12rc12}/setup.cfg +0 -0
- {e6data_python_connector-2.3.12rc2 → e6data_python_connector-2.3.12rc12}/test/__init__.py +0 -0
- {e6data_python_connector-2.3.12rc2 → e6data_python_connector-2.3.12rc12}/test/analyze_38_nines.py +0 -0
- {e6data_python_connector-2.3.12rc2 → e6data_python_connector-2.3.12rc12}/test/analyze_all_cases.py +0 -0
- {e6data_python_connector-2.3.12rc2 → e6data_python_connector-2.3.12rc12}/test/analyze_binary.py +0 -0
- {e6data_python_connector-2.3.12rc2 → e6data_python_connector-2.3.12rc12}/test/analyze_correct_value.py +0 -0
- {e6data_python_connector-2.3.12rc2 → e6data_python_connector-2.3.12rc12}/test/analyze_fields.py +0 -0
- {e6data_python_connector-2.3.12rc2 → e6data_python_connector-2.3.12rc12}/test/check_decimal_errors.py +0 -0
- {e6data_python_connector-2.3.12rc2 → e6data_python_connector-2.3.12rc12}/test/cleanup_test_files.py +0 -0
- {e6data_python_connector-2.3.12rc2 → e6data_python_connector-2.3.12rc12}/test/debug_38_nines.py +0 -0
- {e6data_python_connector-2.3.12rc2 → e6data_python_connector-2.3.12rc12}/test/debug_binary.py +0 -0
- {e6data_python_connector-2.3.12rc2 → e6data_python_connector-2.3.12rc12}/test/final_test.py +0 -0
- {e6data_python_connector-2.3.12rc2 → e6data_python_connector-2.3.12rc12}/test/move_tests.py +0 -0
- {e6data_python_connector-2.3.12rc2 → e6data_python_connector-2.3.12rc12}/test/quick_test.py +0 -0
- {e6data_python_connector-2.3.12rc2 → e6data_python_connector-2.3.12rc12}/test/test_38_nines.py +0 -0
- {e6data_python_connector-2.3.12rc2 → e6data_python_connector-2.3.12rc12}/test/test_all_decimal128_cases.py +0 -0
- {e6data_python_connector-2.3.12rc2 → e6data_python_connector-2.3.12rc12}/test/test_cluster_manager_efficiency.py +0 -0
- {e6data_python_connector-2.3.12rc2 → e6data_python_connector-2.3.12rc12}/test/test_cluster_manager_none_strategy.py +0 -0
- {e6data_python_connector-2.3.12rc2 → e6data_python_connector-2.3.12rc12}/test/test_cluster_manager_strategy.py +0 -0
- {e6data_python_connector-2.3.12rc2 → e6data_python_connector-2.3.12rc12}/test/test_comprehensive.py +0 -0
- {e6data_python_connector-2.3.12rc2 → e6data_python_connector-2.3.12rc12}/test/test_connection_pool.py +0 -0
- {e6data_python_connector-2.3.12rc2 → e6data_python_connector-2.3.12rc12}/test/test_connection_pool_e2e.py +0 -0
- {e6data_python_connector-2.3.12rc2 → e6data_python_connector-2.3.12rc12}/test/test_current_implementation.py +0 -0
- {e6data_python_connector-2.3.12rc2 → e6data_python_connector-2.3.12rc12}/test/test_decimal128_binary_parsing.py +0 -0
- {e6data_python_connector-2.3.12rc2 → e6data_python_connector-2.3.12rc12}/test/test_decimal128_parsing.py +0 -0
- {e6data_python_connector-2.3.12rc2 → e6data_python_connector-2.3.12rc12}/test/test_fix.py +0 -0
- {e6data_python_connector-2.3.12rc2 → e6data_python_connector-2.3.12rc12}/test/test_improved_parsing.py +0 -0
- {e6data_python_connector-2.3.12rc2 → e6data_python_connector-2.3.12rc12}/test/test_known_case.py +0 -0
- {e6data_python_connector-2.3.12rc2 → e6data_python_connector-2.3.12rc12}/test/test_manual_analysis.py +0 -0
- {e6data_python_connector-2.3.12rc2 → e6data_python_connector-2.3.12rc12}/test/test_mock_server.py +0 -0
- {e6data_python_connector-2.3.12rc2 → e6data_python_connector-2.3.12rc12}/test/test_multiprocessing_fix.py +0 -0
- {e6data_python_connector-2.3.12rc2 → e6data_python_connector-2.3.12rc12}/test/test_new_implementation.py +0 -0
- {e6data_python_connector-2.3.12rc2 → e6data_python_connector-2.3.12rc12}/test/test_pool_concurrency_simple.py +0 -0
- {e6data_python_connector-2.3.12rc2 → e6data_python_connector-2.3.12rc12}/test/test_pool_threading_multiprocessing.py +0 -0
- {e6data_python_connector-2.3.12rc2 → e6data_python_connector-2.3.12rc12}/test/test_simple_connection_pool.py +0 -0
- {e6data_python_connector-2.3.12rc2 → e6data_python_connector-2.3.12rc12}/test/test_specific_binary.py +0 -0
- {e6data_python_connector-2.3.12rc2 → e6data_python_connector-2.3.12rc12}/test/test_strategy.py +0 -0
- {e6data_python_connector-2.3.12rc2 → e6data_python_connector-2.3.12rc12}/test/test_strategy_logic.py +0 -0
- {e6data_python_connector-2.3.12rc2 → e6data_python_connector-2.3.12rc12}/test/test_strategy_persistence_fix.py +0 -0
- {e6data_python_connector-2.3.12rc2 → e6data_python_connector-2.3.12rc12}/test/test_strategy_sharing_fix.py +0 -0
- {e6data_python_connector-2.3.12rc2 → e6data_python_connector-2.3.12rc12}/test/test_user_binary_value.py +0 -0
- {e6data_python_connector-2.3.12rc2 → e6data_python_connector-2.3.12rc12}/test/tests.py +0 -0
- {e6data_python_connector-2.3.12rc2 → e6data_python_connector-2.3.12rc12}/test/tests_grpc.py +0 -0
- {e6data_python_connector-2.3.12rc2 → e6data_python_connector-2.3.12rc12}/test/validate_decimal128.py +0 -0
- {e6data_python_connector-2.3.12rc2 → e6data_python_connector-2.3.12rc12}/test/validate_implementation.py +0 -0
- {e6data_python_connector-2.3.12rc2 → e6data_python_connector-2.3.12rc12}/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.3.
|
|
3
|
+
Version: 2.3.12rc12
|
|
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.
|
|
@@ -39,7 +39,7 @@ Dynamic: summary
|
|
|
39
39
|
|
|
40
40
|
# e6data Python Connector
|
|
41
41
|
|
|
42
|
-

|
|
43
43
|
|
|
44
44
|
## Introduction
|
|
45
45
|
|
|
@@ -125,6 +125,7 @@ The `Connection` class supports the following parameters:
|
|
|
125
125
|
| `auto_resume` | bool | No | True | Automatically resume cluster if suspended |
|
|
126
126
|
| `grpc_options` | dict | No | None | Additional gRPC configuration options |
|
|
127
127
|
| `debug` | bool | No | False | Enable debug logging for troubleshooting |
|
|
128
|
+
| `ssl_cert` | str/bytes | No | None | Path to CA certificate (PEM) or certificate bytes for HTTPS connections |
|
|
128
129
|
|
|
129
130
|
#### Secure Connection Example
|
|
130
131
|
|
|
@@ -142,6 +143,52 @@ conn = Connection(
|
|
|
142
143
|
)
|
|
143
144
|
```
|
|
144
145
|
|
|
146
|
+
#### HTTPS Connection with HAProxy
|
|
147
|
+
|
|
148
|
+
When connecting through HAProxy with HTTPS/TLS and custom CA certificates:
|
|
149
|
+
|
|
150
|
+
```python
|
|
151
|
+
from e6data_python_connector import Connection
|
|
152
|
+
|
|
153
|
+
# Option 1: Using CA certificate file path
|
|
154
|
+
conn = Connection(
|
|
155
|
+
host=host,
|
|
156
|
+
port=port,
|
|
157
|
+
username=username,
|
|
158
|
+
password=password,
|
|
159
|
+
database=database,
|
|
160
|
+
cluster_name='analytics-cluster-01', # Specify cluster name
|
|
161
|
+
secure=True,
|
|
162
|
+
ssl_cert='/path/to/ca-cert.pem' # Path to CA certificate
|
|
163
|
+
)
|
|
164
|
+
|
|
165
|
+
# Option 2: Reading certificate content as bytes
|
|
166
|
+
with open('/path/to/ca-cert.pem', 'rb') as f:
|
|
167
|
+
ca_cert = f.read()
|
|
168
|
+
|
|
169
|
+
conn = Connection(
|
|
170
|
+
host=host,
|
|
171
|
+
port=port,
|
|
172
|
+
username=username,
|
|
173
|
+
password=password,
|
|
174
|
+
database=database,
|
|
175
|
+
secure=True,
|
|
176
|
+
ssl_cert=ca_cert # Certificate as bytes
|
|
177
|
+
)
|
|
178
|
+
|
|
179
|
+
# Option 3: System CA bundle (for publicly signed certificates)
|
|
180
|
+
conn = Connection(
|
|
181
|
+
host=host,
|
|
182
|
+
port=port,
|
|
183
|
+
username=username,
|
|
184
|
+
password=password,
|
|
185
|
+
database=database,
|
|
186
|
+
secure=True # Uses system CA bundle
|
|
187
|
+
)
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
**Note:** Certificate verification is always enabled for security. For self-signed certificates, provide the CA certificate via the `ssl_cert` parameter.
|
|
191
|
+
|
|
145
192
|
#### Cluster-Specific Connection
|
|
146
193
|
|
|
147
194
|
When working with multiple clusters, specify the cluster name:
|
|
@@ -504,6 +551,35 @@ pool.return_connection(conn)
|
|
|
504
551
|
pool.close_all()
|
|
505
552
|
```
|
|
506
553
|
|
|
554
|
+
##### Connection Pool with HTTPS (HAProxy)
|
|
555
|
+
|
|
556
|
+
```python
|
|
557
|
+
from e6data_python_connector import ConnectionPool
|
|
558
|
+
|
|
559
|
+
# Connection pool with HTTPS and custom CA certificate
|
|
560
|
+
pool = ConnectionPool(
|
|
561
|
+
min_size=2,
|
|
562
|
+
max_size=10,
|
|
563
|
+
host=host,
|
|
564
|
+
port=port,
|
|
565
|
+
username=username,
|
|
566
|
+
password=password,
|
|
567
|
+
database=database,
|
|
568
|
+
secure=True,
|
|
569
|
+
ssl_cert='/path/to/ca-cert.pem' # CA certificate for HTTPS
|
|
570
|
+
)
|
|
571
|
+
|
|
572
|
+
# Use the pool with context manager for automatic cleanup
|
|
573
|
+
with pool.get_connection_context() as conn:
|
|
574
|
+
cursor = conn.cursor()
|
|
575
|
+
cursor.execute("SELECT * FROM table")
|
|
576
|
+
results = cursor.fetchall()
|
|
577
|
+
print(f"Retrieved {len(results)} rows")
|
|
578
|
+
|
|
579
|
+
# Clean up pool when done
|
|
580
|
+
pool.close_all()
|
|
581
|
+
```
|
|
582
|
+
|
|
507
583
|
##### Using Context Manager (Recommended)
|
|
508
584
|
|
|
509
585
|
The context manager pattern ensures connections are automatically returned to the pool:
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# e6data Python Connector
|
|
2
2
|
|
|
3
|
-

|
|
4
4
|
|
|
5
5
|
## Introduction
|
|
6
6
|
|
|
@@ -86,6 +86,7 @@ The `Connection` class supports the following parameters:
|
|
|
86
86
|
| `auto_resume` | bool | No | True | Automatically resume cluster if suspended |
|
|
87
87
|
| `grpc_options` | dict | No | None | Additional gRPC configuration options |
|
|
88
88
|
| `debug` | bool | No | False | Enable debug logging for troubleshooting |
|
|
89
|
+
| `ssl_cert` | str/bytes | No | None | Path to CA certificate (PEM) or certificate bytes for HTTPS connections |
|
|
89
90
|
|
|
90
91
|
#### Secure Connection Example
|
|
91
92
|
|
|
@@ -103,6 +104,52 @@ conn = Connection(
|
|
|
103
104
|
)
|
|
104
105
|
```
|
|
105
106
|
|
|
107
|
+
#### HTTPS Connection with HAProxy
|
|
108
|
+
|
|
109
|
+
When connecting through HAProxy with HTTPS/TLS and custom CA certificates:
|
|
110
|
+
|
|
111
|
+
```python
|
|
112
|
+
from e6data_python_connector import Connection
|
|
113
|
+
|
|
114
|
+
# Option 1: Using CA certificate file path
|
|
115
|
+
conn = Connection(
|
|
116
|
+
host=host,
|
|
117
|
+
port=port,
|
|
118
|
+
username=username,
|
|
119
|
+
password=password,
|
|
120
|
+
database=database,
|
|
121
|
+
cluster_name='analytics-cluster-01', # Specify cluster name
|
|
122
|
+
secure=True,
|
|
123
|
+
ssl_cert='/path/to/ca-cert.pem' # Path to CA certificate
|
|
124
|
+
)
|
|
125
|
+
|
|
126
|
+
# Option 2: Reading certificate content as bytes
|
|
127
|
+
with open('/path/to/ca-cert.pem', 'rb') as f:
|
|
128
|
+
ca_cert = f.read()
|
|
129
|
+
|
|
130
|
+
conn = Connection(
|
|
131
|
+
host=host,
|
|
132
|
+
port=port,
|
|
133
|
+
username=username,
|
|
134
|
+
password=password,
|
|
135
|
+
database=database,
|
|
136
|
+
secure=True,
|
|
137
|
+
ssl_cert=ca_cert # Certificate as bytes
|
|
138
|
+
)
|
|
139
|
+
|
|
140
|
+
# Option 3: System CA bundle (for publicly signed certificates)
|
|
141
|
+
conn = Connection(
|
|
142
|
+
host=host,
|
|
143
|
+
port=port,
|
|
144
|
+
username=username,
|
|
145
|
+
password=password,
|
|
146
|
+
database=database,
|
|
147
|
+
secure=True # Uses system CA bundle
|
|
148
|
+
)
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
**Note:** Certificate verification is always enabled for security. For self-signed certificates, provide the CA certificate via the `ssl_cert` parameter.
|
|
152
|
+
|
|
106
153
|
#### Cluster-Specific Connection
|
|
107
154
|
|
|
108
155
|
When working with multiple clusters, specify the cluster name:
|
|
@@ -465,6 +512,35 @@ pool.return_connection(conn)
|
|
|
465
512
|
pool.close_all()
|
|
466
513
|
```
|
|
467
514
|
|
|
515
|
+
##### Connection Pool with HTTPS (HAProxy)
|
|
516
|
+
|
|
517
|
+
```python
|
|
518
|
+
from e6data_python_connector import ConnectionPool
|
|
519
|
+
|
|
520
|
+
# Connection pool with HTTPS and custom CA certificate
|
|
521
|
+
pool = ConnectionPool(
|
|
522
|
+
min_size=2,
|
|
523
|
+
max_size=10,
|
|
524
|
+
host=host,
|
|
525
|
+
port=port,
|
|
526
|
+
username=username,
|
|
527
|
+
password=password,
|
|
528
|
+
database=database,
|
|
529
|
+
secure=True,
|
|
530
|
+
ssl_cert='/path/to/ca-cert.pem' # CA certificate for HTTPS
|
|
531
|
+
)
|
|
532
|
+
|
|
533
|
+
# Use the pool with context manager for automatic cleanup
|
|
534
|
+
with pool.get_connection_context() as conn:
|
|
535
|
+
cursor = conn.cursor()
|
|
536
|
+
cursor.execute("SELECT * FROM table")
|
|
537
|
+
results = cursor.fetchall()
|
|
538
|
+
print(f"Retrieved {len(results)} rows")
|
|
539
|
+
|
|
540
|
+
# Clean up pool when done
|
|
541
|
+
pool.close_all()
|
|
542
|
+
```
|
|
543
|
+
|
|
468
544
|
##### Using Context Manager (Recommended)
|
|
469
545
|
|
|
470
546
|
The context manager pattern ensures connections are automatically returned to the pool:
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import logging
|
|
2
2
|
import threading
|
|
3
3
|
import time
|
|
4
|
+
from typing import Optional, Union
|
|
4
5
|
import e6data_python_connector.cluster_server.cluster_pb2 as cluster_pb2
|
|
5
6
|
import e6data_python_connector.cluster_server.cluster_pb2_grpc as cluster_pb2_grpc
|
|
6
7
|
import grpc
|
|
@@ -140,7 +141,7 @@ class ClusterManager:
|
|
|
140
141
|
"""
|
|
141
142
|
|
|
142
143
|
def __init__(self, host: str, port: int, user: str, password: str, secure_channel: bool = False, timeout=60 * 5,
|
|
143
|
-
cluster_uuid=None, grpc_options=None, debug=False):
|
|
144
|
+
cluster_uuid=None, grpc_options=None, debug=False, ssl_cert: Optional[Union[str, bytes]] = None):
|
|
144
145
|
"""
|
|
145
146
|
Initializes a new instance of the ClusterManager class.
|
|
146
147
|
|
|
@@ -155,7 +156,12 @@ class ClusterManager:
|
|
|
155
156
|
defaults to 5 minutes.
|
|
156
157
|
cluster_uuid (str, optional): The unique identifier for the target cluster;
|
|
157
158
|
defaults to None.
|
|
159
|
+
grpc_options (dict, optional): Additional gRPC configuration options;
|
|
160
|
+
defaults to None.
|
|
158
161
|
debug (bool, optional): Enable debug logging; defaults to False.
|
|
162
|
+
ssl_cert (str or bytes, optional): Path to CA certificate file (PEM format)
|
|
163
|
+
or certificate content as bytes. Used when secure_channel=True.
|
|
164
|
+
If not provided and secure_channel=True, system default CA bundle will be used.
|
|
159
165
|
"""
|
|
160
166
|
|
|
161
167
|
self._host = host
|
|
@@ -164,12 +170,67 @@ class ClusterManager:
|
|
|
164
170
|
self._password = password
|
|
165
171
|
self._timeout = time.time() + timeout
|
|
166
172
|
self._secure_channel = secure_channel
|
|
173
|
+
self._ssl_cert = ssl_cert
|
|
167
174
|
self.cluster_uuid = cluster_uuid
|
|
168
175
|
self._grpc_options = grpc_options
|
|
169
176
|
if grpc_options is None:
|
|
170
177
|
self._grpc_options = dict()
|
|
171
178
|
self._debug = debug
|
|
172
179
|
|
|
180
|
+
def _get_ssl_credentials(self):
|
|
181
|
+
"""
|
|
182
|
+
Creates SSL/TLS credentials for secure gRPC connections.
|
|
183
|
+
|
|
184
|
+
This method handles loading CA certificates from various sources:
|
|
185
|
+
- File path (str): Reads the certificate from the specified file
|
|
186
|
+
- Bytes: Uses the certificate content directly
|
|
187
|
+
- None: Uses system default CA bundle
|
|
188
|
+
|
|
189
|
+
Returns:
|
|
190
|
+
grpc.ChannelCredentials: SSL credentials for secure channel creation
|
|
191
|
+
|
|
192
|
+
Raises:
|
|
193
|
+
FileNotFoundError: If ssl_cert is a file path and the file doesn't exist
|
|
194
|
+
ValueError: If ssl_cert format is invalid or file cannot be read
|
|
195
|
+
"""
|
|
196
|
+
root_certificates = None
|
|
197
|
+
|
|
198
|
+
if self._ssl_cert:
|
|
199
|
+
# Custom CA certificate provided
|
|
200
|
+
if isinstance(self._ssl_cert, str):
|
|
201
|
+
# It's a file path - read the certificate
|
|
202
|
+
try:
|
|
203
|
+
with open(self._ssl_cert, 'rb') as f:
|
|
204
|
+
root_certificates = f.read()
|
|
205
|
+
if self._debug:
|
|
206
|
+
logger.info(f"ClusterManager: Loaded CA certificate from file: {self._ssl_cert}")
|
|
207
|
+
except FileNotFoundError:
|
|
208
|
+
raise FileNotFoundError(
|
|
209
|
+
f"CA certificate file not found: {self._ssl_cert}. "
|
|
210
|
+
f"Please ensure the file exists and the path is correct."
|
|
211
|
+
)
|
|
212
|
+
except Exception as e:
|
|
213
|
+
raise ValueError(
|
|
214
|
+
f"Failed to read CA certificate from {self._ssl_cert}: {str(e)}"
|
|
215
|
+
)
|
|
216
|
+
elif isinstance(self._ssl_cert, bytes):
|
|
217
|
+
# Certificate content provided as bytes
|
|
218
|
+
root_certificates = self._ssl_cert
|
|
219
|
+
if self._debug:
|
|
220
|
+
logger.info("ClusterManager: Using CA certificate provided as bytes")
|
|
221
|
+
else:
|
|
222
|
+
raise ValueError(
|
|
223
|
+
f"ssl_cert must be either a file path (str) or certificate content (bytes), "
|
|
224
|
+
f"got {type(self._ssl_cert)}"
|
|
225
|
+
)
|
|
226
|
+
else:
|
|
227
|
+
# No custom CA certificate - use system default CA bundle
|
|
228
|
+
if self._debug:
|
|
229
|
+
logger.info("ClusterManager: Using system default CA bundle for SSL/TLS")
|
|
230
|
+
|
|
231
|
+
# Create and return SSL credentials with certificate verification enabled
|
|
232
|
+
return grpc.ssl_channel_credentials(root_certificates=root_certificates)
|
|
233
|
+
|
|
173
234
|
@property
|
|
174
235
|
def _get_connection(self):
|
|
175
236
|
"""
|
|
@@ -181,16 +242,29 @@ class ClusterManager:
|
|
|
181
242
|
"""
|
|
182
243
|
|
|
183
244
|
if self._secure_channel:
|
|
245
|
+
# Get SSL credentials (handles CA cert loading and validation)
|
|
246
|
+
credentials = self._get_ssl_credentials()
|
|
247
|
+
|
|
184
248
|
self._channel = grpc.secure_channel(
|
|
185
249
|
target='{}:{}'.format(self._host, self._port),
|
|
186
250
|
options=self._grpc_options,
|
|
187
|
-
credentials=
|
|
251
|
+
credentials=credentials
|
|
188
252
|
)
|
|
253
|
+
|
|
254
|
+
if self._debug:
|
|
255
|
+
logger.info(
|
|
256
|
+
f"ClusterManager: Created secure gRPC channel to {self._host}:{self._port} "
|
|
257
|
+
f"with SSL/TLS certificate verification enabled"
|
|
258
|
+
)
|
|
189
259
|
else:
|
|
190
260
|
self._channel = grpc.insecure_channel(
|
|
191
261
|
target='{}:{}'.format(self._host, self._port),
|
|
192
262
|
options=self._grpc_options
|
|
193
263
|
)
|
|
264
|
+
|
|
265
|
+
if self._debug:
|
|
266
|
+
logger.info(f"ClusterManager: Created insecure gRPC channel to {self._host}:{self._port}")
|
|
267
|
+
|
|
194
268
|
return cluster_pb2_grpc.ClusterServiceStub(self._channel)
|
|
195
269
|
|
|
196
270
|
def _try_cluster_request(self, request_type, payload=None):
|
|
@@ -16,6 +16,7 @@ import time
|
|
|
16
16
|
from decimal import Decimal
|
|
17
17
|
from io import BytesIO
|
|
18
18
|
from ssl import CERT_NONE, CERT_OPTIONAL, CERT_REQUIRED
|
|
19
|
+
from typing import Union, Optional
|
|
19
20
|
import threading
|
|
20
21
|
import multiprocessing
|
|
21
22
|
|
|
@@ -335,6 +336,7 @@ class Connection(object):
|
|
|
335
336
|
scheme: str = 'e6data',
|
|
336
337
|
grpc_options: dict = None,
|
|
337
338
|
debug: bool = False,
|
|
339
|
+
ssl_cert: Optional[Union[str, bytes]] = None,
|
|
338
340
|
):
|
|
339
341
|
"""
|
|
340
342
|
Parameters
|
|
@@ -368,6 +370,14 @@ class Connection(object):
|
|
|
368
370
|
- keepalive_time_ms: This parameter defines the time, in milliseconds, Default to 30 seconds
|
|
369
371
|
debug: bool, Optional
|
|
370
372
|
Flag to enable debug logging for blue-green deployment strategy changes
|
|
373
|
+
ssl_cert: str or bytes, Optional
|
|
374
|
+
Path to CA certificate file (PEM format) or certificate content as bytes.
|
|
375
|
+
Used when connecting via HTTPS .
|
|
376
|
+
If not provided and secure=True, system default CA bundle will be used.
|
|
377
|
+
For self-signed certificates, provide the CA certificate via this parameter.
|
|
378
|
+
Examples:
|
|
379
|
+
- File path: ssl_cert='/path/to/ca-cert.pem'
|
|
380
|
+
- Bytes: ssl_cert=open('/path/to/ca-cert.pem', 'rb').read()
|
|
371
381
|
"""
|
|
372
382
|
if not username or not password:
|
|
373
383
|
raise ValueError("username or password cannot be empty.")
|
|
@@ -382,13 +392,13 @@ class Connection(object):
|
|
|
382
392
|
self._port = port
|
|
383
393
|
|
|
384
394
|
self._secure_channel = secure
|
|
395
|
+
self._ssl_cert = ssl_cert
|
|
385
396
|
|
|
386
397
|
self.catalog_name = catalog
|
|
387
398
|
|
|
388
399
|
self._auto_resume = auto_resume
|
|
389
400
|
|
|
390
401
|
self._grpc_options = grpc_options
|
|
391
|
-
|
|
392
402
|
if self._grpc_options is None:
|
|
393
403
|
self._grpc_options = dict()
|
|
394
404
|
self.grpc_prepare_timeout = self._grpc_options.get('grpc_prepare_timeout') or 10 * 60 # 10 minutes
|
|
@@ -398,15 +408,7 @@ class Connection(object):
|
|
|
398
408
|
The default maximum time on client side to wait for the cluster to resume is 5 minutes.
|
|
399
409
|
"""
|
|
400
410
|
self.grpc_auto_resume_timeout_seconds = self._grpc_options.pop('grpc_auto_resume_timeout_seconds')
|
|
401
|
-
|
|
402
|
-
self.timeout = 500
|
|
403
|
-
if 'timeout' in self._grpc_options:
|
|
404
|
-
"""
|
|
405
|
-
The default maximum time on client side to wait for the cluster to resume is 5 minutes.
|
|
406
|
-
"""
|
|
407
|
-
self.timeout = self._grpc_options.pop('timeout')
|
|
408
|
-
|
|
409
|
-
|
|
411
|
+
|
|
410
412
|
# Store debug flag and register with debug connections
|
|
411
413
|
self._debug = debug
|
|
412
414
|
if self._debug:
|
|
@@ -492,6 +494,60 @@ class Connection(object):
|
|
|
492
494
|
|
|
493
495
|
return self._cached_grpc_options
|
|
494
496
|
|
|
497
|
+
def _get_ssl_credentials(self):
|
|
498
|
+
"""
|
|
499
|
+
Creates SSL/TLS credentials for secure gRPC connections.
|
|
500
|
+
|
|
501
|
+
This method handles loading CA certificates from various sources:
|
|
502
|
+
- File path (str): Reads the certificate from the specified file
|
|
503
|
+
- Bytes: Uses the certificate content directly
|
|
504
|
+
- None: Uses system default CA bundle
|
|
505
|
+
|
|
506
|
+
Returns:
|
|
507
|
+
grpc.ChannelCredentials: SSL credentials for secure channel creation
|
|
508
|
+
|
|
509
|
+
Raises:
|
|
510
|
+
FileNotFoundError: If ssl_cert is a file path and the file doesn't exist
|
|
511
|
+
ValueError: If ssl_cert format is invalid or file cannot be read
|
|
512
|
+
"""
|
|
513
|
+
root_certificates = None
|
|
514
|
+
|
|
515
|
+
if self._ssl_cert:
|
|
516
|
+
# Custom CA certificate provided
|
|
517
|
+
if isinstance(self._ssl_cert, str):
|
|
518
|
+
# It's a file path - read the certificate
|
|
519
|
+
try:
|
|
520
|
+
with open(self._ssl_cert, 'rb') as f:
|
|
521
|
+
root_certificates = f.read()
|
|
522
|
+
if self._debug:
|
|
523
|
+
logger.info(f"Loaded CA certificate from file: {self._ssl_cert}")
|
|
524
|
+
except FileNotFoundError:
|
|
525
|
+
raise FileNotFoundError(
|
|
526
|
+
f"CA certificate file not found: {self._ssl_cert}. "
|
|
527
|
+
f"Please ensure the file exists and the path is correct."
|
|
528
|
+
)
|
|
529
|
+
except Exception as e:
|
|
530
|
+
raise ValueError(
|
|
531
|
+
f"Failed to read CA certificate from {self._ssl_cert}: {str(e)}"
|
|
532
|
+
)
|
|
533
|
+
elif isinstance(self._ssl_cert, bytes):
|
|
534
|
+
# Certificate content provided as bytes
|
|
535
|
+
root_certificates = self._ssl_cert
|
|
536
|
+
if self._debug:
|
|
537
|
+
logger.info("Using CA certificate provided as bytes")
|
|
538
|
+
else:
|
|
539
|
+
raise ValueError(
|
|
540
|
+
f"ssl_cert must be either a file path (str) or certificate content (bytes), "
|
|
541
|
+
f"got {type(self._ssl_cert)}"
|
|
542
|
+
)
|
|
543
|
+
else:
|
|
544
|
+
# No custom CA certificate - use system default CA bundle
|
|
545
|
+
if self._debug:
|
|
546
|
+
logger.info("Using system default CA bundle for SSL/TLS")
|
|
547
|
+
|
|
548
|
+
# Create and return SSL credentials with certificate verification enabled
|
|
549
|
+
return grpc.ssl_channel_credentials(root_certificates=root_certificates)
|
|
550
|
+
|
|
495
551
|
def _create_client(self):
|
|
496
552
|
"""
|
|
497
553
|
Creates a gRPC client for the connection.
|
|
@@ -506,13 +562,19 @@ class Connection(object):
|
|
|
506
562
|
|
|
507
563
|
Raises:
|
|
508
564
|
grpc.RpcError: If there is an error in creating the gRPC channel or client stub.
|
|
565
|
+
FileNotFoundError: If ssl_cert is a file path and the file doesn't exist.
|
|
566
|
+
ValueError: If ssl_cert format is invalid.
|
|
509
567
|
"""
|
|
510
568
|
|
|
511
569
|
if self._secure_channel:
|
|
570
|
+
# Get SSL credentials (handles CA cert loading and validation)
|
|
571
|
+
credentials = self._get_ssl_credentials()
|
|
572
|
+
|
|
573
|
+
# Create secure channel
|
|
512
574
|
self._channel = grpc.secure_channel(
|
|
513
575
|
target='{}:{}'.format(self._host, self._port),
|
|
514
576
|
options=self._get_grpc_options,
|
|
515
|
-
credentials=
|
|
577
|
+
credentials=credentials
|
|
516
578
|
)
|
|
517
579
|
else:
|
|
518
580
|
self._channel = grpc.insecure_channel(
|
|
@@ -694,7 +756,8 @@ class Connection(object):
|
|
|
694
756
|
secure_channel=self._secure_channel,
|
|
695
757
|
cluster_uuid=self.cluster_name,
|
|
696
758
|
timeout=self.grpc_auto_resume_timeout_seconds,
|
|
697
|
-
debug=self._debug
|
|
759
|
+
debug=self._debug,
|
|
760
|
+
ssl_cert=self._ssl_cert
|
|
698
761
|
).resume()
|
|
699
762
|
return status # Return boolean status directly
|
|
700
763
|
else:
|
|
@@ -1343,8 +1406,7 @@ class Cursor(DBAPICursor):
|
|
|
1343
1406
|
client = self.connection.client
|
|
1344
1407
|
execute_response = client.executeStatementV2(
|
|
1345
1408
|
execute_statement_request,
|
|
1346
|
-
metadata=self.metadata
|
|
1347
|
-
timeout=self.connection.timeout
|
|
1409
|
+
metadata=self.metadata
|
|
1348
1410
|
)
|
|
1349
1411
|
|
|
1350
1412
|
# Check for new strategy in execute response
|
|
@@ -1453,14 +1515,13 @@ class Cursor(DBAPICursor):
|
|
|
1453
1515
|
get_next_result_batch_request = e6x_engine_pb2.GetNextResultBatchRequest(
|
|
1454
1516
|
engineIP=self._engine_ip,
|
|
1455
1517
|
sessionId=self.connection.get_session_id,
|
|
1456
|
-
queryId=self._query_id
|
|
1518
|
+
queryId=self._query_id
|
|
1457
1519
|
)
|
|
1458
1520
|
# Get fresh client after session access (may have been invalidated)
|
|
1459
1521
|
client = self.connection.client
|
|
1460
1522
|
get_next_result_batch_response = client.getNextResultBatch(
|
|
1461
1523
|
get_next_result_batch_request,
|
|
1462
|
-
metadata=self.metadata
|
|
1463
|
-
timeout=self.connection.timeout
|
|
1524
|
+
metadata=self.metadata
|
|
1464
1525
|
)
|
|
1465
1526
|
|
|
1466
1527
|
# Check for new strategy in batch response
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.2
|
|
2
2
|
Name: e6data-python-connector
|
|
3
|
-
Version: 2.3.
|
|
3
|
+
Version: 2.3.12rc12
|
|
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.
|
|
@@ -39,7 +39,7 @@ Dynamic: summary
|
|
|
39
39
|
|
|
40
40
|
# e6data Python Connector
|
|
41
41
|
|
|
42
|
-

|
|
43
43
|
|
|
44
44
|
## Introduction
|
|
45
45
|
|
|
@@ -125,6 +125,7 @@ The `Connection` class supports the following parameters:
|
|
|
125
125
|
| `auto_resume` | bool | No | True | Automatically resume cluster if suspended |
|
|
126
126
|
| `grpc_options` | dict | No | None | Additional gRPC configuration options |
|
|
127
127
|
| `debug` | bool | No | False | Enable debug logging for troubleshooting |
|
|
128
|
+
| `ssl_cert` | str/bytes | No | None | Path to CA certificate (PEM) or certificate bytes for HTTPS connections |
|
|
128
129
|
|
|
129
130
|
#### Secure Connection Example
|
|
130
131
|
|
|
@@ -142,6 +143,52 @@ conn = Connection(
|
|
|
142
143
|
)
|
|
143
144
|
```
|
|
144
145
|
|
|
146
|
+
#### HTTPS Connection with HAProxy
|
|
147
|
+
|
|
148
|
+
When connecting through HAProxy with HTTPS/TLS and custom CA certificates:
|
|
149
|
+
|
|
150
|
+
```python
|
|
151
|
+
from e6data_python_connector import Connection
|
|
152
|
+
|
|
153
|
+
# Option 1: Using CA certificate file path
|
|
154
|
+
conn = Connection(
|
|
155
|
+
host=host,
|
|
156
|
+
port=port,
|
|
157
|
+
username=username,
|
|
158
|
+
password=password,
|
|
159
|
+
database=database,
|
|
160
|
+
cluster_name='analytics-cluster-01', # Specify cluster name
|
|
161
|
+
secure=True,
|
|
162
|
+
ssl_cert='/path/to/ca-cert.pem' # Path to CA certificate
|
|
163
|
+
)
|
|
164
|
+
|
|
165
|
+
# Option 2: Reading certificate content as bytes
|
|
166
|
+
with open('/path/to/ca-cert.pem', 'rb') as f:
|
|
167
|
+
ca_cert = f.read()
|
|
168
|
+
|
|
169
|
+
conn = Connection(
|
|
170
|
+
host=host,
|
|
171
|
+
port=port,
|
|
172
|
+
username=username,
|
|
173
|
+
password=password,
|
|
174
|
+
database=database,
|
|
175
|
+
secure=True,
|
|
176
|
+
ssl_cert=ca_cert # Certificate as bytes
|
|
177
|
+
)
|
|
178
|
+
|
|
179
|
+
# Option 3: System CA bundle (for publicly signed certificates)
|
|
180
|
+
conn = Connection(
|
|
181
|
+
host=host,
|
|
182
|
+
port=port,
|
|
183
|
+
username=username,
|
|
184
|
+
password=password,
|
|
185
|
+
database=database,
|
|
186
|
+
secure=True # Uses system CA bundle
|
|
187
|
+
)
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
**Note:** Certificate verification is always enabled for security. For self-signed certificates, provide the CA certificate via the `ssl_cert` parameter.
|
|
191
|
+
|
|
145
192
|
#### Cluster-Specific Connection
|
|
146
193
|
|
|
147
194
|
When working with multiple clusters, specify the cluster name:
|
|
@@ -504,6 +551,35 @@ pool.return_connection(conn)
|
|
|
504
551
|
pool.close_all()
|
|
505
552
|
```
|
|
506
553
|
|
|
554
|
+
##### Connection Pool with HTTPS (HAProxy)
|
|
555
|
+
|
|
556
|
+
```python
|
|
557
|
+
from e6data_python_connector import ConnectionPool
|
|
558
|
+
|
|
559
|
+
# Connection pool with HTTPS and custom CA certificate
|
|
560
|
+
pool = ConnectionPool(
|
|
561
|
+
min_size=2,
|
|
562
|
+
max_size=10,
|
|
563
|
+
host=host,
|
|
564
|
+
port=port,
|
|
565
|
+
username=username,
|
|
566
|
+
password=password,
|
|
567
|
+
database=database,
|
|
568
|
+
secure=True,
|
|
569
|
+
ssl_cert='/path/to/ca-cert.pem' # CA certificate for HTTPS
|
|
570
|
+
)
|
|
571
|
+
|
|
572
|
+
# Use the pool with context manager for automatic cleanup
|
|
573
|
+
with pool.get_connection_context() as conn:
|
|
574
|
+
cursor = conn.cursor()
|
|
575
|
+
cursor.execute("SELECT * FROM table")
|
|
576
|
+
results = cursor.fetchall()
|
|
577
|
+
print(f"Retrieved {len(results)} rows")
|
|
578
|
+
|
|
579
|
+
# Clean up pool when done
|
|
580
|
+
pool.close_all()
|
|
581
|
+
```
|
|
582
|
+
|
|
507
583
|
##### Using Context Manager (Recommended)
|
|
508
584
|
|
|
509
585
|
The context manager pattern ensures connections are automatically returned to the pool:
|
|
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
|
|
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-2.3.12rc2 → e6data_python_connector-2.3.12rc12}/gen-py/e6x_vector/ttypes.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{e6data_python_connector-2.3.12rc2 → e6data_python_connector-2.3.12rc12}/test/analyze_38_nines.py
RENAMED
|
File without changes
|
{e6data_python_connector-2.3.12rc2 → e6data_python_connector-2.3.12rc12}/test/analyze_all_cases.py
RENAMED
|
File without changes
|
{e6data_python_connector-2.3.12rc2 → e6data_python_connector-2.3.12rc12}/test/analyze_binary.py
RENAMED
|
File without changes
|
|
File without changes
|
{e6data_python_connector-2.3.12rc2 → e6data_python_connector-2.3.12rc12}/test/analyze_fields.py
RENAMED
|
File without changes
|
|
File without changes
|
{e6data_python_connector-2.3.12rc2 → e6data_python_connector-2.3.12rc12}/test/cleanup_test_files.py
RENAMED
|
File without changes
|
{e6data_python_connector-2.3.12rc2 → e6data_python_connector-2.3.12rc12}/test/debug_38_nines.py
RENAMED
|
File without changes
|
{e6data_python_connector-2.3.12rc2 → e6data_python_connector-2.3.12rc12}/test/debug_binary.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{e6data_python_connector-2.3.12rc2 → e6data_python_connector-2.3.12rc12}/test/test_38_nines.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{e6data_python_connector-2.3.12rc2 → e6data_python_connector-2.3.12rc12}/test/test_comprehensive.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
|
{e6data_python_connector-2.3.12rc2 → e6data_python_connector-2.3.12rc12}/test/test_known_case.py
RENAMED
|
File without changes
|
|
File without changes
|
{e6data_python_connector-2.3.12rc2 → e6data_python_connector-2.3.12rc12}/test/test_mock_server.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{e6data_python_connector-2.3.12rc2 → e6data_python_connector-2.3.12rc12}/test/test_strategy.py
RENAMED
|
File without changes
|
{e6data_python_connector-2.3.12rc2 → e6data_python_connector-2.3.12rc12}/test/test_strategy_logic.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{e6data_python_connector-2.3.12rc2 → e6data_python_connector-2.3.12rc12}/test/validate_decimal128.py
RENAMED
|
File without changes
|
|
File without changes
|
{e6data_python_connector-2.3.12rc2 → e6data_python_connector-2.3.12rc12}/test/verify_decimal_fix.py
RENAMED
|
File without changes
|