| .. _stubtest: |
| |
| .. program:: stubtest |
| |
| Automatic stub testing (stubtest) |
| ================================= |
| |
| Stub files are files containing type annotations. See |
| `PEP 484 <https://www.python.org/dev/peps/pep-0484/#stub-files>`_ |
| for more motivation and details. |
| |
| A common problem with stub files is that they tend to diverge from the |
| actual implementation. Mypy includes the ``stubtest`` tool that can |
| automatically check for discrepancies between the stubs and the |
| implementation at runtime. |
| |
| What stubtest does and does not do |
| ********************************** |
| |
| Stubtest will import your code and introspect your code objects at runtime, for |
| example, by using the capabilities of the :py:mod:`inspect` module. Stubtest |
| will then analyse the stub files, and compare the two, pointing out things that |
| differ between stubs and the implementation at runtime. |
| |
| It's important to be aware of the limitations of this comparison. Stubtest will |
| not make any attempt to statically analyse your actual code and relies only on |
| dynamic runtime introspection (in particular, this approach means stubtest works |
| well with extension modules). However, this means that stubtest has limited |
| visibility; for instance, it cannot tell if a return type of a function is |
| accurately typed in the stubs. |
| |
| For clarity, here are some additional things stubtest can't do: |
| |
| * Type check your code -- use ``mypy`` instead |
| * Generate stubs -- use ``stubgen`` or ``pyright --createstub`` instead |
| * Generate stubs based on running your application or test suite -- use ``monkeytype`` instead |
| * Apply stubs to code to produce inline types -- use ``retype`` or ``libcst`` instead |
| |
| In summary, stubtest works very well for ensuring basic consistency between |
| stubs and implementation or to check for stub completeness. It's used to |
| test Python's official collection of library stubs, |
| `typeshed <https://github.com/python/typeshed>`_. |
| |
| .. warning:: |
| |
| stubtest will import and execute Python code from the packages it checks. |
| |
| Example |
| ******* |
| |
| Here's a quick example of what stubtest can do: |
| |
| .. code-block:: shell |
| |
| $ python3 -m pip install mypy |
| |
| $ cat library.py |
| x = "hello, stubtest" |
| |
| def foo(x=None): |
| print(x) |
| |
| $ cat library.pyi |
| x: int |
| |
| def foo(x: int) -> None: ... |
| |
| $ python3 -m mypy.stubtest library |
| error: library.foo is inconsistent, runtime argument "x" has a default value but stub argument does not |
| Stub: at line 3 |
| def (x: builtins.int) |
| Runtime: in file ~/library.py:3 |
| def (x=None) |
| |
| error: library.x variable differs from runtime type Literal['hello, stubtest'] |
| Stub: at line 1 |
| builtins.int |
| Runtime: |
| 'hello, stubtest' |
| |
| |
| Usage |
| ***** |
| |
| Running stubtest can be as simple as ``stubtest module_to_check``. |
| Run :option:`stubtest --help` for a quick summary of options. |
| |
| Stubtest must be able to import the code to be checked, so make sure that mypy |
| is installed in the same environment as the library to be tested. In some |
| cases, setting ``PYTHONPATH`` can help stubtest find the code to import. |
| |
| Similarly, stubtest must be able to find the stubs to be checked. Stubtest |
| respects the ``MYPYPATH`` environment variable -- consider using this if you |
| receive a complaint along the lines of "failed to find stubs". |
| |
| Note that stubtest requires mypy to be able to analyse stubs. If mypy is unable |
| to analyse stubs, you may get an error on the lines of "not checking stubs due |
| to mypy build errors". In this case, you will need to mitigate those errors |
| before stubtest will run. Despite potential overlap in errors here, stubtest is |
| not intended as a substitute for running mypy directly. |
| |
| If you wish to ignore some of stubtest's complaints, stubtest supports a |
| pretty handy allowlist system. |
| |
| The rest of this section documents the command line interface of stubtest. |
| |
| .. option:: --concise |
| |
| Makes stubtest's output more concise, one line per error |
| |
| .. option:: --ignore-missing-stub |
| |
| Ignore errors for stub missing things that are present at runtime |
| |
| .. option:: --ignore-positional-only |
| |
| Ignore errors for whether an argument should or shouldn't be positional-only |
| |
| .. option:: --allowlist FILE |
| |
| Use file as an allowlist. Can be passed multiple times to combine multiple |
| allowlists. Allowlists can be created with --generate-allowlist. Allowlists |
| support regular expressions. |
| |
| The presence of an entry in the allowlist means stubtest will not generate |
| any errors for the corresponding definition. |
| |
| .. option:: --generate-allowlist |
| |
| Print an allowlist (to stdout) to be used with --allowlist |
| |
| When introducing stubtest to an existing project, this is an easy way to |
| silence all existing errors. |
| |
| .. option:: --ignore-unused-allowlist |
| |
| Ignore unused allowlist entries |
| |
| Without this option enabled, the default is for stubtest to complain if an |
| allowlist entry is not necessary for stubtest to pass successfully. |
| |
| Note if an allowlist entry is a regex that matches the empty string, |
| stubtest will never consider it unused. For example, to get |
| `--ignore-unused-allowlist` behaviour for a single allowlist entry like |
| ``foo.bar`` you could add an allowlist entry ``(foo\.bar)?``. |
| This can be useful when an error only occurs on a specific platform. |
| |
| .. option:: --mypy-config-file FILE |
| |
| Use specified mypy config file to determine mypy plugins and mypy path |
| |
| .. option:: --custom-typeshed-dir DIR |
| |
| Use the custom typeshed in DIR |
| |
| .. option:: --check-typeshed |
| |
| Check all stdlib modules in typeshed |
| |
| .. option:: --help |
| |
| Show a help message :-) |