forked from pyinfra-dev/pyinfra
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathgenerate_facts_docs.py
executable file
·146 lines (117 loc) · 4.73 KB
/
generate_facts_docs.py
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
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
#!/usr/bin/env python
import sys
from glob import glob
from importlib import import_module
from inspect import getfullargspec, getmembers, isclass
from os import makedirs, path
from types import FunctionType, MethodType
from pyinfra.api.facts import FactBase, ShortFactBase
sys.path.append(".")
from docs.utils import format_doc_line, title_line # noqa: E402
def build_facts_docs():
this_dir = path.dirname(path.realpath(__file__))
docs_dir = path.abspath(path.join(this_dir, "..", "docs"))
facts_dir = path.join(this_dir, "..", "pyinfra", "facts", "*.py")
makedirs(path.join(docs_dir, "facts"), exist_ok=True)
fact_module_names = [
path.basename(name)[:-3] for name in glob(facts_dir) if not name.endswith("__init__.py")
]
for module_name in sorted(fact_module_names):
lines = []
print("--> Doing fact module: {0}".format(module_name))
module = import_module("pyinfra.facts.{0}".format(module_name))
full_title = "{0} Facts".format(module_name.title())
lines.append(full_title)
lines.append(title_line("-", full_title))
lines.append("")
if module.__doc__:
lines.append(module.__doc__)
if path.exists(path.join(this_dir, "..", "pyinfra", "operations", f"{module_name}.py")):
lines.append(f"See also: :doc:`../operations/{module_name}`.")
lines.append("")
fact_classes = [
(key, value)
for key, value in getmembers(module)
if (
isclass(value)
and (issubclass(value, FactBase) or issubclass(value, ShortFactBase))
and value.__module__ == module.__name__
and value is not FactBase
and not value.__name__.endswith("Base") # hacky!
)
]
for fact, cls in fact_classes:
# FactClass -> fact_accessor on host object
name = fact
args_string_and_brackets = ""
# Does this fact take args?
command_attr = getattr(cls, "command", None)
if isinstance(command_attr, (FunctionType, MethodType)):
# Attach basic argspec to name
# Note only supports facts with one arg as this is all that's
# possible, will need to refactor to print properly in future.
argspec = getfullargspec(command_attr)
arg_defaults = (
[
"'{}'".format(arg) if isinstance(arg, str) else arg
for arg in argspec.defaults
]
if argspec.defaults
else None
)
# Create a dict of arg name -> default
defaults = (
dict(
zip(
argspec.args[-len(arg_defaults) :],
arg_defaults,
),
)
if arg_defaults
else {}
)
if len(argspec.args):
args_string_and_brackets = ", {0}".format(
", ".join(
("{0}={1}".format(arg, defaults.get(arg)) if arg in defaults else arg)
for arg in argspec.args
if arg != "self"
),
)
lines.append(".. _facts:{0}.{1}:".format(module_name, name))
lines.append("")
title = ":code:`{0}.{1}`".format(module_name, name)
lines.append(title)
# Underline name with -'s for title
lines.append(title_line("~", title))
lines.append("")
# Attach the code block
lines.append(
"""
.. code:: python
host.get_fact({1}{2})
""".strip().format(
module_name,
name,
args_string_and_brackets,
),
)
# Append any docstring
doc = cls.__doc__
if doc:
lines.append("")
lines.append(
"{0}".format(
"\n".join([format_doc_line(line) for line in doc.split("\n")]),
).strip(),
)
lines.append("")
lines.append("")
# Write out the file
module_filename = path.join(docs_dir, "facts", "{0}.rst".format(module_name))
print("--> Writing {0}".format(module_filename))
with open(module_filename, "w", encoding="utf-8") as outfile:
outfile.write("\n".join(lines))
if __name__ == "__main__":
print("### Building fact docs")
build_facts_docs()