Skip to content

Drop Python 3.10 support - #884

Merged
laughingman7743 merged 1 commit into
masterfrom
feat/864-drop-python-310
Sep 29, 2026
Merged

laughingman7743 merged 1 commit into
masterfrom
feat/864-drop-python-310

Conversation

@laughingman7743

@laughingman7743 laughingman7743 commented Sep 28, 2026 •

Copy link
Copy Markdown
Member

WHAT

Drop Python 3.10 support for 4.0.0.

  • requires-python = ">=3.11"; remove the 3.10 classifier, the py310 tox environment and [gh-actions] mapping, and 3.10 from the Test workflow's supported versions (PYTHON_VERSIONS), which the dispatch and Release runs use.
  • Remove 3.10-only compatibility code:
    • the tomli fallback in scripts/check_license_headers.py and the tomli dev dependency;
    • the _parse_datetime fallback in ARRAY timestamp decoding (pyathena/sqlalchemy/array.py); the helper is inlined as datetime.fromisoformat.
  • ruff target-version = "py311", with the fixes it now reports: UP017 (timezone.utc → datetime.UTC, 31 sites) and RUF100 (a PERF203 suppression that no longer applies, since the rule only targets Python < 3.11).
  • README.md and docs/introduction.md list CPython 3.11–3.14; the benchmark workspace member requires Python 3.11 and its packaging test expects Requires-Python: >=3.11.
  • uv.lock regenerated: the 3.10-only resolutions are gone (pandas 2.3.3, numpy 2.2.6, pytz, exceptiongroup, backports-asyncio-runner, and older sphinx/sphinx-design/myst-parser/networkx/markdown-it-py).

Release note (breaking): Python 3.10 is no longer supported. Users on Python 3.10 can stay on the 3.x maintenance branch.

WHY

Closes #864. Python 3.10 reaches end of life in October 2026 (PEP 619), and dropping it in the major release avoids a breaking change in a later 4.x minor release. It also unblocks #852: pandas 3.0 requires Python 3.11.

Python 3.11's datetime.fromisoformat accepts any number of fractional digits, truncating beyond microseconds as _parse_datetime does, so the fallback has no remaining input.

TEST

Tested commit: 6aeb2ae

  • just format, just lint (license headers, ruff, format, mypy, cfn-lint): pass.

  • just docs lint: pass. actionlint .github/workflows/test.yaml: pass.

  • Script tests (uv run --locked python -m pytest scripts/tests/): 108 passed.

  • just benchmark test: 108 passed, 1 skipped (packaging); the packaging test run separately after uv build with BENCHMARK_DIST_DIR: 1 passed. The built wheel metadata has Requires-Python: >=3.11 and classifiers 3.11–3.14.

  • datetime.fromisoformat on Python 3.11.11 and 3.14.5 with Athena TIMESTAMP text of 0–7, 9, and 12 fractional digits and the ISO T form: all parsed, truncated to microseconds.

  • tests/pyathena/sqlalchemy/test_array.py::…::test_array_result_conversion (includes 1- and 9-digit fractions and the T form) on Python 3.11 and 3.14 with --noconftest (offline): 12 passed each.

  • AWS on the new minimum: Test workflow dispatch 36452239325 on 6aeb2ae with python-versions=3.11: test, test-sqla, and test-sqla-async passed.

Not run locally: the AWS suites. The Ready PR run covers the newest Python (3.14).

🤖 Generated with Claude Code

Python 3.10 reaches end of life on 2026-10-31, and 4.0.0 drops it so a
later 4.x minor release does not have to.

- Require Python 3.11 and remove the 3.10 classifier, tox environment,
  and Test workflow version.
- Remove the tomli fallback from the license header check and the
  _parse_datetime fallback from ARRAY timestamp decoding; Python 3.11's
  datetime.fromisoformat accepts any number of fractional digits.
- Target Python 3.11 in ruff and apply its UP017 fixes (datetime.UTC)
  and the RUF100 fix for the PERF203 suppression, which no longer
  applies.
- Regenerate uv.lock, which no longer resolves pandas 2.3.3.

Closes #864

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
return Decimal(value) if type_.asdecimal else float(value)
if isinstance(type_, (types.DateTime, AthenaTimestamp)):
return value if isinstance(value, datetime) else _decode_datetime(value)
return value if isinstance(value, datetime) else datetime.fromisoformat(value)

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Self-review round one (implementation behavior): CLEAN

Scope: base a86a180ebbe5e18920cd75802ea5350ce8d33f10 .. head 6aeb2ae9967f378478737cf6bcd7113c3f4342b6, all 21 changed files.

Covered:

  • ARRAY timestamp decoding: on Python 3.11+ the old helper already returned datetime.fromisoformat(value) whenever it succeeded, so inlining only removes the branch that ran when fromisoformat raised. Checked on 3.11.11 and 3.14.5: text with 0–7, 9 and 12 fractional digits and the ISO T form parses and truncates to microseconds, like _parse_datetime. Text that neither accepts (e.g. a trailing UTC) still raises ValueError. _parse_datetime keeps its converter callers (pyathena/converter.py).
  • timezone.utc → UTC (UP017, 31 sites in library, scripts, tests, docs/conf.py): datetime.UTC is datetime.timezone.utc is True, so the cache-expiration comparison in pyathena/common.py/pyathena/aio/common.py and the UNLOAD location date in pyathena/formatter.py are unchanged.
  • RUF100 in pyathena/pandas/result_set.py:395: only a stale noqa: PERF203 (the rule targets Python < 3.11) is removed.
  • scripts/check_license_headers.py: tomllib is stdlib from 3.11; no other tomli users remain.
  • Test workflow: PYTHON_VERSIONS still matches the classifiers; the PR/schedule path (last) still selects 3.14, and the dispatch path now rejects 3.10 with the existing error.
  • No other sys.version_info gates or 3.10 comments remain (git grep). uv.lock has requires-python = ">=3.11", and only 3.10-only resolutions were removed.

Tests: the existing test_array_result_conversion cases (1- and 9-digit fractions, T form) pass offline on 3.11 and 3.14. The 12-digit case was only checked by hand.

Limitation: the AWS suites have not been run for this head.

Comment thread pyproject.toml
"python-dateutil",
]
requires-python = ">=3.10"
requires-python = ">=3.11"

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Self-review round two (claims, callers, operations): FINDINGS (PR description only, corrected)

Scope: base a86a180ebbe5e18920cd75802ea5350ce8d33f10 .. head 6aeb2ae9967f378478737cf6bcd7113c3f4342b6, the full PR body, commit message, and changed docs.

Claims checked:

  • "Python 3.10 reaches end of life on 2026-10-31": PEP 619 only gives "approximately October 2026". The PR body now says "in October 2026 (PEP 619)"; the commit message keeps the issue's date.
  • The uv.lock removal list, checked by diffing the locked package/version pairs: backports-asyncio-runner, exceptiongroup, markdown-it-py 3.0.0, myst-parser 4.0.1, networkx 3.4.2, numpy 2.2.6, pandas 2.3.3, pytz, sphinx 7.4.7, and sphinx-design 0.6.1. sphinx-design was missing from the description; added. tomli stays in the lock as a transitive dependency for python_full_version <= '3.11', not as our dev dependency.
  • "pandas 3.0 requires Python 3.11": pandas 3.0.0 release notes and the locked 3.0.6 metadata (Requires-Python >=3.11).
  • "fromisoformat accepts any number of fractional digits" (commit): matches the Python 3.11 datetime.fromisoformat docs and the 3.11.11/3.14.5 run recorded in round one.
  • "UP017 at 31 sites": ruff check --statistics reported 31 UP017 and 1 RUF100 before --fix.

Existing callers: Python 3.10 installers resolve to an earlier release through Requires-Python. pyathena.sqlalchemy.array._decode_datetime was added after v3.36.0 (git tag --contains 9a5ca5c is empty), so no released API disappears.

Operations: docs/testing.md stays accurate ("every supported Python version", dispatch example 3.11,3.14). The Release and full-dispatch runs now start 4 matrix jobs instead of 5, while PR/schedule runs still use 3.14 only, so AWS usage drops and nothing is added.

Evidence limits: all validation so far is local and offline. AWS behavior on 3.11 will come from a Test workflow dispatch with python-versions=3.11 before Ready.

# Every supported version, oldest first; keep in sync with the
# pyproject.toml classifiers.
PYTHON_VERSIONS: '["3.10", "3.11", "3.12", "3.13", "3.14"]'
PYTHON_VERSIONS: '["3.11", "3.12", "3.13", "3.14"]'

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Independent review (relayed): CLEAN (static)

Reviewer: OpenAI Codex CLI 0.157.1 (codex exec, model gpt-6-sol, sandbox read-only, ephemeral session 01a0e8db-e3d6-70d1-8e69-5b348cd195a7), which did not author the change.
Scope: base a86a180ebbe5e18920cd75802ea5350ce8d33f10 .. head 6aeb2ae9967f378478737cf6bcd7113c3f4342b6, in a clean detached snapshot without .env. The prompt omitted the PR number, description, commit message, and self-review findings. The reviewer was not allowed to edit, build, test, use the network, or write to GitHub.

Reviewer's result:

Surfaces covered: The specified diff; Python requirements, classifiers, Ruff and tox configuration; CI version selection for pull requests, schedules, dispatches, and releases; ARRAY timestamp decoding and its tests; UTC rewrites; repository-wide Python 3.10 references; docs, benchmarks, scripts, and uv.lock.

Verdict: CLEAN. I found no actionable regression or pre-existing issue in those surfaces. The remaining cp310-abi3 lock entries are wheels usable on supported Python versions, and tomli remains a transitive dependency on Python 3.11. This was a static review; no builds or tests were run.

After the review, the snapshot and PR worktree were unchanged (HEAD 6aeb2ae, clean status).

@laughingman7743
laughingman7743 marked this pull request as ready for review September 28, 2026 16:47
@laughingman7743
laughingman7743 merged commit 6effaa3 into master Sep 29, 2026
21 checks passed
@laughingman7743
laughingman7743 deleted the feat/864-drop-python-310 branch September 29, 2026 15:08
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.

Drop Python 3.10 support in 4.0.0

1 participant