Skip to content

feat: support python 3.9+#36

Merged
jy-tan merged 12 commits intomainfrom
support-py39
Jan 16, 2026
Merged

feat: support python 3.9+#36
jy-tan merged 12 commits intomainfrom
support-py39

Conversation

@jy-tan
Copy link
Contributor

@jy-tan jy-tan commented Jan 15, 2026

Summary

Relaxes the Python version requirement from 3.12+ to 3.9+, significantly expanding the SDK's compatibility with existing Python projects.

Changes

  • Lower requires-python from >=3.12 to >=3.9
  • Add typing_extensions>=4.4.0 dependency for backported typing features
  • Replace PEP 695 type parameter syntax (def f[T]()) with classic TypeVar
  • Replace typing.override with typing_extensions.override (8 files)
  • Replace datetime.UTC with datetime.timezone.utc (Python 3.11+ feature)
  • Replace X | Y union syntax with Union[X, Y] in type aliases
  • Replace zip(..., strict=True) with explicit length check in psycopg2 (Python 3.10+ feature)
  • Lower Django optional dependency in pyproject.toml from >=5.0 to >=4.2
  • Add from __future__ import annotations to files using X | Y syntax in type hints
  • Update ruff/ty target versions to Python 3.9
  • Update README.md requirements to Python 3.9+
  • Update Dockerfile.base and Django e2e test requirements to Python 3.9 / Django 4.2
  • Update CI workflows:
    • Run lint/typecheck/build on Python 3.9
    • Run unit tests on Python 3.9 and 3.14 (matrix)
    • Run e2e tests on Python 3.9
  • Fix asyncio.TimeoutError handling in tests for Python 3.9 compatibility
  • psycopg instrumentation fix: Replace cursor.results() with cursor.nextset() iteration for capturing executemany(..., returning=True) result sets (psycopg 3.3+ feature not available in psycopg 3.2.x used by Python 3.9)

Other fixes:

  • Fix app stdout/stderr capture in e2e test runner to use temporary file instead of DEVNULL, enabling diagnostic output when app fails to start

Why not 3.8 or earlier?

Core dependencies (protobuf>=6.0, opentelemetry-api) require Python 3.9+, and built-in generic types (list[...], dict[...]) used throughout the codebase require 3.9+.

Testing

  • All unit tests pass on Python 3.9 and 3.14 (192 tests)
  • All e2e tests pass on Python 3.9 (10 instrumentation suites)
  • Type checker (ty check) passes
  • Linter (ruff check) passes

Copy link

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1 issue found across 15 files

Prompt for AI agents (all issues)

Check if these issues are valid — if so, understand the root cause of each and fix them.


<file name="drift/instrumentation/psycopg2/instrumentation.py">

<violation number="1" location="drift/instrumentation/psycopg2/instrumentation.py:846">
P2: Removing `strict=True` from `zip(column_names, row)` now hides row/column length mismatches, potentially returning truncated or misaligned dict rows instead of raising an error. Add an explicit length check before zipping to preserve the previous safety.</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

@sohil-kshirsagar
Copy link
Contributor

@podocarp was there any other reason why we were only supporting 3.12+?

Copy link

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

8 issues found across 28 files (changes from recent commits).

Prompt for AI agents (all issues)

Check if these issues are valid — if so, understand the root cause of each and fix them.


<file name="drift/core/communication/communicator.py">

<violation number="1">
P2: Don’t gate cleanup on background thread liveness; always remove the pre-registered routing entries on failure to avoid stale events/data if the thread dies mid-request.</violation>

<violation number="2">
P2: Unblock the background reader before joining (e.g., socket.shutdown) so cleanup reliably stops the thread instead of timing out while it’s stuck in recv().</violation>

<violation number="3">
P2: Avoid `except Exception: continue` in the background read loop; break (or at least handle OSError explicitly) on non-timeout socket errors to prevent a busy loop and hidden failures.</violation>
</file>

<file name="drift/instrumentation/django/e2e-tests/src/test_requests.py">

<violation number="1">
P2: Importing the shared `make_request` helper fixes the timeout/logging duplication but it also hardcodes the base URL to `http://localhost:8000`, so this script no longer honors the `PORT` environment variable that the Django app (and docs) use for running tests on custom ports. Requests sent while the app listens on any non-default port will now connect to the wrong port and fail.</violation>
</file>

<file name="drift/instrumentation/psycopg/e2e-tests/src/app.py">

<violation number="1">
P1: `datetime.UTC` is unavailable on Python 3.9, so importing it breaks the module on the newly supported versions. Use `datetime.timezone.utc` (or alias it) instead.</violation>
</file>

<file name="drift/instrumentation/fastapi/e2e-tests/src/test_requests.py">

<violation number="1">
P2: Importing the shared `make_request` function drops support for the `PORT` environment variable; `test_utils.make_request` hard-codes `http://localhost:8000`, so the FastAPI e2e tests now fail whenever the server listens on a different port. Consider restoring the `PORT` override in the shared utility before using it here.</violation>
</file>

<file name="drift/instrumentation/e2e_common/base_runner.py">

<violation number="1">
P2: App stdout/stderr are redirected to DEVNULL, so the later `communicate()` call can never surface logs when the app fails to start, leaving no diagnostics.</violation>
</file>

<file name="drift/instrumentation/utils/psycopg_utils.py">

<violation number="1">
P2: If psycopg’s Range class isn’t available, the fallback returns the raw `__range__` payload so the lower/upper bounds stay serialized (e.g., ISO strings) instead of being converted back to datetimes/decimals. This breaks the “deserialize to original Python types” contract and yields inconsistent behavior between environments with and without psycopg installed.</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

@jy-tan jy-tan merged commit 921352f into main Jan 16, 2026
17 checks passed
@jy-tan jy-tan deleted the support-py39 branch January 16, 2026 06:26
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants