From 558838d40263392d70977794bc048e83cc7dd7d8 Mon Sep 17 00:00:00 2001 From: Ashhar Hasan Date: Fri, 6 Mar 2026 20:10:08 +0530 Subject: [PATCH 1/3] Raise error when sending credentials over HTTP Aligns with the Java client behavior where TLS/SSL is required for authentication. The error message matches the Java client phrasing: "TLS/SSL is required for authentication." --- tests/unit/test_dbapi.py | 11 +++++++++++ trino/dbapi.py | 7 +++++++ 2 files changed, 18 insertions(+) diff --git a/tests/unit/test_dbapi.py b/tests/unit/test_dbapi.py index e3821bba..17e024df 100644 --- a/tests/unit/test_dbapi.py +++ b/tests/unit/test_dbapi.py @@ -18,6 +18,7 @@ from httpretty import httprettified from requests import Session +import trino.exceptions from tests.unit.oauth_test_utils import _get_token_requests from tests.unit.oauth_test_utils import _post_statement_requests from tests.unit.oauth_test_utils import GetTokenCallback @@ -27,6 +28,7 @@ from tests.unit.oauth_test_utils import SERVER_ADDRESS from tests.unit.oauth_test_utils import TOKEN_RESOURCE from trino import constants +from trino.auth import BasicAuthentication from trino.auth import OAuth2Authentication from trino.dbapi import connect from trino.dbapi import Connection @@ -362,3 +364,12 @@ def test_default_encoding_zstd(): def test_default_encoding_all(): connection = Connection("host", 8080, user="test") assert connection._client_session.encoding == ["json+zstd", "json+lz4", "json"] + + +def test_error_when_auth_over_http(): + with pytest.raises(trino.exceptions.TrinoAuthError, match="TLS/SSL is required for authentication"): + Connection("mytrinoserver.domain", http_scheme=constants.HTTP, auth=BasicAuthentication("u", "p")) + + +def test_no_error_when_auth_over_https(): + Connection("mytrinoserver.domain", http_scheme=constants.HTTPS, auth=BasicAuthentication("u", "p")) diff --git a/trino/dbapi.py b/trino/dbapi.py index 3749d797..24506004 100644 --- a/trino/dbapi.py +++ b/trino/dbapi.py @@ -217,6 +217,13 @@ def __init__( else: self.http_scheme = constants.HTTP + if auth is not None and self.http_scheme == constants.HTTP: + raise trino.exceptions.TrinoAuthError( + "TLS/SSL is required for authentication. " + "To use HTTPS, specify 'https://' in the host URL (which takes precedence " + "over http_scheme), or, if the host URL has no scheme, pass http_scheme='https'." + ) + # Infer connection port: `hostname` takes precedence over explicit `port` argument # If none is given, use default based on HTTP protocol default_port = constants.DEFAULT_TLS_PORT if self.http_scheme == constants.HTTPS else constants.DEFAULT_PORT From 194e757318d464c3769f13ce00fc2cde3825374e Mon Sep 17 00:00:00 2001 From: Ashhar Hasan Date: Fri, 10 Jul 2026 23:09:54 +0530 Subject: [PATCH 2/3] Fix line length --- .github/scripts/update_python_versions.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/scripts/update_python_versions.py b/.github/scripts/update_python_versions.py index f03d201a..752ef609 100644 --- a/.github/scripts/update_python_versions.py +++ b/.github/scripts/update_python_versions.py @@ -26,7 +26,7 @@ from typing import Any from typing import Callable -# From https://github.com/python/devguide/blob/8996911bc6f85f589efc3dc0111ba19351271553/_tools/generate_release_cycle.py#L44 +# From https://github.com/python/devguide/blob/8996911bc6f85f589efc3dc0111ba19351271553/_tools/generate_release_cycle.py#L44 # noqa: E501 RELEASE_CYCLE_URL = ( "https://peps.python.org/api/release-cycle.json" ) From a5a2cd9306282e9246e47e23a12b88de1f0daac9 Mon Sep 17 00:00:00 2001 From: Ashhar Hasan Date: Fri, 10 Jul 2026 23:10:45 +0530 Subject: [PATCH 3/3] Add CLAUDE.md with guidance for assistants --- CLAUDE.md | 67 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 67 insertions(+) create mode 100644 CLAUDE.md diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 00000000..6f17a41d --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,67 @@ +# CLAUDE.md + +## Common Commands + +```bash +# Setup +python3 -m venv .venv && . .venv/bin/activate +pip install -e '.[tests]' + +# Run unit tests +pytest tests/unit + +# Run a single test +pytest tests/unit/test_client.py::test_name -sv + +# Run integration tests (starts a Trino Docker container automatically, or uses an existing one on localhost:8080) +pytest tests/integration + +# Start a standalone Trino (+ LocalStack) dev server for manual testing, without pytest +python tests/development_server.py + +# Lint and format checks +pre-commit run --all-files + +# Type checking only +mypy trino/ + +# Run tests across all supported Python versions +tox +``` + +## Architecture + +### Package layout (`trino/`) + +- **`client.py`** - Core HTTP protocol implementation. `TrinoRequest` manages HTTP requests to the coordinator, `TrinoQuery` manages query lifecycle (submit -> poll -> fetch results), `ClientSession` holds connection state (headers, session properties, transaction ID). Implements the protocol described at https://github.com/trinodb/trino/wiki/HTTP-Protocol. +- **`dbapi.py`** - PEP 249 DBAPI 2.0 interface. `Connection` and `Cursor` classes that wrap `client.py`. Entry point is `trino.dbapi.connect()`. +- **`sqlalchemy/`** - SQLAlchemy dialect (`TrinoDialect`), compiler, and type mapping. Registered as `trino://` via `sqlalchemy.dialects` entry point. Compatible with SQLAlchemy 1.3.x, 1.4.x, and 2.0.x. +- **`auth.py`** - Authentication implementations (Basic, JWT, OAuth2, Kerberos/GSSAPI, Certificate). +- **`types.py`** - Python representations of Trino SQL types. +- **`mapper.py`** - Conversion between Trino wire types and Python objects. +- **`transaction.py`** - Transaction and isolation level management. +- **`exceptions.py`** - Exception hierarchy following DBAPI 2.0 spec. +- **`constants.py`** - Protocol header names and type metadata constants. +- **`logging.py`** - Logger factory used across the package. + +### Tests (`tests/`) + +- `tests/unit/` - No external dependencies needed. Uses `httpretty` for HTTP mocking. +- `tests/integration/` - Requires Docker. Tests automatically pull `trinodb/trino` image and start a container (or reuse one on port 8080). + - Env vars: `TRINO_VERSION` (image tag, default `latest`), `TRINO_RUNNING_HOST`/`TRINO_RUNNING_PORT` (use an existing server instead of starting one). + - When `TRINO_VERSION` is `latest` or >= 466, a LocalStack container with an S3 `spooling` bucket is also started to test the spooled client protocol; older versions use the `etc/*-pre-466*` configs. + +### Key dependencies + +- `requests` - HTTP transport +- `orjson` (CPython) / `json` (PyPy) - JSON parsing, selected at import time in `client.py` +- `lz4`, `zstandard` - Response decompression +- `python-dateutil`, `pytz`, `tzlocal` - Timezone/datetime handling + +## Code Style + +- **Max line length**: 120 characters (flake8) +- **Import ordering**: managed by `reorder-python-imports` (Python 3.9+ style) +- **Type checking**: mypy with strict settings, though `tests/*`, `trino/client`, `trino/dbapi`, and `trino/sqlalchemy.*` have `ignore_errors = true` +- **No mocking libraries**: write mocks by hand instead of using `unittest.mock` or similar. The project uses `httpretty` for HTTP-level stubbing only. +- **Pre-commit hooks**: flake8, mypy, reorder-python-imports, trailing whitespace, EOF newlines, YAML syntax, case-conflict checks