Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do you check if a typeshed stub (.pyi) file matches the implementation?

Tags:

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?

like image 310
Nick T Avatar asked Aug 06 '18 21:08

Nick T


People also ask

What is a .PYI file?

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.

What are MYPY stubs?

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.

What is a stubbed file?

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.

What are Python stub files?

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.


1 Answers

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.

like image 157
pelson Avatar answered Sep 28 '22 18:09

pelson