Janis Lesinskis' Blog

Assorted ramblings

  • All entries
  • About me
  • Projects
  • Economics
  • Misc
  • Software-engineering
  • Sports

SQLAlchemy and mypy type checking


Have you ever spent some time looking to add type annotations to a Python package to test with mypy? Something I find to be a rather frequent frustration is finding the right type checking configuration and type stubs for external libraries.

In particular libraries that use extensive metaprogramming, as seen in many ORM libraries, tend to not play nicely with mypy. Recently I ran into this with SQLAlchemy. If you don't have the right plugin you see results from mypy like this:

$tox
py38 run-test: commands[0] | mypy --config-file mypy.ini example
example/shared/db/inventory.py:11: error: Module "sqlalchemy.orm" has no attribute "declarative_base"
example/shared/db/inventory.py:122: error: Variable "example.db.inventory.Base" is not valid as a type
example/shared/db/inventory.py:122: note: See https://mypy.readthedocs.io/en/stable/common_issues.html#variables-vs-type-aliases
example/shared/db/inventory.py:122: error: Invalid base class "Base"

These messages are obviously false positives because there's definitely a declarative_base provided by SQLAlchemy, just mypy doesn't understand it. You need to use a mypy plugin to get results that make sense. But which plugin and configuration to use? Much like how apps are getting worse search results are getting worse, when I searched up type stubs for various packages I found shockingly little of relevance. Adding quotation marks did little except add to the absurdity, as seen for example here:

Search results for shapley type stubs

This post is as much of a note to my future self as anything else. Mypy support landed natively in SQLAlchemy version 1.4 so the best way to deal with this is just to use what's provided by SQLAlchemy. To do this you need to change the installation of SQLAlchemy requirements to add in the optional mypy extra.

pip install sqlalchemy[mypy]

Then you need to set up your mypy to use the plugin. Then in mypy.ini you need to enable the plugin:

[mypy]
plugins = sqlalchemy.ext.mypy.plugin

This should then get you to the stage where you are getting real type checking results. You may need to add some additional annotations at this point to deal with some loose ends, see the SQLAlchemy mypy extension documentation for more info.

Published: Tue 10 August 2021
By Janis Lesinskis
In Software-engineering
Tags: python mypy tooling linting type-systems SQLAlchemy API-design static-analysis

links

  • JaggedVerge

social

  • My GitHub page
  • LinkedIn

Proudly powered by Pelican, which takes great advantage of Python.