forked from RustPython/RustPython
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathdocs_gen.py
More file actions
101 lines (81 loc) · 3.19 KB
/
docs_gen.py
File metadata and controls
101 lines (81 loc) · 3.19 KB
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
import sys
import warnings
from pydoc import ModuleScanner
def scan_modules():
"""taken from the source code of help('modules')
https://github.com/python/cpython/blob/63298930fb531ba2bb4f23bc3b915dbf1e17e9e1/Lib/pydoc.py#L2178"""
modules = {}
def callback(path, modname, desc, modules=modules):
if modname and modname[-9:] == ".__init__":
modname = modname[:-9] + " (package)"
if modname.find(".") < 0:
modules[modname] = 1
def onerror(modname):
callback(None, modname, None)
with warnings.catch_warnings():
# ignore warnings from importing deprecated modules
warnings.simplefilter("ignore")
ModuleScanner().run(callback, onerror=onerror)
return list(modules.keys())
def import_module(module_name):
import io
from contextlib import redirect_stdout
# Importing modules causes ('Constant String', 2, None, 4) and
# "Hello world!" to be printed to stdout.
f = io.StringIO()
with warnings.catch_warnings(), redirect_stdout(f):
# ignore warnings caused by importing deprecated modules
warnings.filterwarnings("ignore", category=DeprecationWarning)
try:
module = __import__(module_name)
except Exception as e:
return e
return module
def is_child(module, item):
import inspect
item_mod = inspect.getmodule(item)
return item_mod is module
def traverse(module, names, item):
import inspect
has_doc = inspect.ismodule(item) or inspect.isclass(item) or inspect.isbuiltin(item)
if has_doc and isinstance(item.__doc__, str):
yield names, item.__doc__
attr_names = dir(item)
for name in attr_names:
if name in ['__class__', '__dict__', '__doc__', '__objclass__', '__name__', '__qualname__']:
continue
try:
attr = getattr(item, name)
except AttributeError:
assert name == '__abstractmethods__'
continue
if module is item and not is_child(module, attr):
continue
is_type_or_module = (type(attr) is type) or (type(attr) is type(__builtins__))
new_names = names.copy()
new_names.append(name)
if item == attr:
pass
elif not inspect.ismodule(item) and inspect.ismodule(attr):
pass
elif is_type_or_module:
yield from traverse(module, new_names, attr)
elif callable(attr) or not issubclass(type(attr), type) or type(attr).__name__ in ('getset_descriptor', 'member_descriptor'):
if inspect.isbuiltin(attr):
yield new_names, attr.__doc__
else:
assert False, (module, new_names, attr, type(attr).__name__)
def traverse_all():
for module_name in scan_modules():
if module_name in ('this', 'antigravity'):
continue
module = import_module(module_name)
if hasattr(module, '__cached__'): # python module
continue
yield from traverse(module, [module_name], module)
def docs():
docs = {'.'.join(names): doc for names, doc in traverse_all()}
return docs
if __name__ == '__main__':
import json
print(json.dumps(docs(), indent=4, sort_keys=True))