databricks-sql-connector 4.2.6__tar.gz → 4.3.0__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.
- {databricks_sql_connector-4.2.6 → databricks_sql_connector-4.3.0}/CHANGELOG.md +13 -0
- {databricks_sql_connector-4.2.6 → databricks_sql_connector-4.3.0}/PKG-INFO +31 -4
- {databricks_sql_connector-4.2.6 → databricks_sql_connector-4.3.0}/README.md +24 -0
- databricks_sql_connector-4.3.0/pyproject.toml +135 -0
- {databricks_sql_connector-4.2.6 → databricks_sql_connector-4.3.0}/src/databricks/sql/__init__.py +1 -1
- {databricks_sql_connector-4.2.6 → databricks_sql_connector-4.3.0}/src/databricks/sql/auth/common.py +2 -0
- {databricks_sql_connector-4.2.6 → databricks_sql_connector-4.3.0}/src/databricks/sql/auth/retry.py +16 -2
- databricks_sql_connector-4.3.0/src/databricks/sql/backend/kernel/__init__.py +25 -0
- databricks_sql_connector-4.3.0/src/databricks/sql/backend/kernel/_errors.py +185 -0
- databricks_sql_connector-4.3.0/src/databricks/sql/backend/kernel/auth_bridge.py +268 -0
- databricks_sql_connector-4.3.0/src/databricks/sql/backend/kernel/client.py +1069 -0
- databricks_sql_connector-4.3.0/src/databricks/sql/backend/kernel/result_set.py +274 -0
- databricks_sql_connector-4.3.0/src/databricks/sql/backend/kernel/type_mapping.py +248 -0
- {databricks_sql_connector-4.2.6 → databricks_sql_connector-4.3.0}/src/databricks/sql/backend/sea/utils/http_client.py +4 -0
- {databricks_sql_connector-4.2.6 → databricks_sql_connector-4.3.0}/src/databricks/sql/backend/thrift_backend.py +4 -0
- {databricks_sql_connector-4.2.6 → databricks_sql_connector-4.3.0}/src/databricks/sql/client.py +36 -6
- {databricks_sql_connector-4.2.6 → databricks_sql_connector-4.3.0}/src/databricks/sql/common/unified_http_client.py +1 -0
- {databricks_sql_connector-4.2.6 → databricks_sql_connector-4.3.0}/src/databricks/sql/result_set.py +35 -5
- {databricks_sql_connector-4.2.6 → databricks_sql_connector-4.3.0}/src/databricks/sql/session.py +142 -26
- {databricks_sql_connector-4.2.6 → databricks_sql_connector-4.3.0}/src/databricks/sql/telemetry/telemetry_client.py +5 -0
- {databricks_sql_connector-4.2.6 → databricks_sql_connector-4.3.0}/src/databricks/sql/utils.py +6 -4
- databricks_sql_connector-4.2.6/pyproject.toml +0 -95
- {databricks_sql_connector-4.2.6 → databricks_sql_connector-4.3.0}/LICENSE +0 -0
- {databricks_sql_connector-4.2.6 → databricks_sql_connector-4.3.0}/src/databricks/__init__.py +0 -0
- {databricks_sql_connector-4.2.6 → databricks_sql_connector-4.3.0}/src/databricks/sql/auth/__init__.py +0 -0
- {databricks_sql_connector-4.2.6 → databricks_sql_connector-4.3.0}/src/databricks/sql/auth/auth.py +0 -0
- {databricks_sql_connector-4.2.6 → databricks_sql_connector-4.3.0}/src/databricks/sql/auth/auth_utils.py +0 -0
- {databricks_sql_connector-4.2.6 → databricks_sql_connector-4.3.0}/src/databricks/sql/auth/authenticators.py +0 -0
- {databricks_sql_connector-4.2.6 → databricks_sql_connector-4.3.0}/src/databricks/sql/auth/endpoint.py +0 -0
- {databricks_sql_connector-4.2.6 → databricks_sql_connector-4.3.0}/src/databricks/sql/auth/oauth.py +0 -0
- {databricks_sql_connector-4.2.6 → databricks_sql_connector-4.3.0}/src/databricks/sql/auth/oauth_http_handler.py +0 -0
- {databricks_sql_connector-4.2.6 → databricks_sql_connector-4.3.0}/src/databricks/sql/auth/thrift_http_client.py +0 -0
- {databricks_sql_connector-4.2.6 → databricks_sql_connector-4.3.0}/src/databricks/sql/auth/token_federation.py +0 -0
- {databricks_sql_connector-4.2.6 → databricks_sql_connector-4.3.0}/src/databricks/sql/backend/databricks_client.py +0 -0
- {databricks_sql_connector-4.2.6 → databricks_sql_connector-4.3.0}/src/databricks/sql/backend/sea/backend.py +0 -0
- {databricks_sql_connector-4.2.6 → databricks_sql_connector-4.3.0}/src/databricks/sql/backend/sea/models/__init__.py +0 -0
- {databricks_sql_connector-4.2.6 → databricks_sql_connector-4.3.0}/src/databricks/sql/backend/sea/models/base.py +0 -0
- {databricks_sql_connector-4.2.6 → databricks_sql_connector-4.3.0}/src/databricks/sql/backend/sea/models/requests.py +0 -0
- {databricks_sql_connector-4.2.6 → databricks_sql_connector-4.3.0}/src/databricks/sql/backend/sea/models/responses.py +0 -0
- {databricks_sql_connector-4.2.6 → databricks_sql_connector-4.3.0}/src/databricks/sql/backend/sea/queue.py +0 -0
- {databricks_sql_connector-4.2.6 → databricks_sql_connector-4.3.0}/src/databricks/sql/backend/sea/result_set.py +0 -0
- {databricks_sql_connector-4.2.6 → databricks_sql_connector-4.3.0}/src/databricks/sql/backend/sea/utils/constants.py +0 -0
- {databricks_sql_connector-4.2.6 → databricks_sql_connector-4.3.0}/src/databricks/sql/backend/sea/utils/conversion.py +0 -0
- {databricks_sql_connector-4.2.6 → databricks_sql_connector-4.3.0}/src/databricks/sql/backend/sea/utils/filters.py +0 -0
- {databricks_sql_connector-4.2.6 → databricks_sql_connector-4.3.0}/src/databricks/sql/backend/sea/utils/normalize.py +0 -0
- {databricks_sql_connector-4.2.6 → databricks_sql_connector-4.3.0}/src/databricks/sql/backend/types.py +0 -0
- {databricks_sql_connector-4.2.6 → databricks_sql_connector-4.3.0}/src/databricks/sql/backend/utils/__init__.py +0 -0
- {databricks_sql_connector-4.2.6 → databricks_sql_connector-4.3.0}/src/databricks/sql/backend/utils/guid_utils.py +0 -0
- {databricks_sql_connector-4.2.6 → databricks_sql_connector-4.3.0}/src/databricks/sql/cloudfetch/download_manager.py +0 -0
- {databricks_sql_connector-4.2.6 → databricks_sql_connector-4.3.0}/src/databricks/sql/cloudfetch/downloader.py +0 -0
- {databricks_sql_connector-4.2.6 → databricks_sql_connector-4.3.0}/src/databricks/sql/common/agent.py +0 -0
- {databricks_sql_connector-4.2.6 → databricks_sql_connector-4.3.0}/src/databricks/sql/common/feature_flag.py +0 -0
- {databricks_sql_connector-4.2.6 → databricks_sql_connector-4.3.0}/src/databricks/sql/common/http.py +0 -0
- {databricks_sql_connector-4.2.6 → databricks_sql_connector-4.3.0}/src/databricks/sql/common/http_utils.py +0 -0
- {databricks_sql_connector-4.2.6 → databricks_sql_connector-4.3.0}/src/databricks/sql/common/url_utils.py +0 -0
- {databricks_sql_connector-4.2.6 → databricks_sql_connector-4.3.0}/src/databricks/sql/exc.py +0 -0
- {databricks_sql_connector-4.2.6 → databricks_sql_connector-4.3.0}/src/databricks/sql/experimental/__init__.py +0 -0
- {databricks_sql_connector-4.2.6 → databricks_sql_connector-4.3.0}/src/databricks/sql/experimental/oauth_persistence.py +0 -0
- {databricks_sql_connector-4.2.6 → databricks_sql_connector-4.3.0}/src/databricks/sql/parameters/__init__.py +0 -0
- {databricks_sql_connector-4.2.6 → databricks_sql_connector-4.3.0}/src/databricks/sql/parameters/native.py +0 -0
- {databricks_sql_connector-4.2.6 → databricks_sql_connector-4.3.0}/src/databricks/sql/parameters/py.typed +0 -0
- {databricks_sql_connector-4.2.6 → databricks_sql_connector-4.3.0}/src/databricks/sql/py.typed +0 -0
- {databricks_sql_connector-4.2.6 → databricks_sql_connector-4.3.0}/src/databricks/sql/telemetry/circuit_breaker_manager.py +0 -0
- {databricks_sql_connector-4.2.6 → databricks_sql_connector-4.3.0}/src/databricks/sql/telemetry/latency_logger.py +0 -0
- {databricks_sql_connector-4.2.6 → databricks_sql_connector-4.3.0}/src/databricks/sql/telemetry/models/endpoint_models.py +0 -0
- {databricks_sql_connector-4.2.6 → databricks_sql_connector-4.3.0}/src/databricks/sql/telemetry/models/enums.py +0 -0
- {databricks_sql_connector-4.2.6 → databricks_sql_connector-4.3.0}/src/databricks/sql/telemetry/models/event.py +0 -0
- {databricks_sql_connector-4.2.6 → databricks_sql_connector-4.3.0}/src/databricks/sql/telemetry/models/frontend_logs.py +0 -0
- {databricks_sql_connector-4.2.6 → databricks_sql_connector-4.3.0}/src/databricks/sql/telemetry/telemetry_push_client.py +0 -0
- {databricks_sql_connector-4.2.6 → databricks_sql_connector-4.3.0}/src/databricks/sql/telemetry/utils.py +0 -0
- {databricks_sql_connector-4.2.6 → databricks_sql_connector-4.3.0}/src/databricks/sql/thrift_api/TCLIService/TCLIService-remote +0 -0
- {databricks_sql_connector-4.2.6 → databricks_sql_connector-4.3.0}/src/databricks/sql/thrift_api/TCLIService/TCLIService.py +0 -0
- {databricks_sql_connector-4.2.6 → databricks_sql_connector-4.3.0}/src/databricks/sql/thrift_api/TCLIService/__init__.py +0 -0
- {databricks_sql_connector-4.2.6 → databricks_sql_connector-4.3.0}/src/databricks/sql/thrift_api/TCLIService/constants.py +0 -0
- {databricks_sql_connector-4.2.6 → databricks_sql_connector-4.3.0}/src/databricks/sql/thrift_api/TCLIService/ttypes.py +0 -0
- {databricks_sql_connector-4.2.6 → databricks_sql_connector-4.3.0}/src/databricks/sql/thrift_api/__init__.py +0 -0
- {databricks_sql_connector-4.2.6 → databricks_sql_connector-4.3.0}/src/databricks/sql/types.py +0 -0
|
@@ -1,5 +1,18 @@
|
|
|
1
1
|
# Release History
|
|
2
2
|
|
|
3
|
+
# 4.3.0 (2026-06-12)
|
|
4
|
+
- **New: optional Rust kernel backend (`use_kernel=True`).** Adds an alternative connection path backed by the native [`databricks-sql-kernel`](https://pypi.org/project/databricks-sql-kernel/) client (a Rust core exposed via PyO3), installable with the new `databricks-sql-connector[kernel]` extra. The kernel talks to Databricks over the **SEA (Statement Execution API) HTTP transport** — not Thrift — with CloudFetch and inline-Arrow result fetching, so `use_kernel=True` gives you a modern SEA-native client through the same DB-API surface. Supports PAT, OAuth M2M, and OAuth U2M auth. Requires Python >= 3.10 (the kernel wheel is `cp310-abi3`); on older interpreters the extra is a no-op and `use_kernel=True` raises a clear `ImportError`. The default backend remains Thrift — opt in per connection.
|
|
5
|
+
- Kernel backend behavior is aligned with the Thrift backend so application code works the same either way: consistent cursor-state tracking (`query_id` / `get_query_state`), metadata (catalogs/schemas/tables/columns with JDBC-style filter semantics and case-insensitive `table_types`), DML `rowcount`, server-sourced async execution state, sync `cancel()`, fail-loud staging/volume operations, and structured error context (SQLSTATE, diagnostic info). Kernel logs surface through Python `logging` under the `databricks.sql.kernel` logger (databricks/databricks-sql-python#824, #825, #830, #838, #839 by @vikrantpuppala)
|
|
6
|
+
- Revert the thrift 0.23.0 bump that broke installation on DBR LTS (ES-1960554) (databricks/databricks-sql-python#840 by @vikrantpuppala)
|
|
7
|
+
|
|
8
|
+
# 4.2.7 (2026-06-02)
|
|
9
|
+
- Extract SPOG org-id from cluster http_path for non-Thrift requests (databricks/databricks-sql-python#817 by @msrathore-db)
|
|
10
|
+
- Remove empty chunks in CloudFetch concatenation (databricks/databricks-sql-python#814 by @jprakash-db)
|
|
11
|
+
- Add `_retry_server_directed_only` mode for Retry-After header compliance (databricks/databricks-sql-python#756 by @sd-db)
|
|
12
|
+
- Bump thrift to 0.23.0 (databricks/databricks-sql-python#796 by @leoromanovsky)
|
|
13
|
+
- Allow pandas 3.x in dependency constraints (databricks/databricks-sql-python#768 by @moomindani)
|
|
14
|
+
- Telemetry: unwrap TokenFederationProvider to report inner auth mechanism/flow (databricks/databricks-sql-python#781 by @samikshya-db)
|
|
15
|
+
|
|
3
16
|
# 4.2.6 (2026-04-22)
|
|
4
17
|
- Add SPOG routing support for account-level vanity URLs (databricks/databricks-sql-python#767 by @msrathore-db)
|
|
5
18
|
- Fix dependency_manager: handle PEP 440 ~= compatible release syntax (databricks/databricks-sql-python#776 by @vikrantpuppala)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: databricks-sql-connector
|
|
3
|
-
Version: 4.
|
|
3
|
+
Version: 4.3.0
|
|
4
4
|
Summary: Databricks SQL Connector for Python
|
|
5
5
|
License: Apache-2.0
|
|
6
6
|
License-File: LICENSE
|
|
@@ -16,14 +16,17 @@ Classifier: Programming Language :: Python :: 3.11
|
|
|
16
16
|
Classifier: Programming Language :: Python :: 3.12
|
|
17
17
|
Classifier: Programming Language :: Python :: 3.13
|
|
18
18
|
Classifier: Programming Language :: Python :: 3.14
|
|
19
|
+
Provides-Extra: kernel
|
|
19
20
|
Provides-Extra: pyarrow
|
|
21
|
+
Requires-Dist: databricks-sql-kernel (>=0.2.0,<0.3.0) ; (python_version >= "3.10") and (extra == "kernel")
|
|
20
22
|
Requires-Dist: lz4 (>=4.0.2,<5.0.0) ; python_version >= "3.8" and python_version < "3.14"
|
|
21
23
|
Requires-Dist: lz4 (>=4.4.5,<5.0.0) ; python_version >= "3.14"
|
|
22
24
|
Requires-Dist: oauthlib (>=3.1.0,<4.0.0)
|
|
23
25
|
Requires-Dist: openpyxl (>=3.0.10,<4.0.0)
|
|
24
|
-
Requires-Dist: pandas (>=1.2.5,<
|
|
25
|
-
Requires-Dist: pandas (>=2.2.3,<
|
|
26
|
-
Requires-Dist: pyarrow (>=14.0.1) ; (python_version >= "3.
|
|
26
|
+
Requires-Dist: pandas (>=1.2.5,<4.0.0) ; python_version >= "3.8" and python_version < "3.13"
|
|
27
|
+
Requires-Dist: pandas (>=2.2.3,<4.0.0) ; python_version >= "3.13"
|
|
28
|
+
Requires-Dist: pyarrow (>=14.0.1) ; (python_version >= "3.10" and python_version < "3.13") and (extra == "pyarrow")
|
|
29
|
+
Requires-Dist: pyarrow (>=14.0.1,<23) ; (python_version >= "3.8" and python_version < "3.10") and (extra == "pyarrow")
|
|
27
30
|
Requires-Dist: pyarrow (>=18.0.0) ; (python_version == "3.13") and (extra == "pyarrow")
|
|
28
31
|
Requires-Dist: pyarrow (>=22.0.0) ; (python_version >= "3.14") and (extra == "pyarrow")
|
|
29
32
|
Requires-Dist: pybreaker (>=1.0.0,<2.0.0)
|
|
@@ -68,6 +71,30 @@ Install using `pip install databricks-sql-connector`
|
|
|
68
71
|
### Installing the core library with PyArrow
|
|
69
72
|
Install using `pip install databricks-sql-connector[pyarrow]`
|
|
70
73
|
|
|
74
|
+
### Installing with the Rust kernel backend (`use_kernel=True`)
|
|
75
|
+
Install using `pip install databricks-sql-connector[kernel]`
|
|
76
|
+
|
|
77
|
+
This adds the optional [`databricks-sql-kernel`](https://pypi.org/project/databricks-sql-kernel/)
|
|
78
|
+
extension (a native Rust client core, exposed via PyO3). Pass
|
|
79
|
+
`use_kernel=True` to `sql.connect(...)` to route the connection through it
|
|
80
|
+
instead of the default Thrift backend:
|
|
81
|
+
|
|
82
|
+
```python
|
|
83
|
+
connection = sql.connect(
|
|
84
|
+
server_hostname=host,
|
|
85
|
+
http_path=http_path,
|
|
86
|
+
access_token=token,
|
|
87
|
+
use_kernel=True,
|
|
88
|
+
)
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
Notes:
|
|
92
|
+
- Requires **Python >= 3.10** (the kernel wheel is published as
|
|
93
|
+
`cp310-abi3`). On older interpreters the `[kernel]` extra installs
|
|
94
|
+
nothing and `use_kernel=True` raises an `ImportError`.
|
|
95
|
+
- The extra also pulls in PyArrow, which the kernel result path requires.
|
|
96
|
+
- Authentication supports PAT (`access_token`), OAuth M2M, and OAuth U2M.
|
|
97
|
+
|
|
71
98
|
|
|
72
99
|
```bash
|
|
73
100
|
export DATABRICKS_HOST=********.databricks.com
|
|
@@ -30,6 +30,30 @@ Install using `pip install databricks-sql-connector`
|
|
|
30
30
|
### Installing the core library with PyArrow
|
|
31
31
|
Install using `pip install databricks-sql-connector[pyarrow]`
|
|
32
32
|
|
|
33
|
+
### Installing with the Rust kernel backend (`use_kernel=True`)
|
|
34
|
+
Install using `pip install databricks-sql-connector[kernel]`
|
|
35
|
+
|
|
36
|
+
This adds the optional [`databricks-sql-kernel`](https://pypi.org/project/databricks-sql-kernel/)
|
|
37
|
+
extension (a native Rust client core, exposed via PyO3). Pass
|
|
38
|
+
`use_kernel=True` to `sql.connect(...)` to route the connection through it
|
|
39
|
+
instead of the default Thrift backend:
|
|
40
|
+
|
|
41
|
+
```python
|
|
42
|
+
connection = sql.connect(
|
|
43
|
+
server_hostname=host,
|
|
44
|
+
http_path=http_path,
|
|
45
|
+
access_token=token,
|
|
46
|
+
use_kernel=True,
|
|
47
|
+
)
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
Notes:
|
|
51
|
+
- Requires **Python >= 3.10** (the kernel wheel is published as
|
|
52
|
+
`cp310-abi3`). On older interpreters the `[kernel]` extra installs
|
|
53
|
+
nothing and `use_kernel=True` raises an `ImportError`.
|
|
54
|
+
- The extra also pulls in PyArrow, which the kernel result path requires.
|
|
55
|
+
- Authentication supports PAT (`access_token`), OAuth M2M, and OAuth U2M.
|
|
56
|
+
|
|
33
57
|
|
|
34
58
|
```bash
|
|
35
59
|
export DATABRICKS_HOST=********.databricks.com
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
[tool.poetry]
|
|
2
|
+
name = "databricks-sql-connector"
|
|
3
|
+
version = "4.3.0"
|
|
4
|
+
description = "Databricks SQL Connector for Python"
|
|
5
|
+
authors = ["Databricks <databricks-sql-connector-maintainers@databricks.com>"]
|
|
6
|
+
license = "Apache-2.0"
|
|
7
|
+
readme = "README.md"
|
|
8
|
+
packages = [{ include = "databricks", from = "src" }]
|
|
9
|
+
include = ["CHANGELOG.md"]
|
|
10
|
+
|
|
11
|
+
[tool.poetry.dependencies]
|
|
12
|
+
python = "^3.8.0"
|
|
13
|
+
thrift = "~=0.22.0"
|
|
14
|
+
pandas = [
|
|
15
|
+
{ version = ">=1.2.5,<4.0.0", python = ">=3.8,<3.13" },
|
|
16
|
+
{ version = ">=2.2.3,<4.0.0", python = ">=3.13" }
|
|
17
|
+
]
|
|
18
|
+
lz4 = [
|
|
19
|
+
{ version = "^4.0.2", python = ">=3.8,<3.14" },
|
|
20
|
+
{ version = "^4.4.5", python = ">=3.14" }
|
|
21
|
+
]
|
|
22
|
+
requests = "^2.18.1"
|
|
23
|
+
oauthlib = "^3.1.0"
|
|
24
|
+
openpyxl = "^3.0.10"
|
|
25
|
+
urllib3 = ">=1.26"
|
|
26
|
+
python-dateutil = "^2.8.0"
|
|
27
|
+
pyarrow = [
|
|
28
|
+
# The <3.10 band is capped at <23 because pyarrow>=23 dropped
|
|
29
|
+
# Python 3.9 (it requires >=3.10). Without the cap, poetry tries to
|
|
30
|
+
# unify this entry with the kernel's transitive pyarrow>=23.0.1,<24
|
|
31
|
+
# across the 3.8–3.10 slice and `poetry lock` fails ("pyarrow is
|
|
32
|
+
# forbidden"). The cap removes no installable version — the newest
|
|
33
|
+
# pyarrow with a 3.9 wheel is 21.x — it just makes that explicit to
|
|
34
|
+
# the solver so the optional [kernel] extra (python>=3.10) can
|
|
35
|
+
# coexist. See the kernel dep + [kernel] extra below.
|
|
36
|
+
{ version = ">=14.0.1,<23", python = ">=3.8,<3.10", optional=true },
|
|
37
|
+
{ version = ">=14.0.1", python = ">=3.10,<3.13", optional=true },
|
|
38
|
+
{ version = ">=18.0.0", python = ">=3.13,<3.14", optional=true },
|
|
39
|
+
{ version = ">=22.0.0", python = ">=3.14", optional=true }
|
|
40
|
+
]
|
|
41
|
+
pyjwt = "^2.0.0"
|
|
42
|
+
pybreaker = "^1.0.0"
|
|
43
|
+
requests-kerberos = {version = "^0.15.0", optional = true}
|
|
44
|
+
# Optional Rust kernel backend for ``use_kernel=True`` (PyO3 wheel).
|
|
45
|
+
# Pulled in only via the ``[kernel]`` extra below. The published wheel
|
|
46
|
+
# is ``abi3`` with ``Requires-Python: >=3.10`` (built ``abi3-py310``),
|
|
47
|
+
# so the dependency is gated to Python >= 3.10: on 3.8/3.9 the
|
|
48
|
+
# ``[kernel]`` extra resolves to nothing and ``use_kernel=True`` raises
|
|
49
|
+
# a clear ImportError at runtime (see backend/kernel/_errors.py).
|
|
50
|
+
#
|
|
51
|
+
# Floor is 0.2.0 (``^0.2.0`` == ``>=0.2.0,<0.3.0``). The kernel is
|
|
52
|
+
# pre-1.0, so each 0.x minor may carry breaking changes — the ``<0.3.0``
|
|
53
|
+
# cap means we bump this deliberately when the kernel ships 0.3.0 rather
|
|
54
|
+
# than letting a potentially-breaking minor flow in automatically. 0.2.0
|
|
55
|
+
# keeps the same Requires-Python (>=3.10) and pyarrow (>=23.0.1,<24) pin
|
|
56
|
+
# as 0.1.x, so the gating below is unchanged.
|
|
57
|
+
databricks-sql-kernel = {version = "^0.2.0", optional = true, python = ">=3.10"}
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
[tool.poetry.extras]
|
|
61
|
+
pyarrow = ["pyarrow"]
|
|
62
|
+
# ``pip install databricks-sql-connector[kernel]`` adds the Rust kernel
|
|
63
|
+
# backend so ``use_kernel=True`` works. No-op on Python < 3.10 (the
|
|
64
|
+
# wheel's floor) — those users get a runtime ImportError if they pass
|
|
65
|
+
# ``use_kernel=True``.
|
|
66
|
+
#
|
|
67
|
+
# The kernel result path (``backend/kernel/result_set.py``) needs
|
|
68
|
+
# pyarrow, but it is NOT listed in this extra on purpose: the published
|
|
69
|
+
# kernel wheel declares ``pyarrow>=23.0.1,<24`` as a hard runtime
|
|
70
|
+
# dependency, so ``pip install ...[kernel]`` already pulls a compatible
|
|
71
|
+
# pyarrow transitively. Listing bare ``pyarrow`` here additionally
|
|
72
|
+
# forces poetry to co-resolve an unconstrained pyarrow against the
|
|
73
|
+
# kernel's ``>=23.0.1,<24`` (which itself requires Python >=3.10) across
|
|
74
|
+
# the connector's full 3.8–3.14 support matrix, which is unsatisfiable
|
|
75
|
+
# on 3.8/3.9 and breaks ``poetry lock``. The kernel's own dependency
|
|
76
|
+
# metadata is the single source of truth for the pyarrow floor.
|
|
77
|
+
kernel = ["databricks-sql-kernel"]
|
|
78
|
+
|
|
79
|
+
[tool.poetry.group.dev.dependencies]
|
|
80
|
+
pytest = "^7.1.2"
|
|
81
|
+
mypy = "^1.10.1"
|
|
82
|
+
pylint = ">=2.12.0"
|
|
83
|
+
black = "^22.3.0"
|
|
84
|
+
pytest-dotenv = "^0.5.2"
|
|
85
|
+
pytest-cov = "^4.0.0"
|
|
86
|
+
pytest-xdist = "^3.0.0"
|
|
87
|
+
numpy = [
|
|
88
|
+
{ version = ">=1.16.6", python = ">=3.8,<3.11" },
|
|
89
|
+
{ version = ">=1.23.4", python = ">=3.11" },
|
|
90
|
+
]
|
|
91
|
+
|
|
92
|
+
[tool.poetry.urls]
|
|
93
|
+
"Homepage" = "https://github.com/databricks/databricks-sql-python"
|
|
94
|
+
"Bug Tracker" = "https://github.com/databricks/databricks-sql-python/issues"
|
|
95
|
+
|
|
96
|
+
[build-system]
|
|
97
|
+
requires = ["poetry-core>=1.0.0"]
|
|
98
|
+
build-backend = "poetry.core.masonry.api"
|
|
99
|
+
|
|
100
|
+
[tool.mypy]
|
|
101
|
+
ignore_missing_imports = "true"
|
|
102
|
+
exclude = ['ttypes\.py$', 'TCLIService\.py$']
|
|
103
|
+
|
|
104
|
+
[tool.black]
|
|
105
|
+
exclude = '/(\.eggs|\.git|\.hg|\.mypy_cache|\.nox|\.tox|\.venv|\.svn|_build|buck-out|build|dist|thrift_api)/'
|
|
106
|
+
|
|
107
|
+
[tool.pytest.ini_options]
|
|
108
|
+
markers = [
|
|
109
|
+
"reviewed: Test case has been reviewed by Databricks",
|
|
110
|
+
"serial: Tests that must run serially (not parallelized)",
|
|
111
|
+
"realkernel: Requires the real databricks-sql-kernel wheel and an unpolluted sys.modules (no fake kernel stub); must run in a separate pytest invocation from tests that fake databricks_sql_kernel (deselect with -m 'not realkernel', run alone with -m realkernel).",
|
|
112
|
+
]
|
|
113
|
+
minversion = "6.0"
|
|
114
|
+
log_cli = "false"
|
|
115
|
+
log_cli_level = "INFO"
|
|
116
|
+
testpaths = ["tests"]
|
|
117
|
+
env_files = ["test.env"]
|
|
118
|
+
|
|
119
|
+
[tool.coverage.run]
|
|
120
|
+
source = ["src"]
|
|
121
|
+
branch = true
|
|
122
|
+
omit = [
|
|
123
|
+
"*/tests/*",
|
|
124
|
+
"*/test_*",
|
|
125
|
+
"*/__pycache__/*",
|
|
126
|
+
"*/thrift_api/*",
|
|
127
|
+
]
|
|
128
|
+
|
|
129
|
+
[tool.coverage.report]
|
|
130
|
+
precision = 2
|
|
131
|
+
show_missing = true
|
|
132
|
+
skip_covered = false
|
|
133
|
+
|
|
134
|
+
[tool.coverage.xml]
|
|
135
|
+
output = "coverage.xml"
|
{databricks_sql_connector-4.2.6 → databricks_sql_connector-4.3.0}/src/databricks/sql/auth/common.py
RENAMED
|
@@ -47,6 +47,7 @@ class ClientContext:
|
|
|
47
47
|
retry_stop_after_attempts_duration: Optional[float] = None,
|
|
48
48
|
retry_delay_default: Optional[float] = None,
|
|
49
49
|
retry_dangerous_codes: Optional[List[int]] = None,
|
|
50
|
+
respect_server_retry_after_header: Optional[bool] = None,
|
|
50
51
|
proxy_auth_method: Optional[str] = None,
|
|
51
52
|
pool_connections: Optional[int] = None,
|
|
52
53
|
pool_maxsize: Optional[int] = None,
|
|
@@ -80,6 +81,7 @@ class ClientContext:
|
|
|
80
81
|
)
|
|
81
82
|
self.retry_delay_default = retry_delay_default or 5.0
|
|
82
83
|
self.retry_dangerous_codes = retry_dangerous_codes or []
|
|
84
|
+
self.respect_server_retry_after_header = bool(respect_server_retry_after_header)
|
|
83
85
|
self.proxy_auth_method = proxy_auth_method
|
|
84
86
|
self.pool_connections = pool_connections or 10
|
|
85
87
|
self.pool_maxsize = pool_maxsize or 20
|
{databricks_sql_connector-4.2.6 → databricks_sql_connector-4.3.0}/src/databricks/sql/auth/retry.py
RENAMED
|
@@ -94,6 +94,7 @@ class DatabricksRetryPolicy(Retry):
|
|
|
94
94
|
stop_after_attempts_duration: float,
|
|
95
95
|
delay_default: float,
|
|
96
96
|
force_dangerous_codes: List[int],
|
|
97
|
+
respect_server_retry_after_header: bool = False,
|
|
97
98
|
urllib3_kwargs: dict = {},
|
|
98
99
|
):
|
|
99
100
|
# These values do not change from one command to the next
|
|
@@ -103,6 +104,7 @@ class DatabricksRetryPolicy(Retry):
|
|
|
103
104
|
self.stop_after_attempts_duration = stop_after_attempts_duration
|
|
104
105
|
self._delay_default = delay_default
|
|
105
106
|
self.force_dangerous_codes = force_dangerous_codes
|
|
107
|
+
self.respect_server_retry_after_header = respect_server_retry_after_header
|
|
106
108
|
|
|
107
109
|
# the urllib3 kwargs are a mix of configuration (some of which we override)
|
|
108
110
|
# and counters like `total` or `connect` which may change between successive retries
|
|
@@ -202,6 +204,7 @@ class DatabricksRetryPolicy(Retry):
|
|
|
202
204
|
stop_after_attempts_duration=self.stop_after_attempts_duration,
|
|
203
205
|
delay_default=self.delay_default,
|
|
204
206
|
force_dangerous_codes=self.force_dangerous_codes,
|
|
207
|
+
respect_server_retry_after_header=self.respect_server_retry_after_header,
|
|
205
208
|
urllib3_kwargs={},
|
|
206
209
|
)
|
|
207
210
|
|
|
@@ -323,7 +326,9 @@ class DatabricksRetryPolicy(Retry):
|
|
|
323
326
|
|
|
324
327
|
return proposed_backoff
|
|
325
328
|
|
|
326
|
-
def should_retry(
|
|
329
|
+
def should_retry(
|
|
330
|
+
self, method: str, status_code: int, has_retry_after: bool = False
|
|
331
|
+
) -> Tuple[bool, str]:
|
|
327
332
|
"""This method encapsulates the connector's approach to retries.
|
|
328
333
|
|
|
329
334
|
We always retry a request unless one of these conditions is met:
|
|
@@ -388,6 +393,15 @@ class DatabricksRetryPolicy(Retry):
|
|
|
388
393
|
if not self._is_method_retryable(method):
|
|
389
394
|
return False, "Only POST requests are retried"
|
|
390
395
|
|
|
396
|
+
# When respect_server_retry_after_header is enabled, only retry when the
|
|
397
|
+
# server explicitly signals it's safe via a Retry-After header. This prevents
|
|
398
|
+
# duplicate side effects for non-idempotent operations.
|
|
399
|
+
if self.respect_server_retry_after_header and not has_retry_after:
|
|
400
|
+
return (
|
|
401
|
+
False,
|
|
402
|
+
"respect_server_retry_after_header mode: no Retry-After header present",
|
|
403
|
+
)
|
|
404
|
+
|
|
391
405
|
# Request failed, was an ExecuteStatement and the command may have reached the server
|
|
392
406
|
if (
|
|
393
407
|
self.command_type == CommandType.EXECUTE_STATEMENT
|
|
@@ -430,7 +444,7 @@ class DatabricksRetryPolicy(Retry):
|
|
|
430
444
|
Logs a debug message if the request will be retried
|
|
431
445
|
"""
|
|
432
446
|
|
|
433
|
-
should_retry, msg = self.should_retry(method, status_code)
|
|
447
|
+
should_retry, msg = self.should_retry(method, status_code, has_retry_after)
|
|
434
448
|
|
|
435
449
|
if should_retry:
|
|
436
450
|
logger.debug(msg)
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
"""Backend that delegates to the Databricks SQL Kernel (Rust) via PyO3.
|
|
2
|
+
|
|
3
|
+
Routed when ``use_kernel=True`` is passed to ``databricks.sql.connect``.
|
|
4
|
+
The module's identity is "delegates to the kernel" — not the wire
|
|
5
|
+
protocol the kernel happens to use today (SEA REST). The kernel may
|
|
6
|
+
switch its default transport (SEA REST → SEA gRPC → …) without
|
|
7
|
+
renaming this module.
|
|
8
|
+
|
|
9
|
+
This ``__init__`` deliberately does **not** re-export
|
|
10
|
+
``KernelDatabricksClient`` from ``.client``. Importing ``.client``
|
|
11
|
+
loads the ``databricks_sql_kernel`` PyO3 extension at module-import
|
|
12
|
+
time; doing that eagerly here would make ``import
|
|
13
|
+
databricks.sql.backend.kernel.type_mapping`` (used by tests / by
|
|
14
|
+
``KernelResultSet`` consumers) require the kernel wheel even when
|
|
15
|
+
the caller never plans to open a kernel-backed session. Callers
|
|
16
|
+
that need the client import it directly:
|
|
17
|
+
|
|
18
|
+
from databricks.sql.backend.kernel.client import KernelDatabricksClient
|
|
19
|
+
|
|
20
|
+
``session.py::_create_backend`` already does this lazy import under
|
|
21
|
+
the ``use_kernel=True`` branch.
|
|
22
|
+
|
|
23
|
+
See ``docs/designs/pysql-kernel-integration.md`` in
|
|
24
|
+
``databricks-sql-kernel`` for the full integration design.
|
|
25
|
+
"""
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
"""Shared error-mapping primitives for the kernel backend.
|
|
2
|
+
|
|
3
|
+
The PyO3 boundary can produce two flavours of exception:
|
|
4
|
+
|
|
5
|
+
- ``databricks_sql_kernel.KernelError`` — the kernel's own
|
|
6
|
+
structured error type. Carries ``code`` / ``message`` /
|
|
7
|
+
``sql_state`` / ``query_id`` / ``http_status`` / ``retryable`` /
|
|
8
|
+
``vendor_code`` / ``error_code`` as attributes; mapped to a PEP
|
|
9
|
+
249 exception class via ``_CODE_TO_EXCEPTION`` with the
|
|
10
|
+
attributes forwarded onto the re-raised exception so callers can
|
|
11
|
+
branch on ``err.code`` / ``err.sql_state`` without reaching
|
|
12
|
+
through ``__cause__``.
|
|
13
|
+
- Anything else — ``TypeError`` / ``OverflowError`` /
|
|
14
|
+
``ValueError`` from PyO3 argument conversion, or arbitrary
|
|
15
|
+
extension-internal Python errors. These would otherwise propagate
|
|
16
|
+
raw to connector callers, breaking the DB-API contract that says
|
|
17
|
+
"only PEP 249 exception types cross the boundary". Wrapped in
|
|
18
|
+
``OperationalError`` here.
|
|
19
|
+
|
|
20
|
+
These primitives live in their own module so both ``client.py``
|
|
21
|
+
(which orchestrates PyO3 calls) and ``result_set.py`` (which calls
|
|
22
|
+
``fetch_next_batch`` on the same kernel handles) can share them
|
|
23
|
+
without ``result_set.py`` importing from ``client.py``.
|
|
24
|
+
|
|
25
|
+
Usage at every PyO3 call site is a plain try/except:
|
|
26
|
+
|
|
27
|
+
try:
|
|
28
|
+
stmt.execute()
|
|
29
|
+
except Exception as exc:
|
|
30
|
+
raise wrap_kernel_exception("execute_command", exc) from exc
|
|
31
|
+
|
|
32
|
+
The helper returns the mapped exception; callers raise it. Plain
|
|
33
|
+
``try/except`` is preferred over a context manager: the control
|
|
34
|
+
flow is visible at the call site, the helper is a pure function
|
|
35
|
+
(trivial to test), and tracebacks don't carry an extra
|
|
36
|
+
``__exit__`` frame.
|
|
37
|
+
"""
|
|
38
|
+
|
|
39
|
+
from __future__ import annotations
|
|
40
|
+
|
|
41
|
+
import logging
|
|
42
|
+
|
|
43
|
+
from databricks.sql.exc import (
|
|
44
|
+
DatabaseError,
|
|
45
|
+
Error,
|
|
46
|
+
OperationalError,
|
|
47
|
+
ProgrammingError,
|
|
48
|
+
ServerOperationError,
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
try:
|
|
52
|
+
import databricks_sql_kernel as _kernel # type: ignore[import-not-found]
|
|
53
|
+
except ImportError as exc: # pragma: no cover - same hint as client.py
|
|
54
|
+
raise ImportError(
|
|
55
|
+
"use_kernel=True requires the optional databricks-sql-kernel "
|
|
56
|
+
"extension, which is not installed. Install it with:\n"
|
|
57
|
+
' pip install "databricks-sql-connector[kernel]"\n'
|
|
58
|
+
"The kernel wheel requires Python >= 3.10; on older interpreters "
|
|
59
|
+
"use_kernel is unavailable. For local kernel development you can "
|
|
60
|
+
"instead build it from the databricks-sql-kernel repo:\n"
|
|
61
|
+
" cd databricks-sql-kernel/pyo3 && maturin develop --release"
|
|
62
|
+
) from exc
|
|
63
|
+
|
|
64
|
+
# Route the kernel's Rust-side logs into Python's ``logging`` as soon as
|
|
65
|
+
# the extension loads. The kernel emits under the ``databricks.sql.kernel``
|
|
66
|
+
# logger (a child of the connector's ``databricks.sql`` namespace), so a
|
|
67
|
+
# customer who configures ``databricks.sql`` logging gets kernel logs for
|
|
68
|
+
# free with no extra setup.
|
|
69
|
+
#
|
|
70
|
+
# This is a best-effort, non-essential feature: it must never take down
|
|
71
|
+
# ``use_kernel=True`` for a process. ``getattr`` guards against an older
|
|
72
|
+
# kernel wheel that predates the function. The ``try`` guards against the
|
|
73
|
+
# call itself throwing — note ``except BaseException`` is deliberate: a
|
|
74
|
+
# panic raised across the PyO3 boundary surfaces as
|
|
75
|
+
# ``pyo3_runtime.PanicException``, which derives from ``BaseException``
|
|
76
|
+
# (not ``Exception``), so a narrower clause would let it escape module
|
|
77
|
+
# import and fail every kernel-backed connection. The kernel side is
|
|
78
|
+
# idempotent and returns rather than panics on a double install, but we
|
|
79
|
+
# do not rely on that here — the guard holds regardless of the Rust impl.
|
|
80
|
+
_kernel_init_logging = getattr(_kernel, "init_logging", None)
|
|
81
|
+
if _kernel_init_logging is not None:
|
|
82
|
+
try:
|
|
83
|
+
_kernel_init_logging()
|
|
84
|
+
except BaseException as exc: # noqa: BLE001 - see comment above re: PanicException
|
|
85
|
+
logging.getLogger(__name__).debug(
|
|
86
|
+
"kernel log bridge init failed; continuing without it: %r", exc
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
# Map a kernel `code` slug to the PEP 249 exception class that best
|
|
91
|
+
# captures it. The match isn't a perfect 1:1 — PEP 249 has a
|
|
92
|
+
# narrower taxonomy than the kernel — so several kernel codes
|
|
93
|
+
# collapse onto the same Python exception. This table is the only
|
|
94
|
+
# place that mapping lives.
|
|
95
|
+
_CODE_TO_EXCEPTION = {
|
|
96
|
+
"InvalidArgument": ProgrammingError,
|
|
97
|
+
"Unauthenticated": OperationalError,
|
|
98
|
+
"PermissionDenied": OperationalError,
|
|
99
|
+
"NotFound": ProgrammingError,
|
|
100
|
+
"ResourceExhausted": OperationalError,
|
|
101
|
+
"Unavailable": OperationalError,
|
|
102
|
+
"Timeout": OperationalError,
|
|
103
|
+
"Cancelled": OperationalError,
|
|
104
|
+
"DataLoss": DatabaseError,
|
|
105
|
+
"Internal": DatabaseError,
|
|
106
|
+
"InvalidStatementHandle": ProgrammingError,
|
|
107
|
+
"NetworkError": OperationalError,
|
|
108
|
+
# `SqlError` is a server-side query failure (syntax error, missing
|
|
109
|
+
# object, etc.) — exactly what the Thrift backend surfaces as
|
|
110
|
+
# `ServerOperationError`. Match Thrift's contract so user code that
|
|
111
|
+
# catches `ServerOperationError` (a subclass of `DatabaseError`)
|
|
112
|
+
# works equivalently with `use_kernel=True`.
|
|
113
|
+
"SqlError": ServerOperationError,
|
|
114
|
+
"Unknown": DatabaseError,
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
def reraise_kernel_error(exc: "_kernel.KernelError") -> "Error":
|
|
119
|
+
"""Convert a ``databricks_sql_kernel.KernelError`` to a PEP 249
|
|
120
|
+
exception with the kernel's structured attributes forwarded onto
|
|
121
|
+
the new instance.
|
|
122
|
+
|
|
123
|
+
The returned exception is raised by callers with ``raise ... from
|
|
124
|
+
exc``; the ``from`` clause is what sets ``__cause__``, so we don't
|
|
125
|
+
touch it here.
|
|
126
|
+
"""
|
|
127
|
+
code = getattr(exc, "code", "Unknown")
|
|
128
|
+
cls = _CODE_TO_EXCEPTION.get(code, DatabaseError)
|
|
129
|
+
|
|
130
|
+
# For ServerOperationError, reproduce the Thrift backend's
|
|
131
|
+
# ``context`` dict so callers that read
|
|
132
|
+
# ``err.context["diagnostic-info"]`` (the Spark stack trace) /
|
|
133
|
+
# ``err.context["operation-id"]`` get the same shape on the kernel
|
|
134
|
+
# path. ``diagnostic_info`` is forwarded from the kernel error (it
|
|
135
|
+
# now crosses the PyO3 boundary; older wheels return ``None`` via
|
|
136
|
+
# ``getattr``, so this degrades gracefully). Matches
|
|
137
|
+
# thrift_backend.py's ServerOperationError construction.
|
|
138
|
+
context = None
|
|
139
|
+
if cls is ServerOperationError:
|
|
140
|
+
context = {
|
|
141
|
+
"operation-id": getattr(exc, "query_id", None),
|
|
142
|
+
"diagnostic-info": getattr(exc, "diagnostic_info", None),
|
|
143
|
+
}
|
|
144
|
+
new = cls(getattr(exc, "message", str(exc)), context)
|
|
145
|
+
|
|
146
|
+
for attr in (
|
|
147
|
+
"code",
|
|
148
|
+
"sql_state",
|
|
149
|
+
"error_code",
|
|
150
|
+
"vendor_code",
|
|
151
|
+
"http_status",
|
|
152
|
+
"retryable",
|
|
153
|
+
"query_id",
|
|
154
|
+
# Extended server status now forwarded across the PyO3 boundary
|
|
155
|
+
# (kernel #121). ``getattr(..., None)`` keeps this forward-safe
|
|
156
|
+
# against an older wheel that doesn't set these attrs.
|
|
157
|
+
"display_message",
|
|
158
|
+
"diagnostic_info",
|
|
159
|
+
"error_details_json",
|
|
160
|
+
):
|
|
161
|
+
setattr(new, attr, getattr(exc, attr, None))
|
|
162
|
+
return new
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
def wrap_kernel_exception(what: str, exc: BaseException) -> "Error":
|
|
166
|
+
"""Map any exception from a PyO3 call site to a PEP 249 exception.
|
|
167
|
+
|
|
168
|
+
- ``KernelError`` → mapped class with structured attrs forwarded.
|
|
169
|
+
- Already-PEP-249 ``Error`` (e.g. raised by an inner caller that
|
|
170
|
+
already mapped) → passed through unchanged.
|
|
171
|
+
- Anything else (``TypeError`` / ``ValueError`` / etc. from PyO3
|
|
172
|
+
argument conversion, extension-internal errors) → wrapped in
|
|
173
|
+
``OperationalError``.
|
|
174
|
+
|
|
175
|
+
Returned, not raised — the caller decides whether to ``raise``
|
|
176
|
+
or ``raise ... from exc``. ``what`` is a short tag (the calling
|
|
177
|
+
method name) used only in the ``OperationalError`` message.
|
|
178
|
+
"""
|
|
179
|
+
if isinstance(exc, _kernel.KernelError):
|
|
180
|
+
return reraise_kernel_error(exc)
|
|
181
|
+
if isinstance(exc, Error):
|
|
182
|
+
return exc
|
|
183
|
+
return OperationalError(
|
|
184
|
+
f"Unexpected error from databricks_sql_kernel during {what}: {exc!r}"
|
|
185
|
+
)
|