File: stubgen.py

package info (click to toggle)
devolo-plc-api 1.5.1%2Bgit20260208.5d3989e-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 504 kB
  • sloc: python: 1,616; makefile: 6
file content (95 lines) | stat: -rwxr-xr-x 2,851 bytes parent folder | download
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()