Create .pyi files automatically?
Asked Answered
F

3

32

How can I automatically create the boilerplate code of pyi files?

I want to create a pyi file for type hinting as described in pep484 which contains all method names.

I don't want magic. I want to add the type information after auto creating the file.

I want to avoid the copy+paste work.

Goal: Type hinting in PyCharm for Python2.

Flywheel answered 24/2, 2016 at 12:42 Comment(2)
What is the module that you're creating the pyi file for written in?Pathogenesis
@Pathogenesis I want type hinting for python files. But this question is outdated. We are in the process of switching from Python2 to Python3 in the next months.Flywheel
R
48

As far as I am concerned, there is no such direct tool in PyCharm. There are, however, 3rd party tools for this.


.pyi generators

MyPy

Yes, I guess anyone who wants to use compile-time type checking in Python, probably ends up using MyPy. MyPy contains stubgen.py tool which generates .pyi files.

Usage

mkdir out
stubgen urllib.parse

generates out/urllib/parse.pyi.

You can use it wit Python2 too:

stubgen --py2 textwrap

And for C modules:

scripts/stubgen --docpath <DIR>/Python-3.4.2/Doc/library curses

If you want to specify the path to your custom package, you can use --search-path option:

stubgen my-pkg --search-path=folder/path/to/the/package

make-stub-files

This project is dedicated to exactly this goal.

Usage

A very basic one (but it has a handful of options, just consult README.md or make_stub_files -h

make_stub_files foo.py

pre-written .pyi files

So you don't have to write your own.

Typeshed

Yes, if you're using .pyi files in your own project, you probably want to use this also when using external code. Typeshed contains .pyi files for Python2 and Python3 stdlib and a bunch of Python2 libraries (like redis, crypto, ...) and some Python3 libraries (like werkzeug or requests), all nicely versioned.


PyCharm alternatives to .pyi files

In case you're lazy, or simply just working on a project which doesn't require .pyi files and you don't want to be bothered by using 3rd party tools, PyCharm allows you to use:

Raimund answered 29/2, 2016 at 18:7 Comment(5)
i would like to use stubgen, but am a little confused on how to specify the custom path to my package directory. I can't seen to find it. Is it possible to include an example for this?Vertu
@jonathan: I currently have no access to a Python installation with MyPy, but the code says you should be able to use --search_path option. I have added it to the answer, but if you try it out, please give me feedback if it doesn't work so I can fix it.Raimund
The option is '--search-path', without an underscore. I tried that first, but it failed...Vertu
@Vertu oh yes, naturally, it's a dash, not an underscore. My bad. Thank you for pointing that out, I've fixed this.Raimund
@Raimund how do you do this programmatically? Can stubgen.py be imported like a library and used in code?Hanhana
B
2

I wrote a makefile for this:

package := example
sources := $(wildcard $(package)/*.py)

stub: $(sources)
    stubgen --output . --package $(package)

dist: setup.py stub
    python $< sdist bdist_wheel

publish: dist
    twine upload dist/*

.PHONY += stub dist publish

Running make publish will generate the .pyi files in the same location as their corresponding .py files before packaging and uploading to PyPI. They will be included in the package provided that they are part of package_data.

Bucko answered 11/1, 2020 at 13:34 Comment(0)
K
0

Pyright has support for this via its --createstub command-line option:

$ pyright --createstub your_package

Let's say this is your original project tree:

.
└── src
    └── your_package
        └── __init__.py

...where __init__.py contains:

def foo():
    return 42

After running the command above in the same root directory, the new tree will look like this:

.
├── src
│   └── foo
│       └── __init__.py
└── typings
    └── foo
        └── __init__.pyi

...with typings/foo/__init__.pyi containing:

"""
This type stub file was generated by pyright.
"""

def foo(): # -> Literal[42]:
    ...

Yes, the return type is inferred as exactly Literal[42] and not just int. Pyright's type inference capabilities are very powerful. It can work out what you mean in many cases, giving you a good headstart.

Ketch answered 26/7, 2024 at 19:33 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.