| 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
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
 | """Utilities used in generating docs."""
import ast
import importlib
import inspect
import typing
from pathlib import Path
PROJECT_ROOT = Path(__file__).parent.parent
def linkcode_resolve(source_url: str, domain: str, info: dict[str, str]) -> typing.Optional[str]:
    """
    Function called by linkcode to get the URL for a given resource.
    See for more details:
    https://www.sphinx-doc.org/en/master/usage/extensions/linkcode.html#confval-linkcode_resolve
    """
    if domain != "py":
        raise Exception("Unknown domain passed to linkcode function.")
    symbol_name = info["fullname"]
    module = importlib.import_module(info["module"])
    symbol = [module]
    for name in symbol_name.split("."):
        symbol.append(getattr(symbol[-1], name))
        symbol_name = name
    try:
        lines, start = inspect.getsourcelines(symbol[-1])
        end = start + len(lines)
    except TypeError:
        # Find variables by parsing the ast
        source = ast.parse(inspect.getsource(symbol[-2]))
        while isinstance(source.body[0], ast.ClassDef):
            source = source.body[0]
        for ast_obj in source.body:
            if isinstance(ast_obj, ast.Assign):
                names = []
                for target in ast_obj.targets:
                    if isinstance(target, ast.Tuple):
                        names.extend([name.id for name in target.elts])
                    else:
                        names.append(target.id)
                if symbol_name in names:
                    start, end = ast_obj.lineno, ast_obj.end_lineno
                    break
        else:
            raise Exception(f"Could not find symbol `{symbol_name}` in {module.__name__}.")
        _, offset = inspect.getsourcelines(symbol[-2])
        if offset != 0:
            offset -= 1
        start += offset
        end += offset
    file = Path(inspect.getfile(module)).relative_to(PROJECT_ROOT).as_posix()
    url = f"{source_url}/{file}#L{start}"
    if end != start:
        url += f"-L{end}"
    return url
def cleanup() -> None:
    """Remove unneeded autogenerated doc files, and clean up others."""
    included = __get_included()
    for file in (PROJECT_ROOT / "docs" / "output").iterdir():
        if file.name in ("botcore.rst", "botcore.exts.rst", "botcore.utils.rst") and file.name in included:
            content = file.read_text(encoding="utf-8").splitlines(keepends=True)
            # Rename the extension to be less wordy
            # Example: botcore.exts -> Botcore Exts
            title = content[0].split()[0].strip().replace("botcore.", "").replace(".", " ").title()
            title = f"{title}\n{'=' * len(title)}\n\n"
            content = title, *content[3:]
            file.write_text("".join(content), encoding="utf-8")
        elif file.name in included:
            # Clean up the submodule name so it's just the name without the top level module name
            # example: `botcore.regex module` -> `regex`
            lines = file.read_text(encoding="utf-8").splitlines(keepends=True)
            lines[0] = lines[0].replace("module", "").strip().split(".")[-1] + "\n"
            file.write_text("".join(lines))
        else:
            # These are files that have not been explicitly included in the docs via __all__
            print("Deleted file", file.name)
            file.unlink()
            continue
        # Take the opportunity to configure autodoc
        content = file.read_text(encoding="utf-8").replace("undoc-members", "special-members")
        file.write_text(content, encoding="utf-8")
def __get_included() -> set[str]:
    """Get a list of files that should be included in the final build."""
    def get_all_from_module(module_name: str) -> set[str]:
        try:
            module = importlib.import_module(module_name)
        except ModuleNotFoundError:
            return {}
        _modules = {module.__name__ + ".rst"}
        if hasattr(module, "__all__"):
            for sub_module in module.__all__:
                _modules.update(get_all_from_module(sub_module))
        return _modules
    return get_all_from_module("botcore")
 |