1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95
|
#!/usr/bin/env python3
"""Generate stub files for API classes with async and sync interface."""
from __future__ import annotations
import sys
from contextlib import suppress
from copy import copy
from pathlib import Path
from mypy.stubgen import (
ASTStubGenerator,
Options,
StubSource,
collect_build_targets,
generate_asts_for_modules,
generate_guarded,
mypy_options,
)
HEADER = '''"""
@generated by stubgen. Do not edit manually!
isort:skip_file
"""
'''
class ApiStubGenerator(ASTStubGenerator):
"""Generate stub text from a mypy AST."""
def add_sync(self) -> None:
"""Add sync methods."""
output = copy(self._output)
for i in range(len(output)):
if "async" in output[i]:
self.add(output[i].replace("async_", "").replace("async ", ""))
def generate_stubs() -> None:
"""Generate stubs - main entry point for the program."""
options = Options(
pyversion=sys.version_info[:2],
no_import=True,
inspect=False,
doc_dir="",
search_path=[],
interpreter=sys.executable,
parse_only=False,
ignore_errors=False,
include_private=False,
output_dir="",
modules=[],
packages=[],
files=["devolo_plc_api/device_api/deviceapi.py", "devolo_plc_api/plcnet_api/plcnetapi.py"],
verbose=False,
quiet=True,
export_less=True,
include_docstrings=False,
)
mypy_opts = mypy_options(options)
py_modules, _, _ = collect_build_targets(options, mypy_opts)
generate_asts_for_modules(py_modules, options.parse_only, mypy_opts, options.verbose)
files = []
for mod in py_modules:
target = mod.module.replace(".", "/")
target += ".pyi"
target = str(Path(options.output_dir) / target)
files.append(target)
with generate_guarded(mod.module, target, options.ignore_errors, options.verbose):
generate_stub_from_ast(mod, target, options.parse_only, options.include_private, options.export_less)
def generate_stub_from_ast(mod: StubSource, target: str, parse_only: bool, include_private: bool, export_less: bool) -> None:
"""Use analyzed (or just parsed) AST to generate type stub for single file."""
gen = ApiStubGenerator(mod.runtime_all, include_private=include_private, analyzed=not parse_only, export_less=export_less)
if mod.ast is None:
return
mod.ast.accept(gen)
gen.add_sync()
old_output = ""
new_output = HEADER + "".join(gen.output())
with suppress(FileNotFoundError), Path(target).open() as file:
old_output = file.read()
if new_output != old_output:
with Path(target).open("w") as file:
file.write(new_output)
sys.exit(1)
if __name__ == "__main__":
generate_stubs()
|