MyPy & PEP 702 (@deprecated)
I tend to use Python a lot, both in my job and personal projects. It’s a good language, but it still lacks some useful features. However, it seems that Python is evolving in a good direction. The recent PEP 695 signals a promising step forward in my opinion (despite it won’t probably be supported by MyPy any time soon). Alongside this progression, modern code checkers like linters (such as Ruff), formatters (like Black), and type checkers (including MyPy) have become indispensable allies to improve your codebase (and also save a tremendous amount of time by spotting mistakes before running any code).
For a long time, I’ve been looking for something that would help me to update deprecated code. This is very common for a lot of languages (like Java or Rust). Some solutions already exist for Python, like libraries such as deprecated or deprecation but they are not really suitable for what I want to achieve : they will only trigger a warning at runtime. Ideally, I would like to identify deprecated functions or classes usage when code is written. While linting tools like Ruff or PyLint provide some support, they rely on static rules that necessitate manual updates and only extend to explicitly declared libraries.
Back in the past, I’ve already build a custom checker for PyLint for this very purpose. However, since then, I’ve moved to Ruff which is way faster. Unfortunately, such approach cannot be replicated to Ruff as far as I’m aware of. Below is the snippet that used to serve me to identify deprecated code (inspired by this), but I wouldn’t recommend using it anymore.
| |
With the upcoming PEP 702 (which is already backported to previous versions of Python with the typing-extensions package), things are probably going to get more serious. As it’s now part of the standard library, we can expect modern type checkers to support it. As far as I know, it’s currently only supported by Pyright (and thus Pylance). I’ve seen some related code in the MyPy source code, but it doesn’t seem to be implemented yet (and worst case, it is still a fun exercise to dive a bit into the MyPy internals).
The solution I went for is to write a custom MyPy plugin to address this issue. If you want to try it for yourself, I’ve bundled it in a library. The source code is available on GitHub. You can install it directly from PyPI.
pip install mypyppDon’t forget to configure MyPy to use it. For example, within a pyproject.toml file.
| |
deprecated decorator. Other libraries such as deprecated or deprecation are not supported. It won’t be too difficult to implement though.I’ve been testing it with Python 3.12 on the following files, and it appears to work like a charm.
| |
| |
When running MyPy, I correctly get a deprecation error on each deprecated code usage. It also directly integrates in your editor (such as VSCode) if it’s configured properly. Pretty neat.
ben@localhost:~ $ mypy .
definitions.py:33: error: The function "function" is deprecated : This function shouldn't be used. [deprecated]
definitions.py:35: error: The class "Class" is deprecated : This class shouldn't be used. [deprecated]
definitions.py:37: error: The method "Test.instance_method" is deprecated : This method shouldn't be used. [deprecated]
definitions.py:38: error: The method "Test.class_method" is deprecated : This method shouldn't be used. [deprecated]
definitions.py:39: error: The method "Test.static_method" is deprecated : This method shouldn't be used. [deprecated]
definitions.py:41: error: The method "Test.class_method" is deprecated : This method shouldn't be used. [deprecated]
definitions.py:42: error: The method "Test.static_method" is deprecated : This method shouldn't be used. [deprecated]
external.py:7: error: The function "function" is deprecated : This function shouldn't be used. [deprecated]
external.py:9: error: The class "Class" is deprecated : This class shouldn't be used. [deprecated]
external.py:11: error: The method "Test.instance_method" is deprecated : This method shouldn't be used. [deprecated]
external.py:12: error: The method "Test.class_method" is deprecated : This method shouldn't be used. [deprecated]
external.py:13: error: The method "Test.static_method" is deprecated : This method shouldn't be used. [deprecated]
external.py:15: error: The method "Test.class_method" is deprecated : This method shouldn't be used. [deprecated]
external.py:16: error: The method "Test.static_method" is deprecated : This method shouldn't be used. [deprecated]
Found 14 errors in 2 files (checked 2 source files)I think it’s a good starting point. I hope this will soon be integrated into MyPy. I’m pretty sure that this plugin can be improved as I don’t really have a deep knowledge about all the MyPy internals. At the moment, it is unable to flag imports of deprecated class or function (which Pylance manages to do), so there’s definitely room for improvement.