You can use mypy's stubgen.py
or some other tools to generate .pyi
files automatically, but stubgen contains the additional suggestion to check the stub files for accuracy.
If I autogenerated stubs then modified them to fix errors or make things more stringent that weren't picked up on by the generator, how can I then check that they agree with the implementation? Or as the implementation changes and I had hand-generated files, how can I make sure they stay in sync?
A PYI file is a Python Interface Definition file that contains code stub reference for implementation of the interface. Each Python module is represented as a . pyi stub which is a normal Python file but with empty methods. The syntax of PYI files is the same as that of a regular Python module.
Mypy uses stub files stored in the typeshed repository to determine the types of standard library and third-party library functions, classes, and other definitions. You can also create your own stubs that will be used to type check your code.
A stub file is a computer file that appears to the user to be on disk and immediately available for use, but is actually held either in part or entirely on a different storage medium.
A type stub is a file with a .pyi extension that describe a module's types while omitting implementation details. For example, if a module foo has the following source code: class Foo: CONSTANT = 42 def do_foo(x): return x. then foo.
I found this question as a result of needing to do this too, so here is what I've subsequently found...
Apparently, a tool called stubtest was recently added to mypy (undocumented currently) which will validate the stubs against the implementation insofar as is possible. Here is a contrived example:
$ cat <<EOF > simple.py
> def thing(obj):
> if isinstance(obj, float):
> return 0.0
> elif isinstance(obj, int):
> return 0
> else:
> raise TypeError("Unsupported type passed to thing.")
>
> EOF
I create a stubfile which uses knowledge of the implementation to write a specific type annotation (definitely not auto-generated - that would be hard to do!):
$ cat <<EOF > simple.pyi
> from typing import Any, Union
>
> def thing(obj: Union[float, int]): ...
>
> EOF
Running stubtest yeilds no output, and a zero exit code:
$ python -m mypy.stubtest simple
$ echo $?
0
Now I update the implementation to have another argument:
cat <<EOF > simple.py
> def thing(obj, something_else):
> if isinstance(obj, float):
> return 0.0
> elif isinstance(obj, int):
> return 0
> else:
> raise TypeError("Unsupported type passed to thing.")
>
> EOF
And re-run the stubtest:
$ python -m mypy.stubtest simple
error: simple.thing is inconsistent, stub does not have argument "something_else"
Stub: at line 3
def (obj: Union[builtins.float, builtins.int]) -> Any
Runtime: at line 1 in file stub-testing/simple.py
def (obj, something_else)
$ echo $?
1
I'm still fairly weak in my understanding of the mypy search path, and couldn't get the direct stubtest
entrypoint to work (as opposed to the python -m mypy.stubtest
form).
I didn't yet test this against anything more complex than this toy example - I suspect there is a lot of devil in the detail.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With