Modernize dependencies for SQLAlchemy 2.x, Flask 3.x, and Python 3.9-3.13#451
Open
Shaun-3adesign wants to merge 1 commit into
Open
Conversation
…3.13
The library was pinned to SQLAlchemy ^1.0 and Flask-SQLAlchemy ^2, hadn't
been maintained since ~2023, and CI only tested Python 3.7-3.9. This brings
the dependency baseline and test/tooling stack up to date without touching
public API behavior, as groundwork before tackling the 18 open upstream
issues in a later pass.
Dependencies (pyproject.toml, poetry.lock):
- SQLAlchemy ^1.0 -> ^2.0, python floor ^3.7 -> ^3.9 (3.7/3.8 are EOL)
- Dropped abandoned sqlalchemy-stubs (conflicts with SQLAlchemy 2.0's native
typing) and pytest-flake8 (unmaintained, breaks collection under pytest 7+)
- Bumped all dev dependencies to current majors (Flask-SQLAlchemy ^3, pytest
^8, typeguard ^4, mypy/pylint/black/isort/bandit, etc.)
- Added setuptools/wheel as an explicit optional extra: modern environments
no longer guarantee setuptools is present, and the CLI's package-build
feature shells out to setup.py, which needs it
- Added types-jsonschema/types-PyYAML stub packages, standalone flake8 dev
dependency (previously only available via the now-dropped pytest-flake8)
Source fixes:
- open_alchemy/__init__.py: moved declarative_base off the deprecated
sqlalchemy.ext.declarative import path (removed in SQLAlchemy 2.0's
intended usage, was only a deprecation warning so far)
- open_alchemy/types.py: fixed a real bug where Python 3.11+ changed
str(SomeStrEnum.MEMBER) to render "ClassName.MEMBER" instead of the
enum's value, corrupting any error message or extension-key lookup that
formatted one of these enums; added a shared _StrEnum mixin with an
explicit __str__. Also removed Literal[...] annotations on enum members,
which mypy now rejects outright and nothing downstream relied on.
- open_alchemy/helpers/ext_prop/__init__.py: fixed a latent bug where
looking up an unrecognized custom x- extension property passed
schema=None into jsonschema.validate(), which raises TypeError on modern
jsonschema (previously silently accepted). Now skips validation when no
schema is registered for the property name.
- open_alchemy/schemas/helpers/association.py,
open_alchemy/schemas/validation/property_/backref.py: minor type-
correctness fixes surfaced by mypy (dict key typing, map/filter
reassignment)
- open_alchemy/build/__init__.py: added usedforsecurity=False to the sha1
cache-key hash (non-cryptographic use, flagged by newer bandit)
Test/tooling fixes:
- Replaced Engine.execute() (removed in SQLAlchemy 2.0) with
connection.execute(text(...)) in integration and example tests
- Updated tests/open_alchemy/conftest.py to mock open_alchemy.declarative_base
directly instead of the now-unused sqlalchemy.ext.declarative module
- Updated SQLAlchemy relationship introspection tests for 2.0's
_RelationshipDeclared/_init_args API (unset kwargs now raise AttributeError
instead of returning None on the raw, unmapped relationship() object)
- Fixed typeguard.check_type() 2-arg call signature (typeguard 4.x dropped
the leading name argument)
- Updated mypy revealed-type assertions in test_generate_type_check to match
current mypy output formatting; documented in MODERNIZATION_PLAN.md that
legacy Column(...)-style models now lose instance-level type narrowing
without a mypy plugin, which sqlalchemy-stubs previously provided - flagged
as a follow-up issue, not fixed here
- Fixed two test_build.py fixtures using a non-PEP-440 version string
("version 1"), which modern setuptools now rejects with InvalidVersion
- Rewrote test_build_dist_wheel_import_error to monkeypatch the command
runner instead of uninstalling/reinstalling the wheel package against the
real dev environment - modern setuptools vendors bdist_wheel itself, so
the old approach no longer reproduces the failure it was testing for
- Added tests/open_alchemy/helpers/test_command.py and a schema=None
regression case in test_get_ext_prop.py to keep the project's 100%
coverage gate green after the above fixes
- Removed --flake8/--strict from pytest addopts (pytest-flake8 is dropped);
moved flake8 config to a standalone [flake8] section in setup.cfg
- Reformatted the codebase with the bumped black/isort versions
CI and lint config:
- .github/workflows/*.yaml: Python matrix -> 3.9-3.13, all GitHub Actions
bumped to current majors, poetry install now uses --all-extras
- .pre-commit-config.yaml: bumped black/isort/mypy/pylint hook revisions,
added a standalone flake8 hook, dropped the sqlalchemy-stubs mypy
dependency
- .pylintrc: removed dead/deprecated options (optimize-ast, files-output,
no-space-check, a hardcoded init-hook path from the original maintainer's
machine, the removed bad-continuation disable) that were spamming
unrecognized-option/useless-option-value warnings on every file
Verified: 3023 tests passing (was 322 failing before this change), 100%
coverage, mypy/flake8/black/isort/bandit/pylint all clean, and a manual
end-to-end smoke test (spec -> generated model -> CRUD) against Flask 3.1 +
Flask-SQLAlchemy 3.1 + SQLAlchemy 2.0.
Author
|
@jdkandersson wanted to flag this for review when you have a moment. This modernizes the dependency baseline (SQLAlchemy 2.x, Python 3.9-3.13, current majors for the rest of the dev toolchain) and fixes the real breakage that came with it — a couple of latent bugs included (see the PR description for details). Full test suite is green with 100% coverage, and mypy/flake8/black/isort/bandit/pylint are all clean. Once this lands I'd like to start looking at the Connexion 3 migration and a few of the open bugs, but wanted to get the baseline more stable/green first before building on top of it. Happy to split this into smaller PRs or adjust the approach if you'd prefer a different sequencing — just let me know. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
^1.0->^2.0, Python floor^3.7->^3.9, and all dev dependencies (Flask-SQLAlchemy, pytest, typeguard, mypy, pylint, black, isort, bandit, etc.) to current majors; drop the abandonedsqlalchemy-stubsand unmaintainedpytest-flake8.declarative_baseimport path, a Python 3.11+ str-enum formatting bug that corrupted error messages, and a latent bug where an unrecognized customx-extension property passedschema=Noneintojsonschema.validate()(raisesTypeErroron modern jsonschema).typeguard4.x's signature change, modernsetuptools/mypyoutput changes, and refresh CI workflows (Python 3.9–3.13 matrix, current GitHub Actions versions).Test plan
pytest: 3023 passed, 13 pre-existing xfailed, 100% coverage (project's CI gate)mypy open_alchemy: clean (0 errors, was 39 before)flake8,black --check,isort --check-only: cleanbandit -r open_alchemy -s B101,B303,B310: cleanpylint open_alchemy: 9.86/10, 0 errorsopen_alchemy.init_model_factoryand ran a full CRUD cycle against Flask 3.1 + Flask-SQLAlchemy 3.1 + SQLAlchemy 2.0