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 147 148 149 150 151 152 153 154
|
# pyinfra
# File: docs/generate_module_docs.py
# Desc: generate rst docs from the module modules
import re
from types import FunctionType
from importlib import import_module
from inspect import getmembers, getargspec
from six.moves import range
from pyinfra import modules
MODULE_DEF_LINE_MAX = 90
def _title_line(char, string):
return ''.join(char for _ in range(0, len(string)))
def _format_doc_line(line):
# Bold the <arg>: part of each line
line = re.sub(r'\+ ([a-z_\/]+)(.*)', r'+ **\1**\2', line)
# Strip the first 4 characters (first indent from docstring)
return line[4:]
def build_facts():
for module_name in modules.__all__:
lines = []
print('--> Doing module: {0}'.format(module_name))
module = import_module('pyinfra.modules.{0}'.format(module_name))
lines.append(module_name.title())
lines.append(_title_line('-', module_name))
lines.append('')
if module.__doc__:
lines.append(module.__doc__)
operation_functions = [
(key, value._pyinfra_op)
for key, value in getmembers(module)
if (
isinstance(value, FunctionType)
and value.__module__ == module.__name__
and getattr(value, '_pyinfra_op', False)
and not value.__name__.startswith('_')
and not key.startswith('_')
)
]
for name, func in operation_functions:
title_name = ':code:`{0}.{1}`'.format(module_name, name)
lines.append(title_name)
# Underline name with -'s for title
lines.append(_title_line('~', title_name))
doc = func.__doc__
if doc:
docbits = doc.strip().split('\n')
description_lines = []
for line in docbits:
if line:
description_lines.append(line)
else:
break
if len(docbits) > 0:
lines.append('')
lines.extend([line.strip() for line in description_lines])
lines.append('')
doc = '\n'.join(docbits[len(description_lines):])
argspec = getargspec(func)
# Make default strings appear as strings
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 {}
# Build args string
args = [
'{0}={1}'.format(arg, defaults[arg])
if arg in defaults else arg
for arg in argspec.args[2:]
]
if len(', '.join(args)) <= MODULE_DEF_LINE_MAX:
args_string = ', '.join(args)
else:
arg_buffer = []
arg_lines = []
for arg in args:
if len(', '.join(arg_buffer + [arg])) >= MODULE_DEF_LINE_MAX:
arg_lines.append(arg_buffer)
arg_buffer = []
arg_buffer.append(arg)
if arg_buffer:
arg_lines.append(arg_buffer)
arg_lines = [
' {0}'.format(', '.join(line_args))
for line_args in arg_lines
]
args_string = '\n{0}\n '.format(',\n'.join(arg_lines))
# Atttach the code block
lines.append('''
.. code:: python
{0}.{1}({2})
'''.strip().format(module_name, name, args_string))
# Append any remaining docstring
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 = 'docs/modules/{0}.rst'.format(module_name)
print('--> Writing {0}'.format(module_filename))
out = '\n'.join(lines)
outfile = open(module_filename, 'w')
outfile.write(out)
outfile.close()
if __name__ == '__main__':
print('### Building module docs')
build_facts()
|