Recently, there have been cases where Python stub files are used for work. However, mypy's stub file generation command does not add docstring to the stub file, so I made a library to add it myself, so I will write about that.
It is a file with the extension .pyi
, which describes Python type information and omits the logic part.
Using a stub file has the following advantages.
--Due to the convenience of decorators and the convenience of third-party libraries, you can enable completion and checking for modules that do not work well. --Type information can be added by adding a separate stub file to a third-party library that does not have type annotations (checking is also enabled). ――For large modules that have a high load when many type inferences are run, using the minimum stub file that omits the mounting part reduces the load and reduces delays such as completion and checking.
On the contrary, as a disadvantage,
--Since the definition of type information is overwritten in the stub file for the original module, it is not preferable in the case where the project code is frequently updated because it deviates from the contents of the module (when using it in a project). It would be nice to have a mechanism to update the stub file in real time as much as possible). -* Basically, if it is a library code, etc., it will not be updated frequently, so it will be a good match for reducing the load on the editor.
And so on.
For details on how to create a stub file, see How to create a package containing type hints conforming to PEP 561.
Some code in the project is no longer completed or type checked on VS Code (due to decorators etc.). Although it worked before, it is unknown whether it is due to the update or the library used on VS Code, but it is inconvenient if it is left as it is even after waiting for a while, so some Only the project module has stub files added in real time (I tried various other measures, but in the end, partial stub file support seemed to be quick and calm).
It's okay to decide to use the stub file in your project, but if you need to manually update the stub file, it simply increases the man-hours and is not preferable. I would like you to update the stub file in real time while automating as much as possible.
Regarding the automatic generation of stub files, mypy provides a function of a command called stubgen, so I am using that.
Automatic stub generation (stubgen)
This avoids the manual generation of stub files, which greatly reduces the burden. However, the docstring disappears in the stub file generated using this stubgen.
For example, if you have the following module code,
from random import randint
sample_int: int = 100
def sample_func(a: int, b: str) -> bool:
"""
Lorem ipsum dolor sit amet, consectetur adipiscing elit,
ed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
Parameters
----------
a : int
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
b : str
ed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
Returns
-------
c : bool
Ut enim ad minim veniam, quis nostrud exercitation.
"""
return True
class SampleClass:
def __init__(self) -> None:
"""
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
"""
@property
def sample_property(self) -> int:
"""
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
Returns
-------
d : int
ed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
"""
return randint(0, 100)
If you use the stubgen command to generate a stub file with the contents of this module, it will look like the following.
sample_int: int
def sample_func(a: int, b: str) -> bool: ...
class SampleClass:
def __init__(self) -> None: ...
@property
def sample_property(self) -> int: ...
You can see that the type information remains, but the docstring has disappeared. In each extension used on VS Code (Pylance etc.), if the stub file is placed in the same directory as the module, the stub file seems to have priority, in this case the contents of docstring are displayed on the editor It will not be.
So I wrote a library that gives the docstring of the original module to the stub file and registered it in PyPI (pip) (MIT license).
It was okay to write it at work, but I felt that it might be used privately, so I wrote it as OSS privately (so that it can be used for other private projects etc.).
I finished it in a short time (I have a lot of other things I want to do), so please forgive me for the rough cutting (because if I stick to it, it will not finish in a few days ...). I think I'll start using it at work soon, so if I notice something that doesn't work there, I'll update it from time to time.
Installation can be done with the pip command.
$ pip install stubdoc
The usage is the path of the original module file of the stub file in the -m
argument (or --module_path
) (since import is done internally by referring to this path as a module, .. in the upper hierarchy. Specify the path of the stub file to which you want to add the docstring to the
-s argument (or
--stub_path) in/
or the root /
path specification does not work.).
Command example:
$ stubdoc -m samples/sample.py -s out/samples/sample.pyi
Or you can handle it on Python.
from stubdoc import add_docstring_to_stubfile
add_docstring_to_stubfile(
original_module_path='sample/path.py',
stub_file_path='sample/path.pyi')
As a result, the contents of the stub file given as an example earlier will be replaced with the one with docstring as shown below.
sample_int: int
def sample_func(a: int, b: str) -> bool:
"""
Lorem ipsum dolor sit amet, consectetur adipiscing elit,
ed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
Parameters
----------
a : int
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
b : str
ed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
Returns
-------
c : bool
Ut enim ad minim veniam, quis nostrud exercitation.
"""
class SampleClass:
def __init__(self) -> None:
"""
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
"""
@property
def sample_property(self) -> int:
"""
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
Returns
-------
d : int
ed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
"""
It works only when the Ellipsis instance (...
) and the pass keyword description are described after the colon of the function without line breaks (the same form as the output result of mypy's stubgen command). For example, it does not support stub files that have a line break before the Ellipsis instance (such as when a stub file is manually generated) as shown below.
def sample_func(a: int, b: str) -> bool:
...
class SampleClass:
def __init__(self) -> None:
...
@property
def sample_property(self) -> int:
pass
Also, it does not support nested functions. Only the methods of functions or classes defined at the top level are targeted. For example, the function that is nested as shown below is not checked.
def sample_func_1(a: int, b: str) -> bool:
def sample_func_2(c: list) -> None: ...