Skip to content

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
jdkandersson:masterfrom
Shaun-3adesign:modernize/sqlalchemy2-flask3-python39
Open

Modernize dependencies for SQLAlchemy 2.x, Flask 3.x, and Python 3.9-3.13#451
Shaun-3adesign wants to merge 1 commit into
jdkandersson:masterfrom
Shaun-3adesign:modernize/sqlalchemy2-flask3-python39

Conversation

@Shaun-3adesign

Copy link
Copy Markdown

Summary

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: clean
  • bandit -r open_alchemy -s B101,B303,B310: clean
  • pylint open_alchemy: 9.86/10, 0 errors
  • Manual smoke test: generated a model from a spec via open_alchemy.init_model_factory and ran a full CRUD cycle against Flask 3.1 + Flask-SQLAlchemy 3.1 + SQLAlchemy 2.0

…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.
@Shaun-3adesign

Shaun-3adesign commented Jul 3, 2026

Copy link
Copy Markdown
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.

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