-
-
Notifications
You must be signed in to change notification settings - Fork 11
/
Copy pathdocument_sopel_plugins.py
164 lines (139 loc) · 5.29 KB
/
document_sopel_plugins.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
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
#!/usr/bin/env python3
"""
Sopel plugin documentation utility
This script creates (either Markdown or reST) files, documenting the commands
and plugin configuration options in a Sopel instance.
Copyright 2012 Edward Powell, embolalia.net
Copyright 2019 dgw, technobabbl.es
Licensed under the Eiffel Forum License 2.
https://sopel.chat
"""
import argparse
from importlib.machinery import SourceFileLoader
import inspect
import operator
import os
import sys
try:
import sopel
except:
print ("Sopel isn't installed globally. You may have problems.")
def main(argv=None):
this_dir = os.path.dirname(os.path.abspath(__file__))
config_vals_file = os.path.join(this_dir, '_usage/plugin-configuration.md')
commands_file = os.path.join(this_dir, '_usage/commands.md')
parser = argparse.ArgumentParser(
description="Sopel plugin documentation utility",
usage='%(prog)s [options]'
)
parser.add_argument(
'--sopel',
dest='sopel_root',
nargs='?',
help="Specify the location of Sopel's code.",
default=os.path.join(os.path.dirname(this_dir), 'sopel')
)
parser.add_argument(
'--clean',
action='store_true',
help="Clean up the generated page files."
)
if argv:
args = parser.parse_args(argv)
else:
args = parser.parse_args()
if args.clean:
print("Cleaning up generated plugin documentation...")
os.remove(config_vals_file)
os.remove(commands_file)
return
print("Generating plugin docs using Sopel from " + args.sopel_root)
print("...")
os.sys.path.insert(0, args.sopel_root)
filenames = []
plugins_dir = os.path.join(args.sopel_root, 'sopel', 'builtins')
for fn in os.listdir(plugins_dir):
if fn.endswith('.py') and not fn.startswith('_'):
filenames.append(os.path.join(plugins_dir, fn))
filenames.sort()
filenames.append(os.path.join(args.sopel_root, 'sopel', 'coretasks.py'))
commands = []
with open(config_vals_file, 'w', encoding='utf8') as f:
f.write(inspect.cleandoc("""\
---
title: Plugin configuration
order: 5
previously:
- /usage/module-configuration/
---
This page contains documentation for all plugins within Sopel's main
plugins directory. If you have added plugins without rebuilding the
documentation, or are using a secondary plugins directory, those
plugins will not be shown here.
## Plugins
"""))
for filename in filenames:
c = document_plugin(filename, f)
if c:
commands.extend(c)
with open(commands_file, 'w', encoding='utf8') as f:
f.write(inspect.cleandoc("""\
---
title: Plugin commands
order: 10
---
This page contains a list of all commands from plugins within Sopel's
main plugins directory. If you have added plugins without rebuilding
the documentation, or are using a secondary plugins directory, those
plugins will not be shown here.
"""))
f.write("\n\n| Command(s) | Purpose | Example | Plugin |\n")
f.write("| ---------- | ------- | ------- | ------ |\n")
for c in commands:
process_command(f, c)
print("Done!")
def document_plugin(plugin_file, f):
try:
plugin = SourceFileLoader(os.path.basename(plugin_file)[:-3], plugin_file).load_module()
except Exception as e:
print ("Error loading %s: %s\nThis plugin will not be documented."
% (plugin_file, e))
else:
commands = []
if hasattr(plugin, 'configure'):
f.write('\n\n### %s\n\n'%(plugin.__name__))
if not plugin.configure.__doc__:
plugin.configure.__doc__ = 'This plugin has configuration options that are not documented. Go bludgeon the author.'
f.write(inspect.cleandoc(plugin.configure.__doc__))
for obj in dir(plugin):
func = getattr(plugin, obj)
if not callable(func):
# guard against plugins that use `from sopel import module`
continue
if (hasattr(func, 'commands')):
if not hasattr(func, 'name'):
name = func.__name__
else:
name = func.name
setattr(func, 'plugin_name', plugin.__name__)
commands.append((name, func))
# return the commands from each plugin in (roughly) alphabetical order
commands.sort()
return commands
def process_command(f, func):
name = func[0]
func = func[1]
purpose = (func.__doc__ or '*No documentation found.*')
purpose = inspect.cleandoc(purpose).replace('\n', '<br>').replace('|', '\\|')
# Remove when upstream docstrings in the meetbot file have been updated
purpose = purpose.replace('_usage/meetbot-module.md', '_usage/meetbot-plugin.md')
if hasattr(func, 'example'):
example = '`%s`' % func.example[0]["example"].replace('$nickname', 'Sopel')
else:
example = ''
commands = '.'+'<br>.'.join(func.commands) #TODO rules
plugin = func.plugin_name
line = "| %s | %s | %s | %s |\n" % (commands, purpose, example, plugin)
f.write(line)
if __name__ == '__main__':
main()