Skip to content

fix: restore Python 3.12/3.13 compatibility so import raja works on the declared minimum - #82

Open
eastagiletracker wants to merge 1 commit into
quiltdata:mainfrom
eastagiletracker:agile-board/python-312-compat
Open

fix: restore Python 3.12/3.13 compatibility so import raja works on the declared minimum#82
eastagiletracker wants to merge 1 commit into
quiltdata:mainfrom
eastagiletracker:agile-board/python-312-compat

Conversation

@eastagiletracker

@eastagiletracker eastagiletracker commented Aug 13, 2026

Copy link
Copy Markdown

This PR proposes restoring Python 3.12/3.13 compatibility, so that import raja works on the interpreters requires-python declares. We include this PR work along with a full history of your repo at https://eastagiletracker.com/projects/342. You can sign in with your GitHub ID to claim ownership of the project.

What is broken

pyproject.toml declares requires-python = ">=3.12", and the CI workflow's unit-test matrix lists python-version: ['3.12', '3.13']. On both of those interpreters the package cannot be imported at all: eight except clauses use the unparenthesized multi-exception form (except ClientError, BotoCoreError:), which is PEP 758 syntax and only parses on Python 3.14. Four of them are in code you ship — src/raja/scope.py, src/raja/datazone/service.py — and the rest are in lambda_handlers/rale_authorizer/handler.py and lambda_handlers/rale_router/handler.py. Because src/raja/__init__.py re-exports parse_scope/is_subset through enforcer.py, the very first line of any user's script fails.

Reproduced on main at e7aa23b:

$ uv build --wheel
Successfully built dist/raja-1.3.2-py3-none-any.whl
$ uv venv --python 3.12 /tmp/raja312 && uv pip install --python /tmp/raja312/bin/python dist/raja-1.3.2-py3-none-any.whl
$ /tmp/raja312/bin/python -c "import raja"
Traceback (most recent call last):
  File "<string>", line 1, in <module>
  File "/tmp/raja312/lib/python3.12/site-packages/raja/__init__.py", line 1, in <module>
    from .enforcer import (
  File "/tmp/raja312/lib/python3.12/site-packages/raja/enforcer.py", line 17, in <module>
    from .scope import format_scope, parse_scope
  File "/tmp/raja312/lib/python3.12/site-packages/raja/scope.py", line 77
    except ScopeParseError, ScopeValidationError:
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
SyntaxError: multiple exception types must be parenthesized

The same thing happens to the test suite: uv run --python 3.12 --extra test python -m pytest tests/unit/ on main collects nothing and dies at the import above.

Two things hide this today. The CI matrix does not actually vary the interpreter — actions/setup-python installs 3.12/3.13, but uv sync then builds the environment from .python-version, which has been 3.14 since #58, so all four matrix jobs run 3.14 and pass. And [tool.ruff] target-version = "py314", set while requires-python is >=3.12, means ruff format actively rewrites the parenthesized form back to the 3.14-only one — running ./poe check on a corrected file undoes the correction.

The change

Parenthesizing the exception tuples is semantically identical on 3.14 and restores parsing on 3.12/3.13, so no runtime behaviour changes on any supported interpreter. I also dropped the explicit target-version from [tool.ruff]; ruff then infers it from project.requires-python, so the formatter can never again emit syntax newer than what you support, and the setting follows automatically if you raise the floor later. tests/unit/test_python_floor.py reads the minimum version out of pyproject.toml and parses every module under src/ and lambda_handlers/ at that version, so the floor is enforced regardless of which interpreter the suite happens to run on.

Verified before and after, from a clean checkout:

  • The new test fails on the unfixed tree, naming all four modules, and passes after — pytest tests/unit/test_python_floor.py.
  • On Python 3.12, uv run --python 3.12 --extra test python -m pytest tests/unit/ goes from a collection failure to 266 passed.
  • On 3.14 (your .python-version), pytest tests/unit/ goes from 264 passed to 266 passed — the two additions, no other movement.
  • ruff check . reports the same four pre-existing findings under scripts/ as before, and ruff format --check . names exactly the same eight files as before this change (all under scripts/, none touched here). mypy src/raja remains clean.
  • Exercised the changed re-raise path on 3.12 against the rebuilt wheel: is_subset(scope, ["not-a-scope"]) still raises ScopeParseError, and exact/miss subset checks still return True/False.

Two related things I deliberately left alone, so this stays one change. [tool.mypy] python_version = "3.14" is the same mismatch as the ruff setting; I checked that python_version = "3.12" is clean today, and I am happy to include that line if you want it. And making the CI matrix real — passing the action's python-version input through to uv sync --python in .github/actions/setup-raja — would catch this class in CI, but two dependency PRs are already open against that file, so I left it to you.

How this was managed

I imported your issues and pull requests into an agile board — 79 stories — and tracked this work on it as Restore Python 3.12/3.13 compatibility, on the board at https://eastagiletracker.com/projects/342.

board

If you'd rather not receive contributions like this, reply no-more-prs on this pull request and we won't open any further ones on your repositories.


Lawrence W. Sinclair
CEO / East Agile
linkedin.com/in/lwsinclair/
eastagile.com

Greptile Summary

This PR restores compatibility with the declared Python 3.12 minimum and adds a regression test for unsupported syntax.

  • Parenthesizes multi-exception handlers in the packaged library and Lambda handlers.
  • Lets Ruff infer its target from project.requires-python.
  • Parses shipped library and Lambda sources against the declared Python floor during unit tests.

Confidence Score: 5/5

The PR appears safe to merge, with the compatibility fixes preserving existing exception-handling semantics and the new test covering all shipped Python sources.

The parenthesized handlers are valid across all declared interpreters, current Ruff invocations correctly infer Python 3.12 from the project metadata, and no concrete blocking or non-blocking defect remains.

Important Files Changed

Filename Overview
src/raja/scope.py Parenthesizes the exception tuple so the module parses on Python 3.12 and 3.13 without changing exception handling.
src/raja/datazone/service.py Converts three multi-exception handlers to syntax supported by the declared Python floor.
lambda_handlers/rale_authorizer/handler.py Makes authorization-service and secret-loading exception handlers parse on supported Lambda interpreters.
lambda_handlers/rale_router/handler.py Makes S3, secret-loading, and token-validation exception handlers compatible with Python 3.12 and 3.13.
pyproject.toml Removes the conflicting Python 3.14 Ruff target so the locked Ruff version infers Python 3.12 from project metadata.
tests/unit/test_python_floor.py Adds coverage that parses all shipped package and Lambda modules using the declared minimum grammar and validates the relevant feature-version gate.

Reviews (1): Last reviewed commit: "fix: restore Python 3.12/3.13 compatibil..." | Re-trigger Greptile

pyproject.toml declares requires-python = ">=3.12" and CI runs a 3.12/3.13
matrix, but eight except clauses used the unparenthesized multi-exception
form (PEP 758), which only parses on 3.14. Installing the wheel on 3.12
raised SyntaxError from `import raja`, via enforcer.py -> scope.py.

Parenthesize the exception tuples (identical semantics on 3.14) in the
library and in both Lambda handlers, and let ruff infer target-version from
requires-python so `poe check` stops reformatting them back to the 3.14-only
form.

Add tests/unit/test_python_floor.py, which parses every module under src/
and lambda_handlers/ at the declared minimum version, so the floor is
enforced on whichever interpreter the suite happens to run on.
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.

1 participant