comparison env/lib/python3.9/site-packages/humanfriendly/sphinx.py @ 0:4f3585e2f14b draft default tip

"planemo upload commit 60cee0fc7c0cda8592644e1aad72851dec82c959"
author shellac
date Mon, 22 Mar 2021 18:12:50 +0000
parents
children
comparison
equal deleted inserted replaced
-1:000000000000 0:4f3585e2f14b
1 # Human friendly input/output in Python.
2 #
3 # Author: Peter Odding <peter@peterodding.com>
4 # Last Change: March 1, 2020
5 # URL: https://humanfriendly.readthedocs.io
6
7 """
8 Customizations for and integration with the Sphinx_ documentation generator.
9
10 The :mod:`humanfriendly.sphinx` module uses the `Sphinx extension API`_ to
11 customize the process of generating Sphinx based Python documentation. To
12 explore the functionality this module offers its best to start reading
13 from the :func:`setup()` function.
14
15 .. _Sphinx: http://www.sphinx-doc.org/
16 .. _Sphinx extension API: http://sphinx-doc.org/extdev/appapi.html
17 """
18
19 # Standard library modules.
20 import logging
21 import types
22
23 # External dependencies (if Sphinx is installed docutils will be installed).
24 import docutils.nodes
25 import docutils.utils
26
27 # Modules included in our package.
28 from humanfriendly.deprecation import get_aliases
29 from humanfriendly.text import compact, dedent, format
30 from humanfriendly.usage import USAGE_MARKER, render_usage
31
32 # Public identifiers that require documentation.
33 __all__ = (
34 "deprecation_note_callback",
35 "enable_deprecation_notes",
36 "enable_man_role",
37 "enable_pypi_role",
38 "enable_special_methods",
39 "enable_usage_formatting",
40 "logger",
41 "man_role",
42 "pypi_role",
43 "setup",
44 "special_methods_callback",
45 "usage_message_callback",
46 )
47
48 # Initialize a logger for this module.
49 logger = logging.getLogger(__name__)
50
51
52 def deprecation_note_callback(app, what, name, obj, options, lines):
53 """
54 Automatically document aliases defined using :func:`~humanfriendly.deprecation.define_aliases()`.
55
56 Refer to :func:`enable_deprecation_notes()` to enable the use of this
57 function (you probably don't want to call :func:`deprecation_note_callback()`
58 directly).
59
60 This function implements a callback for ``autodoc-process-docstring`` that
61 reformats module docstrings to append an overview of aliases defined by the
62 module.
63
64 The parameters expected by this function are those defined for Sphinx event
65 callback functions (i.e. I'm not going to document them here :-).
66 """
67 if isinstance(obj, types.ModuleType) and lines:
68 aliases = get_aliases(obj.__name__)
69 if aliases:
70 # Convert the existing docstring to a string and remove leading
71 # indentation from that string, otherwise our generated content
72 # would have to match the existing indentation in order not to
73 # break docstring parsing (because indentation is significant
74 # in the reStructuredText format).
75 blocks = [dedent("\n".join(lines))]
76 # Use an admonition to group the deprecated aliases together and
77 # to distinguish them from the autodoc entries that follow.
78 blocks.append(".. note:: Deprecated names")
79 indent = " " * 3
80 if len(aliases) == 1:
81 explanation = """
82 The following alias exists to preserve backwards compatibility,
83 however a :exc:`~exceptions.DeprecationWarning` is triggered
84 when it is accessed, because this alias will be removed
85 in a future release.
86 """
87 else:
88 explanation = """
89 The following aliases exist to preserve backwards compatibility,
90 however a :exc:`~exceptions.DeprecationWarning` is triggered
91 when they are accessed, because these aliases will be
92 removed in a future release.
93 """
94 blocks.append(indent + compact(explanation))
95 for name, target in aliases.items():
96 blocks.append(format("%s.. data:: %s", indent, name))
97 blocks.append(format("%sAlias for :obj:`%s`.", indent * 2, target))
98 update_lines(lines, "\n\n".join(blocks))
99
100
101 def enable_deprecation_notes(app):
102 """
103 Enable documenting backwards compatibility aliases using the autodoc_ extension.
104
105 :param app: The Sphinx application object.
106
107 This function connects the :func:`deprecation_note_callback()` function to
108 ``autodoc-process-docstring`` events.
109
110 .. _autodoc: http://www.sphinx-doc.org/en/stable/ext/autodoc.html
111 """
112 app.connect("autodoc-process-docstring", deprecation_note_callback)
113
114
115 def enable_man_role(app):
116 """
117 Enable the ``:man:`` role for linking to Debian Linux manual pages.
118
119 :param app: The Sphinx application object.
120
121 This function registers the :func:`man_role()` function to handle the
122 ``:man:`` role.
123 """
124 app.add_role("man", man_role)
125
126
127 def enable_pypi_role(app):
128 """
129 Enable the ``:pypi:`` role for linking to the Python Package Index.
130
131 :param app: The Sphinx application object.
132
133 This function registers the :func:`pypi_role()` function to handle the
134 ``:pypi:`` role.
135 """
136 app.add_role("pypi", pypi_role)
137
138
139 def enable_special_methods(app):
140 """
141 Enable documenting "special methods" using the autodoc_ extension.
142
143 :param app: The Sphinx application object.
144
145 This function connects the :func:`special_methods_callback()` function to
146 ``autodoc-skip-member`` events.
147
148 .. _autodoc: http://www.sphinx-doc.org/en/stable/ext/autodoc.html
149 """
150 app.connect("autodoc-skip-member", special_methods_callback)
151
152
153 def enable_usage_formatting(app):
154 """
155 Reformat human friendly usage messages to reStructuredText_.
156
157 :param app: The Sphinx application object (as given to ``setup()``).
158
159 This function connects the :func:`usage_message_callback()` function to
160 ``autodoc-process-docstring`` events.
161
162 .. _reStructuredText: https://en.wikipedia.org/wiki/ReStructuredText
163 """
164 app.connect("autodoc-process-docstring", usage_message_callback)
165
166
167 def man_role(role, rawtext, text, lineno, inliner, options={}, content=[]):
168 """
169 Convert a Linux manual topic to a hyperlink.
170
171 Using the ``:man:`` role is very simple, here's an example:
172
173 .. code-block:: rst
174
175 See the :man:`python` documentation.
176
177 This results in the following:
178
179 See the :man:`python` documentation.
180
181 As the example shows you can use the role inline, embedded in sentences of
182 text. In the generated documentation the ``:man:`` text is omitted and a
183 hyperlink pointing to the Debian Linux manual pages is emitted.
184 """
185 man_url = "https://manpages.debian.org/%s" % text
186 reference = docutils.nodes.reference(rawtext, docutils.utils.unescape(text), refuri=man_url, **options)
187 return [reference], []
188
189
190 def pypi_role(role, rawtext, text, lineno, inliner, options={}, content=[]):
191 """
192 Generate hyperlinks to the Python Package Index.
193
194 Using the ``:pypi:`` role is very simple, here's an example:
195
196 .. code-block:: rst
197
198 See the :pypi:`humanfriendly` package.
199
200 This results in the following:
201
202 See the :pypi:`humanfriendly` package.
203
204 As the example shows you can use the role inline, embedded in sentences of
205 text. In the generated documentation the ``:pypi:`` text is omitted and a
206 hyperlink pointing to the Python Package Index is emitted.
207 """
208 pypi_url = "https://pypi.org/project/%s/" % text
209 reference = docutils.nodes.reference(rawtext, docutils.utils.unescape(text), refuri=pypi_url, **options)
210 return [reference], []
211
212
213 def setup(app):
214 """
215 Enable all of the provided Sphinx_ customizations.
216
217 :param app: The Sphinx application object.
218
219 The :func:`setup()` function makes it easy to enable all of the Sphinx
220 customizations provided by the :mod:`humanfriendly.sphinx` module with the
221 least amount of code. All you need to do is to add the module name to the
222 ``extensions`` variable in your ``conf.py`` file:
223
224 .. code-block:: python
225
226 # Sphinx extension module names.
227 extensions = [
228 'sphinx.ext.autodoc',
229 'sphinx.ext.doctest',
230 'sphinx.ext.intersphinx',
231 'humanfriendly.sphinx',
232 ]
233
234 When Sphinx sees the :mod:`humanfriendly.sphinx` name it will import the
235 module and call its :func:`setup()` function. This function will then call
236 the following:
237
238 - :func:`enable_deprecation_notes()`
239 - :func:`enable_man_role()`
240 - :func:`enable_pypi_role()`
241 - :func:`enable_special_methods()`
242 - :func:`enable_usage_formatting()`
243
244 Of course more functionality may be added at a later stage. If you don't
245 like that idea you may be better of calling the individual functions from
246 your own ``setup()`` function.
247 """
248 enable_deprecation_notes(app)
249 enable_man_role(app)
250 enable_pypi_role(app)
251 enable_special_methods(app)
252 enable_usage_formatting(app)
253
254
255 def special_methods_callback(app, what, name, obj, skip, options):
256 """
257 Enable documenting "special methods" using the autodoc_ extension.
258
259 Refer to :func:`enable_special_methods()` to enable the use of this
260 function (you probably don't want to call
261 :func:`special_methods_callback()` directly).
262
263 This function implements a callback for ``autodoc-skip-member`` events to
264 include documented "special methods" (method names with two leading and two
265 trailing underscores) in your documentation. The result is similar to the
266 use of the ``special-members`` flag with one big difference: Special
267 methods are included but other types of members are ignored. This means
268 that attributes like ``__weakref__`` will always be ignored (this was my
269 main annoyance with the ``special-members`` flag).
270
271 The parameters expected by this function are those defined for Sphinx event
272 callback functions (i.e. I'm not going to document them here :-).
273 """
274 if getattr(obj, "__doc__", None) and isinstance(obj, (types.FunctionType, types.MethodType)):
275 return False
276 else:
277 return skip
278
279
280 def update_lines(lines, text):
281 """Private helper for ``autodoc-process-docstring`` callbacks."""
282 while lines:
283 lines.pop()
284 lines.extend(text.splitlines())
285
286
287 def usage_message_callback(app, what, name, obj, options, lines):
288 """
289 Reformat human friendly usage messages to reStructuredText_.
290
291 Refer to :func:`enable_usage_formatting()` to enable the use of this
292 function (you probably don't want to call :func:`usage_message_callback()`
293 directly).
294
295 This function implements a callback for ``autodoc-process-docstring`` that
296 reformats module docstrings using :func:`.render_usage()` so that Sphinx
297 doesn't mangle usage messages that were written to be human readable
298 instead of machine readable. Only module docstrings whose first line starts
299 with :data:`.USAGE_MARKER` are reformatted.
300
301 The parameters expected by this function are those defined for Sphinx event
302 callback functions (i.e. I'm not going to document them here :-).
303 """
304 # Make sure we only modify the docstrings of modules.
305 if isinstance(obj, types.ModuleType) and lines:
306 # Make sure we only modify docstrings containing a usage message.
307 if lines[0].startswith(USAGE_MARKER):
308 # Convert the usage message to reStructuredText.
309 text = render_usage("\n".join(lines))
310 # Fill up the buffer with our modified docstring.
311 update_lines(lines, text)