When I refactor code I often find myself tediously adding type annotations that are obvious from context: functions that don't return anything, boolean flags, etcetera. That's where autotyping comes in: it automatically adds those types and inserts the right annotations.
Autotyping can be called directly from the CLI, be used as a pre-commit hook or run via the libcst interface as a codemod.
Here's how to use it from the CLI:
pip install autotypingpython -m autotyping /path/to/my/code
By default it does nothing; you have to add flags to make it do more transformations. The following are supported:
- Annotating return types:
--none-return: add a-> Nonereturn type to functions without any return, yield, or raise in their body--scalar-return: add a return annotation to functions that only return literal bool, str, bytes, int, or float objects.
- Annotating parameter types:
--bool-param: add a: boolannotation to any function parameter with a default ofTrueorFalse--int-param,--float-param,--str-param,--bytes-param: add an annotation to any parameter for which the default is a literal int, float, str, or bytes object--annotate-optional foo:bar.Baz: for any parameter of the formfoo=None, addBaz, imported frombar, as the type. For example, use--annotate-optional uid:my_types.Uidto annotate anyuidin your codebase with aNonedefault asOptional[my_types.Uid].--annotate-named-param foo:bar.Baz: annotate any parameter with no default that is namedfoowithbar.Baz. For example, use--annotate-named-param uid:my_types.Uidto annotate anyuidparameter in your codebase with no default asmy_types.Uid.--guess-common-names: infer certain parameter types from their names based on common patterns in open-source Python code. For example, infer that averboseparameter is of typebool.
- Annotating magical methods:
--annotate-magics: add type annotation to certain magic methods. Currently this does the following:__str__returnsstr__repr__returnsstr__len__returnsint__length_hint__returnsint__init__returnsNone__del__returnsNone__bool__returnsbool__bytes__returnsbytes__format__returnsstr__contains__returnsbool__complex__returnscomplex__int__returnsint__float__returnsfloat__index__returnsint__exit__: the three parameters areOptional[Type[BaseException]],Optional[BaseException], andOptional[TracebackType]__aexit__: same as__exit__
--annotate-imprecise-magics: add imprecise type annotations for some additional magic methods. Currently this addstyping.Iteratorreturn annotations to__iter__,__await__, and__reversed__. These annotations should have a generic parameter to indicate what you're iterating over, but that's too hard for autotyping to figure out.
- External integrations
--pyanalyze-report: takes types suggested by pyanalyze'ssuggested_parameter_typeandsuggested_return_typecodes and applies them. You can generate these with a command like:pyanalyze --json-output failures.json -e suggested_return_type -e suggested_parameter_type -v .--only-without-imports: only apply pyanalyze suggestions that do not require new imports. This is useful because suggestions that require imports may need more manual work.
There are two shortcut flags to enable multiple transformations at once:
--safeenables changes that should always be safe. This includes--none-return,--scalar-return, and--annotate-magics.--aggressiveenables riskier changes that are more likely to produce new type checker errors. It includes all of--safeas well as--bool-param,--int-param,--float-param,--str-param,--bytes-param, and--annotate-imprecise-magics.
Autotyping is built as a LibCST codemod; see the LibCST documentation for more information on how to use codemods.
If you wish to run things through the libcst.tool interface, you can do this like so:
- Make sure you have a
.libcst.codemod.yamlwith'autotyping'in themoduleslist. For an example, see the.libcst.codemod.yamlin this repo. - Run
python -m libcst.tool codemod autotyping.AutotypeCommand /path/to/my/code
Pre-commit hooks are scripts that runs automatically before a commit is made, which makes them really handy for checking and enforcing code-formatting (or in this case, typing)
- To add
autotypingas a pre-commit hook, you will first need to install pre-commit if you haven't already:
pip install pre-commit
- After that, create or update the
.pre-commit-config.yamlfile at the root of your repository and add in:
- repos:
- repo: https://github.com/JelleZijlstra/autotyping
rev: 24.9.0
hooks:
- id: autotyping
stages: [pre-commit]
types: [python]
args: [--safe] # or alternatively, --aggressive, or any of the other flags mentioned above - Finally, run the following command to install the pre-commit hook in your repository:
pre-commit install
Now whenever you commit changes, autotyping will automatically add type annotations to your code!
Autotyping is intended to be a simple tool that uses heuristics to find annotations that would be tedious to add by hand. The heuristics may fail, and after you run autotyping you should run a type checker to verify that the types it added are correct.
Known limitations:
- autotyping does not model code flow through a function, so it may miss
implicit
Nonereturns
- Add pre-commit support. (Thanks to Akshit Tyagi and Matthew Akram.)
- Add missing dependency. (Thanks to Stefane Fermigier.)
- Add simpler ways to invoke autotyping. Now, it is possible to simply use
python3 -m autotypingto invoke the tool. (Thanks to Shantanu Jain.) - Drop support for Python 3.7; add support for Python 3.12. (Thanks to Hugo van Kemenade.)
- Infer return types for some more magic methods. (Thanks to Dhruv Manilawala.)
- Fix crash on certain argument names like
iterables(contributed by Marco Gorelli)
- Add
--guess-common-names(contributed by John Litborn) - Fix the
--safeand--aggressiveflags so they don't take ignored arguments --length-hintshould returnint(contributed by Nikita Sobolev)- Fix bug in import adding (contributed by Shantanu)
- Add
--safeand--aggressive - Add
--pyanalyze-report - Do not add
Nonereturn types to methods marked with@abstractmethodand to methods in stub files - Improve type inference:
"string" % ...is alwaysstrb"bytes" % ...is alwaysbytes- An
andororoperator where left and right sides are of the same type returns that type is,is not,in, andnot inalways returnbool
- Initial PyPI release