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 autotyping
python -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-> None
return 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: bool
annotation to any function parameter with a default ofTrue
orFalse
-
--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.Uid
to annotate anyuid
in your codebase with aNone
default asOptional[my_types.Uid]
. -
--annotate-named-param foo:bar.Baz
: annotate any parameter with no default that is namedfoo
withbar.Baz
. For example, use--annotate-named-param uid:my_types.Uid
to annotate anyuid
parameter 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 averbose
parameter 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.Iterator
return 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_type
andsuggested_return_type
codes 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:
-
--safe
enables changes that should always be safe. This includes--none-return
,--scalar-return
, and--annotate-magics
. -
--aggressive
enables riskier changes that are more likely to produce new type checker errors. It includes all of--safe
as 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.yaml
with'autotyping'
in themodules
list. For an example, see the.libcst.codemod.yaml
in 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
autotyping
as 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.yaml
file at the root of your repository and add in:
- repos:
- repo: https://github.com/JelleZijlstra/autotyping
rev: 24.9.0
hooks:
- id: autotyping
stages: [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
None
returns
- 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 autotyping
to 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
--safe
and--aggressive
flags so they don't take ignored arguments -
--length-hint
should returnint
(contributed by Nikita Sobolev) - Fix bug in import adding (contributed by Shantanu)
- Add
--safe
and--aggressive
- Add
--pyanalyze-report
- Do not add
None
return types to methods marked with@abstractmethod
and to methods in stub files - Improve type inference:
-
"string" % ...
is alwaysstr
-
b"bytes" % ...
is alwaysbytes
- An
and
oror
operator where left and right sides are of the same type returns that type -
is
,is not
,in
, andnot in
always returnbool
-
- Initial PyPI release