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 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700
|
# Licensed under a 3-clause BSD style license - see LICENSE.rst
"""
This directive will produce an "autosummary"-style table for public
attributes of a specified module. See the `sphinx.ext.autosummary`_ extension
for details on this process. The main difference from the `autosummary`_
directive is that `autosummary`_ requires manually inputting all attributes
that appear in the table, while this captures the entries automatically.
This directive requires a single argument that must be a module or
package.
It also accepts any options supported by the `autosummary`_ directive-
see `sphinx.ext.autosummary`_ for details. It also accepts some additional
options:
* ``:classes-only:``
If present, the autosummary table will only contain entries for
classes. This cannot be used at the same time with
``:functions-only:`` or ``:variables-only:``.
* ``:functions-only:``
If present, the autosummary table will only contain entries for
functions. This cannot be used at the same time with
``:classes-only:`` or ``:variables-only:``.
* ``:variables-only:``
If present, the autosummary table will only contain entries for
variables (everything except functions and classes). This cannot
be used at the same time with ``:classes-only:`` or
``:functions-only:``.
* ``:skip: obj1, [obj2, obj3, ...]``
If present, specifies that the listed objects should be skipped
and not have their documentation generated, nor be included in
the summary table.
* ``:allowed-package-names: pkgormod1, [pkgormod2, pkgormod3, ...]``
Specifies the packages that functions/classes documented here are
allowed to be from, as comma-separated list of package names. If not
given, only objects that are actually in a subpackage of the package
currently being documented are included.
* ``:inherited-members:`` or ``:no-inherited-members:``
The global sphinx configuration option ``automodsumm_inherited_members``
decides if members that a class inherits from a base class are included
in the generated documentation. The flags ``:inherited-members:`` or
``:no-inherited-members:`` allows overrriding this global setting.
* ``:sort:``
If the module contains ``__all__``, sort the module's objects
alphabetically (if ``__all__`` is not present, the objects are found
using `dir`, which always gives a sorted list).
This extension also adds three sphinx configuration options:
* ``automodsumm_writereprocessed``
Should be a bool, and if ``True``, will cause `automodsumm`_ to write files
with any ``automodsumm`` sections replaced with the content Sphinx
processes after ``automodsumm`` has run. The output files are not
actually used by sphinx, so this option is only for figuring out the
cause of sphinx warnings or other debugging. Defaults to ``False``.
* ``automodsumm_inherited_members``
Should be a bool and if ``True``, will cause `automodsumm`_ to document
class members that are inherited from a base class. This value can be
overriden for any particular automodsumm directive by including the
``:inherited-members:`` or ``:no-inherited-members:`` options. Defaults to
``False``.
* ``automodsumm_included_members``
A list of strings containing the names of hidden class members that should be
included in the documentation. This is most commonly used to add special class
methods like ``__getitem__`` and ``__setitem__``. Defaults to
``['__init__', '__call__']``.
.. _sphinx.ext.autosummary: http://sphinx-doc.org/latest/ext/autosummary.html
.. _autosummary: http://sphinx-doc.org/latest/ext/autosummary.html#directive-autosummary
.. _automod-diagram:
automod-diagram directive
=========================
This directive will produce an inheritance diagram like that of the
`sphinx.ext.inheritance_diagram`_ extension.
This directive requires a single argument that must be a module or
package. It accepts no options.
.. note::
Like 'inheritance-diagram', 'automod-diagram' requires
`graphviz <http://www.graphviz.org/>`_ to generate the inheritance diagram.
.. _sphinx.ext.inheritance_diagram: http://sphinx-doc.org/latest/ext/inheritance.html
"""
import inspect
import os
import re
from sphinx.util import logging
from sphinx.ext.autosummary import Autosummary
from sphinx.ext.inheritance_diagram import InheritanceDiagram, InheritanceGraph, try_import
from docutils.parsers.rst.directives import flag
from .utils import find_mod_objs, cleanup_whitespace
__all__ = ['Automoddiagram', 'Automodsumm', 'automodsumm_to_autosummary_lines',
'generate_automodsumm_docs', 'process_automodsumm_generation']
logger = logging.getLogger(__name__)
def _str_list_converter(argument):
"""
A directive option conversion function that converts the option into a list
of strings. Used for 'skip' option.
"""
if argument is None:
return []
else:
return [s.strip() for s in argument.split(',')]
class Automodsumm(Autosummary):
required_arguments = 1
optional_arguments = 0
final_argument_whitespace = False
has_content = False
option_spec = dict(Autosummary.option_spec)
option_spec['functions-only'] = flag
option_spec['classes-only'] = flag
option_spec['variables-only'] = flag
option_spec['skip'] = _str_list_converter
option_spec['allowed-package-names'] = _str_list_converter
option_spec['inherited-members'] = flag
option_spec['no-inherited-members'] = flag
option_spec['noindex'] = flag
option_spec['sort'] = flag
def run(self):
env = self.state.document.settings.env
modname = self.arguments[0]
nodelist = []
try:
localnames, fqns, objs = find_mod_objs(modname, sort='sort' in self.options)
except ImportError:
logger.warning("Couldn't import module " + modname)
return []
try:
# set self.content to trick the autosummary internals.
# Be sure to respect functions-only and classes-only.
funconly = 'functions-only' in self.options
clsonly = 'classes-only' in self.options
varonly = 'variables-only' in self.options
if [clsonly, funconly, varonly].count(True) > 1:
logger.warning('more than one of "functions-only", "classes-only", '
'or "variables-only" defined. Ignoring.')
clsonly = funconly = varonly = False
skipnames = []
if 'skip' in self.options:
option_skipnames = set(self.options['skip'])
for lnm in localnames:
if lnm in option_skipnames:
option_skipnames.remove(lnm)
skipnames.append(lnm)
if len(option_skipnames) > 0:
logger.warning('Tried to skip objects {objs} in module {mod}, '
'but they were not present. Ignoring.'
.format(objs=option_skipnames, mod=modname))
if funconly:
cont = []
for nm, obj in zip(localnames, objs):
if nm not in skipnames and inspect.isroutine(obj):
cont.append(nm)
elif clsonly:
cont = []
for nm, obj in zip(localnames, objs):
if nm not in skipnames and inspect.isclass(obj):
cont.append(nm)
elif varonly:
cont = []
for nm, obj in zip(localnames, objs):
if nm not in skipnames and not (inspect.isclass(obj) or
inspect.isroutine(obj)):
cont.append(nm)
else:
cont = [nm for nm in localnames if nm not in skipnames]
self.content = cont
# for some reason, even though ``currentmodule`` is substituted in,
# sphinx doesn't necessarily recognize this fact. So we just force
# it internally, and that seems to fix things
env.temp_data['py:module'] = modname
env.ref_context['py:module'] = modname
# can't use super because Sphinx/docutils has trouble return
# super(Autosummary,self).run()
nodelist.extend(Autosummary.run(self))
return nodelist
finally: # has_content = False for the Automodsumm
self.content = []
def get_items(self, names):
try:
self.bridge.genopt['imported-members'] = True
except AttributeError: # Sphinx < 4.0
self.genopt['imported-members'] = True
return Autosummary.get_items(self, names)
# <-------------------automod-diagram stuff----------------------------------->
class Automoddiagram(InheritanceDiagram):
option_spec = dict(InheritanceDiagram.option_spec)
option_spec['allowed-package-names'] = _str_list_converter
option_spec['skip'] = _str_list_converter
def run(self):
try:
ols = self.options.get('allowed-package-names', [])
ols = True if len(ols) == 0 else ols # if none are given, assume only local
nms, objs = find_mod_objs(self.arguments[0], onlylocals=ols)[1:]
except ImportError:
logger.warning("Couldn't import module " + self.arguments[0])
return []
# Check if some classes should be skipped
skip = self.options.get('skip', [])
clsnms = []
for n, o in zip(nms, objs):
if n.split('.')[-1] in skip:
continue
if inspect.isclass(o):
clsnms.append(n)
oldargs = self.arguments
try:
if len(clsnms) > 0:
self.arguments = [' '.join(clsnms)]
return InheritanceDiagram.run(self)
finally:
self.arguments = oldargs
# sphinx.ext.inheritance_diagram generates a list of class full names and
# generates a mapping from class full names to documentation URLs. However, the
# smart resolver in sphinx-automodapi causes the generated mapping to be instead
# from class documented name to documentation URLs. The class documented name
# can be different from the class full name if the class is not documented where
# it is defined, but rather at some other location where it is imported. In
# such situations, the code will fail to find the URL that for the class.
# The following code monkey-patches the method that receives the mapping and
# converts the keys from class documented names to class full names.
old_generate_dot = InheritanceGraph.generate_dot
def patched_generate_dot(self, name, urls={}, env=None,
graph_attrs={}, node_attrs={}, edge_attrs={}):
# Make a new mapping dictionary that uses class full names by importing each
# class documented name
fullname_urls = {self.class_name(try_import(name), 0, None): url
for name, url in urls.items() if try_import(name) is not None}
return old_generate_dot(self, name, urls=fullname_urls, env=env,
graph_attrs=graph_attrs, node_attrs=node_attrs, edge_attrs=edge_attrs)
InheritanceGraph.generate_dot = patched_generate_dot
# <---------------------automodsumm generation stuff-------------------------->
def process_automodsumm_generation(app):
env = app.builder.env
filestosearch = []
for docname in env.found_docs:
filename = env.doc2path(docname)
if os.path.isfile(filename):
filestosearch.append(docname + os.path.splitext(filename)[1])
liness = []
for sfn in filestosearch:
lines = automodsumm_to_autosummary_lines(sfn, app)
liness.append(lines)
if app.config.automodsumm_writereprocessed:
if lines: # empty list means no automodsumm entry is in the file
outfn = os.path.join(app.srcdir, sfn) + '.automodsumm'
with open(outfn, 'w', encoding='utf8') as f:
for l in lines: # noqa: E741
f.write(l)
f.write('\n')
for sfn, lines in zip(filestosearch, liness):
if len(lines) > 0:
generate_automodsumm_docs(
lines, sfn, app=app, builder=app.builder,
base_path=app.srcdir,
inherited_members=app.config.automodsumm_inherited_members,
included_members=app.config.automodsumm_included_members)
# _automodsummrex = re.compile(r'^(\s*)\.\. automodsumm::\s*([A-Za-z0-9_.]+)\s*'
# r'\n\1(\s*)(\S|$)', re.MULTILINE)
_lineendrex = r'(?:\n|$)'
_hdrex = r'^\n?(\s*)\.\. automodsumm::\s*(\S+)\s*' + _lineendrex
_oprex1 = r'(?:\1(\s+)\S.*' + _lineendrex + ')'
_oprex2 = r'(?:\1\4\S.*' + _lineendrex + ')'
_automodsummrex = re.compile(_hdrex + '(' + _oprex1 + '?' + _oprex2 + '*)',
re.MULTILINE)
def automodsumm_to_autosummary_lines(fn, app):
"""
Generates lines from a file with an "automodsumm" entry suitable for
feeding into "autosummary".
Searches the provided file for `automodsumm` directives and returns
a list of lines specifying the `autosummary` commands for the modules
requested. This does *not* return the whole file contents - just an
autosummary section in place of any :automodsumm: entries. Note that
any options given for `automodsumm` are also included in the
generated `autosummary` section.
Parameters
----------
fn : str
The name of the file to search for `automodsumm` entries.
app : sphinx.application.Application
The sphinx Application object
Returns
-------
lines : list of str
Lines for all `automodsumm` entries with the entries replaced by
`autosummary` and the module's members added.
"""
fullfn = os.path.join(app.builder.env.srcdir, fn)
with open(fullfn, encoding='utf8') as fr:
# Note: we use __name__ here instead of just writing the module name in
# case this extension is bundled into another package
from . import automodapi
try:
extensions = app.extensions
except AttributeError: # Sphinx <1.6
extensions = app._extensions
if automodapi.__name__ in extensions:
# Must do the automodapi on the source to get the automodsumm
# that might be in there
docname = os.path.splitext(fn)[0]
filestr = automodapi.automodapi_replace(fr.read(), app, True, docname, False)
else:
filestr = fr.read()
spl = _automodsummrex.split(filestr)
# 0th entry is the stuff before the first automodsumm line
indent1s = spl[1::5]
mods = spl[2::5]
opssecs = spl[3::5]
indent2s = spl[4::5]
remainders = spl[5::5]
# only grab automodsumm sections and convert them to autosummary with the
# entries for all the public objects
newlines = []
# loop over all automodsumms in this document
for i, (i1, i2, modnm, ops, rem) in enumerate(zip(indent1s, indent2s, mods,
opssecs, remainders)):
allindent = i1 + (' ' if i2 is None else i2)
# filter out functions-only, classes-only, variables-only, and sort
# options if present.
oplines = ops.split('\n')
toskip = []
allowedpkgnms = []
funcsonly = clssonly = varsonly = sort = False
for i, ln in reversed(list(enumerate(oplines))):
if ':functions-only:' in ln:
funcsonly = True
del oplines[i]
if ':classes-only:' in ln:
clssonly = True
del oplines[i]
if ':variables-only:' in ln:
varsonly = True
del oplines[i]
if ':skip:' in ln:
toskip.extend(_str_list_converter(ln.replace(':skip:', '')))
del oplines[i]
if ':allowed-package-names:' in ln:
allowedpkgnms.extend(_str_list_converter(ln.replace(':allowed-package-names:', '')))
del oplines[i]
if ':sort:' in ln:
sort = True
del oplines[i]
if [funcsonly, clssonly, varsonly].count(True) > 1:
msg = ('Defined more than one of functions-only, classes-only, '
'and variables-only. Skipping this directive.')
lnnum = sum([spl[j].count('\n') for j in range(i * 5 + 1)])
logger.warning('[automodsumm] ' + msg, (fn, lnnum))
continue
# Use the currentmodule directive so we can just put the local names
# in the autosummary table. Note that this doesn't always seem to
# actually "take" in Sphinx's eyes, so in `Automodsumm.run`, we have to
# force it internally, as well.
newlines.extend([i1 + '.. currentmodule:: ' + modnm,
'',
'.. autosummary::'])
newlines.extend(oplines)
ols = True if len(allowedpkgnms) == 0 else allowedpkgnms
for nm, fqn, obj in zip(*find_mod_objs(modnm, onlylocals=ols, sort=sort)):
if nm in toskip:
continue
if funcsonly and not inspect.isroutine(obj):
continue
if clssonly and not inspect.isclass(obj):
continue
if varsonly and (inspect.isclass(obj) or inspect.isroutine(obj)):
continue
newlines.append(allindent + nm)
# add one newline at the end of the autosummary block
newlines.append('')
return newlines
def generate_automodsumm_docs(lines, srcfn, app=None, suffix='.rst',
base_path=None, builder=None,
template_dir=None,
inherited_members=False,
included_members=('__init__', '__call__')):
"""
This function is adapted from
`sphinx.ext.autosummary.generate.generate_autosummmary_docs` to
generate source for the automodsumm directives that should be
autosummarized. Unlike generate_autosummary_docs, this function is
called one file at a time.
"""
from sphinx.jinja2glue import BuiltinTemplateLoader
from sphinx.ext.autosummary import import_by_name, get_documenter
from sphinx.util.osutil import ensuredir
from sphinx.util.inspect import safe_getattr
from jinja2 import FileSystemLoader, TemplateNotFound
from jinja2.sandbox import SandboxedEnvironment
from .utils import find_autosummary_in_lines_for_automodsumm as find_autosummary_in_lines
# Create our own templating environment - here we use Astropy's
# templates rather than the default autosummary templates, in order to
# allow docstrings to be shown for methods.
template_dirs = [os.path.join(os.path.dirname(__file__), 'templates'),
os.path.join(base_path, '_templates')]
if builder is not None:
# allow the user to override the templates
template_loader = BuiltinTemplateLoader()
template_loader.init(builder, dirs=template_dirs)
else:
if template_dir:
template_dirs.insert(0, template_dir)
template_loader = FileSystemLoader(template_dirs)
template_env = SandboxedEnvironment(loader=template_loader)
# read
# items = find_autosummary_in_files(sources)
items = find_autosummary_in_lines(lines, filename=srcfn)
if len(items) > 0:
msg = '[automodsumm] {1}: found {0} automodsumm entries to generate'
logger.info(msg.format(len(items), srcfn))
# gennms = [item[0] for item in items]
# if len(gennms) > 20:
# gennms = gennms[:10] + ['...'] + gennms[-10:]
# logger.info('[automodsumm] generating autosummary for: ' + ', '.join(gennms))
# remove possible duplicates
items = list(set(items))
# keep track of new files
new_files = []
# write
for name, path, template_name, inherited_mem, noindex in sorted(items):
if path is None:
# The corresponding autosummary:: directive did not have
# a :toctree: option
continue
path = os.path.abspath(os.path.join(base_path, path))
ensuredir(path)
try:
import_by_name_values = import_by_name(name)
except ImportError as e:
logger.warning('[automodsumm] failed to import %r: %s' % (name, e))
continue
# if block to accommodate Sphinx's v1.2.2 and v1.2.3 respectively
if len(import_by_name_values) == 3:
name, obj, parent = import_by_name_values
elif len(import_by_name_values) == 4:
name, obj, parent, module_name = import_by_name_values
fn = os.path.join(path, name + suffix)
# skip it if it exists
if os.path.isfile(fn):
continue
new_files.append(fn)
f = open(fn, 'w', encoding='utf8')
try:
doc = get_documenter(app, obj, parent)
if template_name is not None:
template = template_env.get_template(template_name)
else:
tmplstr = 'autosummary_core/%s.rst'
try:
template = template_env.get_template(tmplstr % doc.objtype)
except TemplateNotFound:
template = template_env.get_template(tmplstr % 'base')
def get_members_mod(obj, typ, include_public=[]):
"""
typ = None -> all
"""
items = []
for name in dir(obj):
try:
documenter = get_documenter(app, safe_getattr(obj, name), obj)
except AttributeError:
continue
if typ is None or documenter.objtype == typ:
items.append(name)
public = [x for x in items
if x in include_public or not x.startswith('_')]
return public, items
def get_members_class(obj, typ, include_public=[],
include_base=False):
"""
typ = None -> all
include_base -> include attrs that are from a base class
"""
items = []
# using dir gets all of the attributes, including the elements
# from the base class, otherwise use __dict__
if include_base:
names = dir(obj)
else:
names = getattr(obj, '__dict__').keys()
for name in names:
try:
documenter = get_documenter(app, safe_getattr(obj, name), obj)
except AttributeError:
continue
if typ is None or documenter.objtype == typ:
items.append(name)
elif typ == 'attribute' and documenter.objtype == 'property':
# In Sphinx 2.0 and above, properties have a separate
# objtype, but we treat them the same here.
items.append(name)
public = [x for x in items
if x in include_public or not x.startswith('_')]
return public, items
ns = {}
if doc.objtype == 'module':
ns['members'] = get_members_mod(obj, None)
ns['functions'], ns['all_functions'] = \
get_members_mod(obj, 'function')
ns['classes'], ns['all_classes'] = \
get_members_mod(obj, 'class')
ns['exceptions'], ns['all_exceptions'] = \
get_members_mod(obj, 'exception')
elif doc.objtype == 'class':
if inherited_mem is not None:
# option set in this specifc directive
include_base = inherited_mem
else:
# use default value
include_base = inherited_members
ns['members'] = get_members_class(obj, None,
include_base=include_base)
ns['methods'], ns['all_methods'] = \
get_members_class(obj, 'method', included_members,
include_base=include_base)
ns['attributes'], ns['all_attributes'] = \
get_members_class(obj, 'attribute',
include_base=include_base)
ns['methods'].sort()
ns['attributes'].sort()
parts = name.split('.')
if doc.objtype in ('method', 'attribute'):
mod_name = '.'.join(parts[:-2])
cls_name = parts[-2]
obj_name = '.'.join(parts[-2:])
ns['class'] = cls_name
else:
mod_name, obj_name = '.'.join(parts[:-1]), parts[-1]
ns['noindex'] = noindex
ns['fullname'] = name
ns['module'] = mod_name
ns['objname'] = obj_name
ns['name'] = parts[-1]
ns['objtype'] = doc.objtype
ns['underline'] = len(obj_name) * '='
# We now check whether a file for reference footnotes exists for
# the module being documented. We first check if the
# current module is a file or a directory, as this will give a
# different path for the reference file. For example, if
# documenting astropy.wcs then the reference file is at
# ../wcs/references.txt, while if we are documenting
# astropy.config.logging_helper (which is at
# astropy/config/logging_helper.py) then the reference file is set
# to ../config/references.txt
if '.' in mod_name:
mod_name_dir = mod_name.split('.', 1)[1].replace('.', os.sep)
else:
mod_name_dir = mod_name
if (not os.path.isdir(os.path.join(base_path, mod_name_dir))
and os.path.isdir(os.path.join(base_path, mod_name_dir.rsplit(os.sep, 1)[0]))):
mod_name_dir = mod_name_dir.rsplit(os.sep, 1)[0]
# We then have to check whether it exists, and if so, we pass it
# to the template.
if os.path.exists(os.path.join(base_path, mod_name_dir, 'references.txt')):
# An important subtlety here is that the path we pass in has
# to be relative to the file being generated, so we have to
# figure out the right number of '..'s
ndirsback = path.replace(str(base_path), '').count(os.sep)
ref_file_rel_segments = ['..'] * ndirsback
ref_file_rel_segments.append(mod_name_dir)
ref_file_rel_segments.append('references.txt')
ns['referencefile'] = os.path.join(*ref_file_rel_segments).replace(os.sep, '/')
rendered = template.render(**ns)
f.write(cleanup_whitespace(rendered))
finally:
f.close()
def setup(app):
# need autodoc fixes
# Note: we use __name__ here instead of just writing the module name in
# case this extension is bundled into another package
from . import autodoc_enhancements
app.setup_extension(autodoc_enhancements.__name__)
# need inheritance-diagram for automod-diagram
app.setup_extension('sphinx.ext.inheritance_diagram')
app.add_directive('automod-diagram', Automoddiagram)
app.add_directive('automodsumm', Automodsumm)
app.connect('builder-inited', process_automodsumm_generation)
app.add_config_value('automodsumm_writereprocessed', False, True)
app.add_config_value('automodsumm_inherited_members', False, 'env')
app.add_config_value(
'automodsumm_included_members', ['__init__', '__call__'], 'env')
return {'parallel_read_safe': True,
'parallel_write_safe': True}
|