mirror of
https://github.com/sphinx-doc/sphinx.git
synced 2025-02-25 18:55:22 -06:00
Merge branch '2.0' into 6165_tab_width
This commit is contained in:
7
CHANGES
7
CHANGES
@@ -11,11 +11,13 @@ Incompatible changes
|
||||
API directly
|
||||
* #6230: The anchor of term in glossary directive is changed if it is consisted
|
||||
by non-ASCII characters
|
||||
* #4550: html: Centering tables by default using CSS
|
||||
|
||||
Deprecated
|
||||
----------
|
||||
|
||||
* ``sphinx.builders.latex.LaTeXBuilder.apply_transforms()``
|
||||
* ``sphinx.builders._epub_base.EpubBuilder.esc()``
|
||||
* ``sphinx.directives.Acks``
|
||||
* ``sphinx.directives.Author``
|
||||
* ``sphinx.directives.Centered``
|
||||
@@ -40,6 +42,8 @@ Deprecated
|
||||
* ``sphinx.domains.std.StandardDomain.note_citation_refs()``
|
||||
* ``sphinx.domains.std.StandardDomain.note_labels()``
|
||||
* ``sphinx.environment.NoUri``
|
||||
* ``sphinx.ext.apidoc.format_directive()``
|
||||
* ``sphinx.ext.apidoc.format_heading()``
|
||||
* ``sphinx.ext.autodoc.importer.MockFinder``
|
||||
* ``sphinx.ext.autodoc.importer.MockLoader``
|
||||
* ``sphinx.ext.autodoc.importer.mock()``
|
||||
@@ -91,6 +95,9 @@ Bugs fixed
|
||||
* #6213: ifconfig: contents after headings are not shown
|
||||
* commented term in glossary directive is wrongly recognized
|
||||
* #6299: rst domain: rst:directive directive generates waste space
|
||||
* #6331: man: invalid output when doctest follows rubric
|
||||
* #6351: "Hyperlink target is not referenced" message is shown even if
|
||||
referenced
|
||||
* #6165: autodoc: ``tab_width`` setting of docutils has been ignored
|
||||
|
||||
Testing
|
||||
|
||||
@@ -31,6 +31,11 @@ The following is a list of deprecated interfaces.
|
||||
- 4.0
|
||||
- N/A
|
||||
|
||||
* - ``sphinx.builders._epub_base.EpubBuilder.esc()``
|
||||
- 2.1
|
||||
- 4.0
|
||||
- ``html.escape()``
|
||||
|
||||
* - ``sphinx.directives.Acks``
|
||||
- 2.1
|
||||
- 4.0
|
||||
@@ -156,6 +161,15 @@ The following is a list of deprecated interfaces.
|
||||
- 2.1
|
||||
- 4.0
|
||||
- ``sphinx.errors.NoUri``
|
||||
* - ``sphinx.ext.apidoc.format_directive()``
|
||||
- 2.1
|
||||
- 4.0
|
||||
- N/A
|
||||
|
||||
* - ``sphinx.ext.apidoc.format_heading()``
|
||||
- 2.1
|
||||
- 4.0
|
||||
- N/A
|
||||
|
||||
* - ``sphinx.ext.autodoc.importer.MockFinder``
|
||||
- 2.1
|
||||
|
||||
@@ -55,6 +55,11 @@ strict_optional = False
|
||||
filterwarnings =
|
||||
all
|
||||
ignore::DeprecationWarning:docutils.io
|
||||
markers =
|
||||
sphinx
|
||||
apidoc
|
||||
setup_command
|
||||
test_params
|
||||
|
||||
[coverage:run]
|
||||
branch = True
|
||||
|
||||
3
setup.py
3
setup.py
@@ -38,6 +38,9 @@ extras_require = {
|
||||
':sys_platform=="win32"': [
|
||||
'colorama>=0.3.5',
|
||||
],
|
||||
'docs': [
|
||||
'sphinxcontrib-websupport',
|
||||
],
|
||||
'test': [
|
||||
'pytest',
|
||||
'pytest-cov',
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
:license: BSD, see LICENSE for details.
|
||||
"""
|
||||
|
||||
import html
|
||||
import os
|
||||
import re
|
||||
import warnings
|
||||
@@ -178,7 +179,9 @@ class EpubBuilder(StandaloneHTMLBuilder):
|
||||
def esc(self, name):
|
||||
# type: (str) -> str
|
||||
"""Replace all characters not allowed in text an attribute values."""
|
||||
# Like cgi.escape, but also replace apostrophe
|
||||
warnings.warn(
|
||||
'%s.esc() is deprecated. Use html.escape() instead.' % self.__class__.__name__,
|
||||
RemovedInSphinx40Warning)
|
||||
name = name.replace('&', '&')
|
||||
name = name.replace('<', '<')
|
||||
name = name.replace('>', '>')
|
||||
@@ -201,8 +204,8 @@ class EpubBuilder(StandaloneHTMLBuilder):
|
||||
if (self.toctree_template % level) in classes:
|
||||
result.append({
|
||||
'level': level,
|
||||
'refuri': self.esc(refuri),
|
||||
'text': ssp(self.esc(doctree.astext()))
|
||||
'refuri': html.escape(refuri),
|
||||
'text': ssp(html.escape(doctree.astext()))
|
||||
})
|
||||
break
|
||||
elif isinstance(doctree, nodes.Element):
|
||||
@@ -241,21 +244,21 @@ class EpubBuilder(StandaloneHTMLBuilder):
|
||||
"""
|
||||
refnodes.insert(0, {
|
||||
'level': 1,
|
||||
'refuri': self.esc(self.config.master_doc + self.out_suffix),
|
||||
'text': ssp(self.esc(
|
||||
'refuri': html.escape(self.config.master_doc + self.out_suffix),
|
||||
'text': ssp(html.escape(
|
||||
self.env.titles[self.config.master_doc].astext()))
|
||||
})
|
||||
for file, text in reversed(self.config.epub_pre_files):
|
||||
refnodes.insert(0, {
|
||||
'level': 1,
|
||||
'refuri': self.esc(file),
|
||||
'text': ssp(self.esc(text))
|
||||
'refuri': html.escape(file),
|
||||
'text': ssp(html.escape(text))
|
||||
})
|
||||
for file, text in self.config.epub_post_files:
|
||||
refnodes.append({
|
||||
'level': 1,
|
||||
'refuri': self.esc(file),
|
||||
'text': ssp(self.esc(text))
|
||||
'refuri': html.escape(file),
|
||||
'text': ssp(html.escape(text))
|
||||
})
|
||||
|
||||
def fix_fragment(self, prefix, fragment):
|
||||
@@ -511,15 +514,15 @@ class EpubBuilder(StandaloneHTMLBuilder):
|
||||
file properly escaped.
|
||||
"""
|
||||
metadata = {} # type: Dict[str, Any]
|
||||
metadata['title'] = self.esc(self.config.epub_title)
|
||||
metadata['author'] = self.esc(self.config.epub_author)
|
||||
metadata['uid'] = self.esc(self.config.epub_uid)
|
||||
metadata['lang'] = self.esc(self.config.epub_language)
|
||||
metadata['publisher'] = self.esc(self.config.epub_publisher)
|
||||
metadata['copyright'] = self.esc(self.config.epub_copyright)
|
||||
metadata['scheme'] = self.esc(self.config.epub_scheme)
|
||||
metadata['id'] = self.esc(self.config.epub_identifier)
|
||||
metadata['date'] = self.esc(format_date("%Y-%m-%d"))
|
||||
metadata['title'] = html.escape(self.config.epub_title)
|
||||
metadata['author'] = html.escape(self.config.epub_author)
|
||||
metadata['uid'] = html.escape(self.config.epub_uid)
|
||||
metadata['lang'] = html.escape(self.config.epub_language)
|
||||
metadata['publisher'] = html.escape(self.config.epub_publisher)
|
||||
metadata['copyright'] = html.escape(self.config.epub_copyright)
|
||||
metadata['scheme'] = html.escape(self.config.epub_scheme)
|
||||
metadata['id'] = html.escape(self.config.epub_identifier)
|
||||
metadata['date'] = html.escape(format_date("%Y-%m-%d"))
|
||||
metadata['manifest_items'] = []
|
||||
metadata['spines'] = []
|
||||
metadata['guides'] = []
|
||||
@@ -566,9 +569,9 @@ class EpubBuilder(StandaloneHTMLBuilder):
|
||||
type='epub', subtype='unknown_project_files')
|
||||
continue
|
||||
filename = filename.replace(os.sep, '/')
|
||||
item = ManifestItem(self.esc(filename),
|
||||
self.esc(self.make_id(filename)),
|
||||
self.esc(self.media_types[ext]))
|
||||
item = ManifestItem(html.escape(filename),
|
||||
html.escape(self.make_id(filename)),
|
||||
html.escape(self.media_types[ext]))
|
||||
metadata['manifest_items'].append(item)
|
||||
self.files.append(filename)
|
||||
|
||||
@@ -579,21 +582,21 @@ class EpubBuilder(StandaloneHTMLBuilder):
|
||||
continue
|
||||
if refnode['refuri'] in self.ignored_files:
|
||||
continue
|
||||
spine = Spine(self.esc(self.make_id(refnode['refuri'])), True)
|
||||
spine = Spine(html.escape(self.make_id(refnode['refuri'])), True)
|
||||
metadata['spines'].append(spine)
|
||||
spinefiles.add(refnode['refuri'])
|
||||
for info in self.domain_indices:
|
||||
spine = Spine(self.esc(self.make_id(info[0] + self.out_suffix)), True)
|
||||
spine = Spine(html.escape(self.make_id(info[0] + self.out_suffix)), True)
|
||||
metadata['spines'].append(spine)
|
||||
spinefiles.add(info[0] + self.out_suffix)
|
||||
if self.use_index:
|
||||
spine = Spine(self.esc(self.make_id('genindex' + self.out_suffix)), True)
|
||||
spine = Spine(html.escape(self.make_id('genindex' + self.out_suffix)), True)
|
||||
metadata['spines'].append(spine)
|
||||
spinefiles.add('genindex' + self.out_suffix)
|
||||
# add auto generated files
|
||||
for name in self.files:
|
||||
if name not in spinefiles and name.endswith(self.out_suffix):
|
||||
spine = Spine(self.esc(self.make_id(name)), False)
|
||||
spine = Spine(html.escape(self.make_id(name)), False)
|
||||
metadata['spines'].append(spine)
|
||||
|
||||
# add the optional cover
|
||||
@@ -601,18 +604,18 @@ class EpubBuilder(StandaloneHTMLBuilder):
|
||||
if self.config.epub_cover:
|
||||
image, html_tmpl = self.config.epub_cover
|
||||
image = image.replace(os.sep, '/')
|
||||
metadata['cover'] = self.esc(self.make_id(image))
|
||||
metadata['cover'] = html.escape(self.make_id(image))
|
||||
if html_tmpl:
|
||||
spine = Spine(self.esc(self.make_id(self.coverpage_name)), True)
|
||||
spine = Spine(html.escape(self.make_id(self.coverpage_name)), True)
|
||||
metadata['spines'].insert(0, spine)
|
||||
if self.coverpage_name not in self.files:
|
||||
ext = path.splitext(self.coverpage_name)[-1]
|
||||
self.files.append(self.coverpage_name)
|
||||
item = ManifestItem(self.esc(self.coverpage_name),
|
||||
self.esc(self.make_id(self.coverpage_name)),
|
||||
self.esc(self.media_types[ext]))
|
||||
item = ManifestItem(html.escape(self.coverpage_name),
|
||||
html.escape(self.make_id(self.coverpage_name)),
|
||||
html.escape(self.media_types[ext]))
|
||||
metadata['manifest_items'].append(item)
|
||||
ctx = {'image': self.esc(image), 'title': self.config.project}
|
||||
ctx = {'image': html.escape(image), 'title': self.config.project}
|
||||
self.handle_page(
|
||||
path.splitext(self.coverpage_name)[0], ctx, html_tmpl)
|
||||
spinefiles.add(self.coverpage_name)
|
||||
@@ -628,17 +631,17 @@ class EpubBuilder(StandaloneHTMLBuilder):
|
||||
auto_add_cover = False
|
||||
if type == 'toc':
|
||||
auto_add_toc = False
|
||||
metadata['guides'].append(Guide(self.esc(type),
|
||||
self.esc(title),
|
||||
self.esc(uri)))
|
||||
metadata['guides'].append(Guide(html.escape(type),
|
||||
html.escape(title),
|
||||
html.escape(uri)))
|
||||
if auto_add_cover and html_tmpl:
|
||||
metadata['guides'].append(Guide('cover',
|
||||
self.guide_titles['cover'],
|
||||
self.esc(self.coverpage_name)))
|
||||
html.escape(self.coverpage_name)))
|
||||
if auto_add_toc and self.refnodes:
|
||||
metadata['guides'].append(Guide('toc',
|
||||
self.guide_titles['toc'],
|
||||
self.esc(self.refnodes[0]['refuri'])))
|
||||
html.escape(self.refnodes[0]['refuri'])))
|
||||
|
||||
# write the project file
|
||||
copy_asset_file(path.join(self.template_dir, 'content.opf_t'),
|
||||
@@ -707,7 +710,7 @@ class EpubBuilder(StandaloneHTMLBuilder):
|
||||
"""
|
||||
metadata = {} # type: Dict[str, Any]
|
||||
metadata['uid'] = self.config.epub_uid
|
||||
metadata['title'] = self.esc(self.config.epub_title)
|
||||
metadata['title'] = html.escape(self.config.epub_title)
|
||||
metadata['level'] = level
|
||||
metadata['navpoints'] = navpoints
|
||||
return metadata
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
:license: BSD, see LICENSE for details.
|
||||
"""
|
||||
|
||||
import html
|
||||
import warnings
|
||||
from collections import namedtuple
|
||||
from os import path
|
||||
@@ -98,12 +99,12 @@ class Epub3Builder(_epub_base.EpubBuilder):
|
||||
writing_mode = self.config.epub_writing_mode
|
||||
|
||||
metadata = super().content_metadata()
|
||||
metadata['description'] = self.esc(self.config.epub_description)
|
||||
metadata['contributor'] = self.esc(self.config.epub_contributor)
|
||||
metadata['description'] = html.escape(self.config.epub_description)
|
||||
metadata['contributor'] = html.escape(self.config.epub_contributor)
|
||||
metadata['page_progression_direction'] = PAGE_PROGRESSION_DIRECTIONS.get(writing_mode)
|
||||
metadata['ibook_scroll_axis'] = IBOOK_SCROLL_AXIS.get(writing_mode)
|
||||
metadata['date'] = self.esc(format_date("%Y-%m-%dT%H:%M:%SZ"))
|
||||
metadata['version'] = self.esc(self.config.version)
|
||||
metadata['date'] = html.escape(format_date("%Y-%m-%dT%H:%M:%SZ"))
|
||||
metadata['version'] = html.escape(self.config.version)
|
||||
metadata['epub_version'] = self.config.epub_version
|
||||
return metadata
|
||||
|
||||
@@ -166,8 +167,8 @@ class Epub3Builder(_epub_base.EpubBuilder):
|
||||
properly escaped.
|
||||
"""
|
||||
metadata = {} # type: Dict
|
||||
metadata['lang'] = self.esc(self.config.epub_language)
|
||||
metadata['toc_locale'] = self.esc(self.guide_titles['toc'])
|
||||
metadata['lang'] = html.escape(self.config.epub_language)
|
||||
metadata['toc_locale'] = html.escape(self.guide_titles['toc'])
|
||||
metadata['navlist'] = navlist
|
||||
return metadata
|
||||
|
||||
|
||||
@@ -19,15 +19,18 @@ import glob
|
||||
import locale
|
||||
import os
|
||||
import sys
|
||||
import warnings
|
||||
from fnmatch import fnmatch
|
||||
from os import path
|
||||
|
||||
import sphinx.locale
|
||||
from sphinx import __display_version__, package_dir
|
||||
from sphinx.cmd.quickstart import EXTENSIONS
|
||||
from sphinx.deprecation import RemovedInSphinx40Warning
|
||||
from sphinx.locale import __
|
||||
from sphinx.util import rst
|
||||
from sphinx.util.osutil import FileAvoidWrite, ensuredir
|
||||
from sphinx.util.template import ReSTRenderer
|
||||
|
||||
if False:
|
||||
# For type annotation
|
||||
@@ -47,6 +50,8 @@ else:
|
||||
INITPY = '__init__.py'
|
||||
PY_SUFFIXES = {'.py', '.pyx'}
|
||||
|
||||
template_dir = path.join(package_dir, 'templates', 'apidoc')
|
||||
|
||||
|
||||
def makename(package, module):
|
||||
# type: (str, str) -> str
|
||||
@@ -79,6 +84,8 @@ def write_file(name, text, opts):
|
||||
def format_heading(level, text, escape=True):
|
||||
# type: (int, str, bool) -> str
|
||||
"""Create a heading of <level> [1, 2 or 3 supported]."""
|
||||
warnings.warn('format_warning() is deprecated.',
|
||||
RemovedInSphinx40Warning)
|
||||
if escape:
|
||||
text = rst.escape(text)
|
||||
underlining = ['=', '-', '~', ][level - 1] * len(text)
|
||||
@@ -88,100 +95,79 @@ def format_heading(level, text, escape=True):
|
||||
def format_directive(module, package=None):
|
||||
# type: (str, str) -> str
|
||||
"""Create the automodule directive and add the options."""
|
||||
warnings.warn('format_directive() is deprecated.',
|
||||
RemovedInSphinx40Warning)
|
||||
directive = '.. automodule:: %s\n' % makename(package, module)
|
||||
for option in OPTIONS:
|
||||
directive += ' :%s:\n' % option
|
||||
return directive
|
||||
|
||||
|
||||
def create_module_file(package, module, opts):
|
||||
def create_module_file(package, basename, opts):
|
||||
# type: (str, str, Any) -> None
|
||||
"""Build the text of the file and write the file."""
|
||||
if not opts.noheadings:
|
||||
text = format_heading(1, '%s module' % module)
|
||||
else:
|
||||
text = ''
|
||||
# text += format_heading(2, ':mod:`%s` Module' % module)
|
||||
text += format_directive(module, package)
|
||||
write_file(makename(package, module), text, opts)
|
||||
qualname = makename(package, basename)
|
||||
context = {
|
||||
'show_headings': not opts.noheadings,
|
||||
'basename': basename,
|
||||
'qualname': qualname,
|
||||
'automodule_options': OPTIONS,
|
||||
}
|
||||
text = ReSTRenderer(template_dir).render('module.rst', context)
|
||||
write_file(qualname, text, opts)
|
||||
|
||||
|
||||
def create_package_file(root, master_package, subroot, py_files, opts, subs, is_namespace, excludes=[]): # NOQA
|
||||
# type: (str, str, str, List[str], Any, List[str], bool, List[str]) -> None
|
||||
"""Build the text of the file and write the file."""
|
||||
text = format_heading(1, ('%s package' if not is_namespace else "%s namespace")
|
||||
% makename(master_package, subroot))
|
||||
|
||||
if opts.modulefirst and not is_namespace:
|
||||
text += format_directive(subroot, master_package)
|
||||
text += '\n'
|
||||
|
||||
# build a list of directories that are szvpackages (contain an INITPY file)
|
||||
# and also checks the INITPY file is not empty, or there are other python
|
||||
# source files in that folder.
|
||||
# (depending on settings - but shall_skip() takes care of that)
|
||||
subs = [sub for sub in subs if not
|
||||
shall_skip(path.join(root, sub, INITPY), opts, excludes)]
|
||||
# if there are some package directories, add a TOC for theses subpackages
|
||||
if subs:
|
||||
text += format_heading(2, 'Subpackages')
|
||||
text += '.. toctree::\n\n'
|
||||
for sub in subs:
|
||||
text += ' %s.%s\n' % (makename(master_package, subroot), sub)
|
||||
text += '\n'
|
||||
|
||||
submods = [path.splitext(sub)[0] for sub in py_files
|
||||
if not shall_skip(path.join(root, sub), opts, excludes) and
|
||||
sub != INITPY]
|
||||
if submods:
|
||||
text += format_heading(2, 'Submodules')
|
||||
if opts.separatemodules:
|
||||
text += '.. toctree::\n\n'
|
||||
for submod in submods:
|
||||
modfile = makename(master_package, makename(subroot, submod))
|
||||
text += ' %s\n' % modfile
|
||||
|
||||
# generate separate file for this module
|
||||
if not opts.noheadings:
|
||||
filetext = format_heading(1, '%s module' % modfile)
|
||||
else:
|
||||
filetext = ''
|
||||
filetext += format_directive(makename(subroot, submod),
|
||||
master_package)
|
||||
write_file(modfile, filetext, opts)
|
||||
else:
|
||||
for submod in submods:
|
||||
modfile = makename(master_package, makename(subroot, submod))
|
||||
if not opts.noheadings:
|
||||
text += format_heading(2, '%s module' % modfile)
|
||||
text += format_directive(makename(subroot, submod),
|
||||
master_package)
|
||||
text += '\n'
|
||||
text += '\n'
|
||||
|
||||
if not opts.modulefirst and not is_namespace:
|
||||
text += format_heading(2, 'Module contents')
|
||||
text += format_directive(subroot, master_package)
|
||||
# build a list of sub packages (directories containing an INITPY file)
|
||||
subpackages = [sub for sub in subs if not
|
||||
shall_skip(path.join(root, sub, INITPY), opts, excludes)]
|
||||
subpackages = [makename(makename(master_package, subroot), pkgname)
|
||||
for pkgname in subpackages]
|
||||
# build a list of sub modules
|
||||
submodules = [path.splitext(sub)[0] for sub in py_files
|
||||
if not shall_skip(path.join(root, sub), opts, excludes) and
|
||||
sub != INITPY]
|
||||
submodules = [makename(master_package, makename(subroot, modname))
|
||||
for modname in submodules]
|
||||
|
||||
context = {
|
||||
'pkgname': makename(master_package, subroot),
|
||||
'subpackages': subpackages,
|
||||
'submodules': submodules,
|
||||
'is_namespace': is_namespace,
|
||||
'modulefirst': opts.modulefirst,
|
||||
'separatemodules': opts.separatemodules,
|
||||
'automodule_options': OPTIONS,
|
||||
'show_headings': not opts.noheadings,
|
||||
}
|
||||
text = ReSTRenderer(template_dir).render('package.rst', context)
|
||||
write_file(makename(master_package, subroot), text, opts)
|
||||
|
||||
if submodules and opts.separatemodules:
|
||||
for submodule in submodules:
|
||||
create_module_file(None, submodule, opts)
|
||||
|
||||
|
||||
def create_modules_toc_file(modules, opts, name='modules'):
|
||||
# type: (List[str], Any, str) -> None
|
||||
"""Create the module's index."""
|
||||
text = format_heading(1, '%s' % opts.header, escape=False)
|
||||
text += '.. toctree::\n'
|
||||
text += ' :maxdepth: %s\n\n' % opts.maxdepth
|
||||
|
||||
modules.sort()
|
||||
prev_module = ''
|
||||
for module in modules:
|
||||
for module in modules[:]:
|
||||
# look if the module is a subpackage and, if yes, ignore it
|
||||
if module.startswith(prev_module + '.'):
|
||||
continue
|
||||
prev_module = module
|
||||
text += ' %s\n' % module
|
||||
modules.remove(module)
|
||||
else:
|
||||
prev_module = module
|
||||
|
||||
context = {
|
||||
'header': opts.header,
|
||||
'maxdepth': opts.maxdepth,
|
||||
'docnames': modules,
|
||||
}
|
||||
text = ReSTRenderer(template_dir).render('toc.rst', context)
|
||||
write_file(name, text, opts)
|
||||
|
||||
|
||||
|
||||
@@ -32,6 +32,7 @@ if False:
|
||||
# For type annotation
|
||||
from typing import Any, Dict # NOQA
|
||||
from pygments.formatter import Formatter # NOQA
|
||||
from pygments.style import Style # NOQA
|
||||
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
@@ -69,16 +70,8 @@ class PygmentsBridge:
|
||||
def __init__(self, dest='html', stylename='sphinx', trim_doctest_flags=None):
|
||||
# type: (str, str, bool) -> None
|
||||
self.dest = dest
|
||||
if stylename is None or stylename == 'sphinx':
|
||||
style = SphinxStyle
|
||||
elif stylename == 'none':
|
||||
style = NoneStyle
|
||||
elif '.' in stylename:
|
||||
module, stylename = stylename.rsplit('.', 1)
|
||||
style = getattr(__import__(module, None, None, ['__name__']),
|
||||
stylename)
|
||||
else:
|
||||
style = get_style_by_name(stylename)
|
||||
|
||||
style = self.get_style(stylename)
|
||||
self.formatter_args = {'style': style} # type: Dict[str, Any]
|
||||
if dest == 'html':
|
||||
self.formatter = self.html_formatter
|
||||
@@ -91,6 +84,18 @@ class PygmentsBridge:
|
||||
warnings.warn('trim_doctest_flags option for PygmentsBridge is now deprecated.',
|
||||
RemovedInSphinx30Warning, stacklevel=2)
|
||||
|
||||
def get_style(self, stylename):
|
||||
# type: (str) -> Style
|
||||
if stylename is None or stylename == 'sphinx':
|
||||
return SphinxStyle
|
||||
elif stylename == 'none':
|
||||
return NoneStyle
|
||||
elif '.' in stylename:
|
||||
module, stylename = stylename.rsplit('.', 1)
|
||||
return getattr(__import__(module, None, None, ['__name__']), stylename)
|
||||
else:
|
||||
return get_style_by_name(stylename)
|
||||
|
||||
def get_formatter(self, **kwargs):
|
||||
# type: (Any) -> Formatter
|
||||
kwargs.update(self.formatter_args)
|
||||
@@ -110,11 +115,8 @@ class PygmentsBridge:
|
||||
return '\\begin{Verbatim}[commandchars=\\\\\\{\\}]\n' + \
|
||||
source + '\\end{Verbatim}\n'
|
||||
|
||||
def highlight_block(self, source, lang, opts=None, location=None, force=False, **kwargs):
|
||||
# type: (str, str, Any, Any, bool, Any) -> str
|
||||
if not isinstance(source, str):
|
||||
source = source.decode()
|
||||
|
||||
def get_lexer(self, source, lang, opts=None, location=None):
|
||||
# type: (str, str, Any, Any) -> Lexer
|
||||
# find out which lexer to use
|
||||
if lang in ('py', 'python'):
|
||||
if source.startswith('>>>'):
|
||||
@@ -145,6 +147,15 @@ class PygmentsBridge:
|
||||
else:
|
||||
lexer.add_filter('raiseonerror')
|
||||
|
||||
return lexer
|
||||
|
||||
def highlight_block(self, source, lang, opts=None, location=None, force=False, **kwargs):
|
||||
# type: (str, str, Any, Any, bool, Any) -> str
|
||||
if not isinstance(source, str):
|
||||
source = source.decode()
|
||||
|
||||
lexer = self.get_lexer(source, lang, opts, location)
|
||||
|
||||
# trim doctest options if wanted
|
||||
if isinstance(lexer, PythonConsoleLexer) and self.trim_doctest_flags:
|
||||
source = doctest.blankline_re.sub('', source)
|
||||
@@ -165,6 +176,7 @@ class PygmentsBridge:
|
||||
type='misc', subtype='highlighting_failure',
|
||||
location=location)
|
||||
hlsource = highlight(source, lexers['none'], formatter)
|
||||
|
||||
if self.dest == 'html':
|
||||
return hlsource
|
||||
else:
|
||||
|
||||
11
sphinx/io.py
11
sphinx/io.py
@@ -16,6 +16,7 @@ from docutils.io import FileInput, NullOutput
|
||||
from docutils.parsers.rst import Parser as RSTParser
|
||||
from docutils.readers import standalone
|
||||
from docutils.statemachine import StringList, string2lines
|
||||
from docutils.transforms.references import DanglingReferences
|
||||
from docutils.writers import UnfilteredWriter
|
||||
|
||||
from sphinx.deprecation import RemovedInSphinx30Warning
|
||||
@@ -64,7 +65,15 @@ class SphinxBaseReader(standalone.Reader):
|
||||
|
||||
def get_transforms(self):
|
||||
# type: () -> List[Type[Transform]]
|
||||
return super().get_transforms() + self.transforms
|
||||
transforms = super().get_transforms() + self.transforms
|
||||
|
||||
# remove transforms which is not needed for Sphinx
|
||||
unused = [DanglingReferences]
|
||||
for transform in unused:
|
||||
if transform in transforms:
|
||||
transforms.remove(transform)
|
||||
|
||||
return transforms
|
||||
|
||||
def new_document(self):
|
||||
# type: () -> nodes.document
|
||||
|
||||
9
sphinx/templates/apidoc/module.rst
Normal file
9
sphinx/templates/apidoc/module.rst
Normal file
@@ -0,0 +1,9 @@
|
||||
{%- if show_headings %}
|
||||
{{- [basename, "module"] | join(' ') | e | heading }}
|
||||
|
||||
{% endif -%}
|
||||
.. automodule:: {{ qualname }}
|
||||
{%- for option in automodule_options %}
|
||||
:{{ option }}:
|
||||
{%- endfor %}
|
||||
|
||||
52
sphinx/templates/apidoc/package.rst
Normal file
52
sphinx/templates/apidoc/package.rst
Normal file
@@ -0,0 +1,52 @@
|
||||
{%- macro automodule(modname, options) -%}
|
||||
.. automodule:: {{ modname }}
|
||||
{%- for option in options %}
|
||||
:{{ option }}:
|
||||
{%- endfor %}
|
||||
{%- endmacro %}
|
||||
|
||||
{%- macro toctree(docnames) -%}
|
||||
.. toctree::
|
||||
{% for docname in docnames %}
|
||||
{{ docname }}
|
||||
{%- endfor %}
|
||||
{%- endmacro %}
|
||||
|
||||
{%- if is_namespace %}
|
||||
{{- [pkgname, "namespace"] | join(" ") | e | heading }}
|
||||
{% else %}
|
||||
{{- [pkgname, "package"] | join(" ") | e | heading }}
|
||||
{% endif %}
|
||||
|
||||
{%- if modulefirst and not is_namespace %}
|
||||
{{ automodule(pkgname, automodule_options) }}
|
||||
{% endif %}
|
||||
|
||||
{%- if subpackages %}
|
||||
Subpackages
|
||||
-----------
|
||||
|
||||
{{ toctree(subpackages) }}
|
||||
{% endif %}
|
||||
|
||||
{%- if submodules %}
|
||||
Submodules
|
||||
----------
|
||||
{% if separatemodules %}
|
||||
{{ toctree(submodules) }}
|
||||
{%- else %}
|
||||
{%- for submodule in submodules %}
|
||||
{% if show_headings %}
|
||||
{{- [submodule, "module"] | join(" ") | e | heading(2) }}
|
||||
{% endif %}
|
||||
{{ automodule(submodule, automodule_options) }}
|
||||
{%- endfor %}
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
|
||||
{%- if not modulefirst and not is_namespace %}
|
||||
Module contents
|
||||
---------------
|
||||
|
||||
{{ automodule(pkgname, automodule_options) }}
|
||||
{% endif %}
|
||||
8
sphinx/templates/apidoc/toc.rst
Normal file
8
sphinx/templates/apidoc/toc.rst
Normal file
@@ -0,0 +1,8 @@
|
||||
{{ header | heading }}
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: {{ maxdepth }}
|
||||
{% for docname in docnames %}
|
||||
{{ docname }}
|
||||
{%- endfor %}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
\documentclass[12pt]{article}
|
||||
\usepackage[utf8x]{inputenc}
|
||||
\usepackage[utf8]{inputenc}
|
||||
\usepackage{amsmath}
|
||||
\usepackage{amsthm}
|
||||
\usepackage{amssymb}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
\documentclass[12pt]{article}
|
||||
\usepackage[utf8x]{inputenc}
|
||||
\usepackage[utf8]{inputenc}
|
||||
\usepackage{amsmath}
|
||||
\usepackage{amsthm}
|
||||
\usepackage{amssymb}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
\begin{savenotes}\sphinxatlongtablestart\begin{longtable}
|
||||
<%- if table.align == 'center' -%>
|
||||
<%- if table.align in ('center', 'default') -%>
|
||||
[c]
|
||||
<%- elif table.align == 'left' -%>
|
||||
[l]
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
\begin{savenotes}\sphinxattablestart
|
||||
<% if table.align -%>
|
||||
<%- if table.align == 'center' -%>
|
||||
<%- if table.align in ('center', 'default') -%>
|
||||
\centering
|
||||
<%- elif table.align == 'left' -%>
|
||||
\raggedright
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
\begin{savenotes}\sphinxattablestart
|
||||
<% if table.align -%>
|
||||
<%- if table.align == 'center' -%>
|
||||
<%- if table.align in ('center', 'default') -%>
|
||||
\centering
|
||||
<%- elif table.align == 'left' -%>
|
||||
\raggedright
|
||||
|
||||
@@ -289,6 +289,12 @@ img.align-center, .figure.align-center, object.align-center {
|
||||
margin-right: auto;
|
||||
}
|
||||
|
||||
img.align-default, .figure.align-default {
|
||||
display: block;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
}
|
||||
|
||||
.align-left {
|
||||
text-align: left;
|
||||
}
|
||||
@@ -297,6 +303,10 @@ img.align-center, .figure.align-center, object.align-center {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.align-default {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.align-right {
|
||||
text-align: right;
|
||||
}
|
||||
@@ -368,6 +378,11 @@ table.align-center {
|
||||
margin-right: auto;
|
||||
}
|
||||
|
||||
table.align-default {
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
}
|
||||
|
||||
table caption span.caption-number {
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
@@ -293,7 +293,7 @@ class FigureAligner(SphinxTransform):
|
||||
# type: (Any) -> None
|
||||
matcher = NodeMatcher(nodes.table, nodes.figure)
|
||||
for node in self.document.traverse(matcher): # type: nodes.Element
|
||||
node.setdefault('align', 'center')
|
||||
node.setdefault('align', 'default')
|
||||
|
||||
|
||||
class FilterSystemMessages(SphinxTransform):
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
"""
|
||||
|
||||
from docutils import nodes
|
||||
from docutils.transforms.references import Substitutions
|
||||
from docutils.transforms.references import DanglingReferences, Substitutions
|
||||
|
||||
from sphinx.transforms import SphinxTransform
|
||||
|
||||
@@ -31,6 +31,22 @@ class SubstitutionDefinitionsRemover(SphinxTransform):
|
||||
node.parent.remove(node)
|
||||
|
||||
|
||||
class SphinxDanglingReferences(DanglingReferences):
|
||||
"""DanglingReferences transform which does not output info messages."""
|
||||
|
||||
def apply(self, **kwargs):
|
||||
# type: (Any) -> None
|
||||
try:
|
||||
reporter = self.document.reporter
|
||||
report_level = reporter.report_level
|
||||
|
||||
# suppress INFO level messages for a while
|
||||
reporter.report_level = max(reporter.WARNING_LEVEL, reporter.report_level)
|
||||
super().apply()
|
||||
finally:
|
||||
reporter.report_level = report_level
|
||||
|
||||
|
||||
class SphinxDomains(SphinxTransform):
|
||||
"""Collect objects to Sphinx domains for cross references."""
|
||||
default_priority = 850
|
||||
@@ -44,6 +60,7 @@ class SphinxDomains(SphinxTransform):
|
||||
def setup(app):
|
||||
# type: (Sphinx) -> Dict[str, Any]
|
||||
app.add_transform(SubstitutionDefinitionsRemover)
|
||||
app.add_transform(SphinxDanglingReferences)
|
||||
app.add_transform(SphinxDomains)
|
||||
|
||||
return {
|
||||
|
||||
@@ -9,11 +9,14 @@
|
||||
"""
|
||||
|
||||
import re
|
||||
from collections import defaultdict
|
||||
from contextlib import contextmanager
|
||||
from unicodedata import east_asian_width
|
||||
|
||||
from docutils.parsers.rst import roles
|
||||
from docutils.parsers.rst.languages import en as english
|
||||
from docutils.utils import Reporter
|
||||
from jinja2 import environmentfilter
|
||||
|
||||
from sphinx.locale import __
|
||||
from sphinx.util import docutils
|
||||
@@ -21,13 +24,20 @@ from sphinx.util import logging
|
||||
|
||||
if False:
|
||||
# For type annotation
|
||||
from typing import Generator # NOQA
|
||||
from typing import Callable, Dict, Generator # NOQA
|
||||
from docutils.statemachine import StringList # NOQA
|
||||
from jinja2 import Environment # NOQA
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
docinfo_re = re.compile(':\\w+:.*?')
|
||||
symbols_re = re.compile(r'([!-\-/:-@\[-`{-~])') # symbols without dot(0x2e)
|
||||
SECTIONING_CHARS = ['=', '-', '~']
|
||||
|
||||
# width of characters
|
||||
WIDECHARS = defaultdict(lambda: "WF") # type: Dict[str, str]
|
||||
# WF: Wide + Full-width
|
||||
WIDECHARS["ja"] = "WFA" # In Japanese, Ambiguous characters also have double width
|
||||
|
||||
|
||||
def escape(text):
|
||||
@@ -37,6 +47,29 @@ def escape(text):
|
||||
return text
|
||||
|
||||
|
||||
def textwidth(text, widechars='WF'):
|
||||
# type: (str, str) -> int
|
||||
"""Get width of text."""
|
||||
def charwidth(char, widechars):
|
||||
# type: (str, str) -> int
|
||||
if east_asian_width(char) in widechars:
|
||||
return 2
|
||||
else:
|
||||
return 1
|
||||
|
||||
return sum(charwidth(c, widechars) for c in text)
|
||||
|
||||
|
||||
@environmentfilter
|
||||
def heading(env, text, level=1):
|
||||
# type: (Environment, str, int) -> str
|
||||
"""Create a heading for *level*."""
|
||||
assert level <= 3
|
||||
width = textwidth(text, WIDECHARS[env.language]) # type: ignore
|
||||
sectioning_char = SECTIONING_CHARS[level - 1]
|
||||
return '%s\n%s' % (text, sectioning_char * width)
|
||||
|
||||
|
||||
@contextmanager
|
||||
def default_role(docname, name):
|
||||
# type: (str, str) -> Generator
|
||||
|
||||
@@ -15,7 +15,7 @@ from jinja2.sandbox import SandboxedEnvironment
|
||||
from sphinx import package_dir
|
||||
from sphinx.jinja2glue import SphinxFileSystemLoader
|
||||
from sphinx.locale import get_translator
|
||||
from sphinx.util import texescape
|
||||
from sphinx.util import rst, texescape
|
||||
|
||||
if False:
|
||||
# For type annotation
|
||||
@@ -84,3 +84,17 @@ class LaTeXRenderer(SphinxRenderer):
|
||||
self.env.variable_end_string = '%>'
|
||||
self.env.block_start_string = '<%'
|
||||
self.env.block_end_string = '%>'
|
||||
|
||||
|
||||
class ReSTRenderer(SphinxRenderer):
|
||||
def __init__(self, template_path=None, language=None):
|
||||
# type: (str, str) -> None
|
||||
super().__init__(template_path)
|
||||
|
||||
# add language to environment
|
||||
self.env.extend(language=language)
|
||||
|
||||
# use texescape as escape filter
|
||||
self.env.filters['e'] = rst.escape
|
||||
self.env.filters['escape'] = rst.escape
|
||||
self.env.filters['heading'] = rst.heading
|
||||
|
||||
@@ -1565,6 +1565,7 @@ class LaTeXTranslator(SphinxTranslator):
|
||||
(1, 'middle'): ('\\raisebox{-0.5\\height}{', '}'),
|
||||
(1, 'bottom'): ('\\raisebox{-\\height}{', '}'),
|
||||
(0, 'center'): ('{\\hspace*{\\fill}', '\\hspace*{\\fill}}'),
|
||||
(0, 'default'): ('{\\hspace*{\\fill}', '\\hspace*{\\fill}}'),
|
||||
# These 2 don't exactly do the right thing. The image should
|
||||
# be floated alongside the paragraph. See
|
||||
# https://www.w3.org/TR/html4/struct/objects.html#adef-align-IMG
|
||||
|
||||
@@ -282,7 +282,7 @@ class ManualPageTranslator(SphinxTranslator, BaseTranslator):
|
||||
|
||||
def depart_rubric(self, node):
|
||||
# type: (nodes.Element) -> None
|
||||
pass
|
||||
self.body.append('\n')
|
||||
|
||||
def visit_seealso(self, node):
|
||||
# type: (nodes.Element) -> None
|
||||
|
||||
@@ -565,7 +565,7 @@ def test_numfig_disabled_warn(app, warning):
|
||||
|
||||
@pytest.mark.parametrize("fname,expect", flat_dict({
|
||||
'index.html': [
|
||||
(".//div[@class='figure align-center']/p[@class='caption']/"
|
||||
(".//div[@class='figure align-default']/p[@class='caption']/"
|
||||
"span[@class='caption-number']", None, True),
|
||||
(".//table/caption/span[@class='caption-number']", None, True),
|
||||
(".//div[@class='code-block-caption']/"
|
||||
@@ -582,21 +582,21 @@ def test_numfig_disabled_warn(app, warning):
|
||||
(".//li/p/a/span", '^Sect.1 Foo$', True),
|
||||
],
|
||||
'foo.html': [
|
||||
(".//div[@class='figure align-center']/p[@class='caption']/"
|
||||
(".//div[@class='figure align-default']/p[@class='caption']/"
|
||||
"span[@class='caption-number']", None, True),
|
||||
(".//table/caption/span[@class='caption-number']", None, True),
|
||||
(".//div[@class='code-block-caption']/"
|
||||
"span[@class='caption-number']", None, True),
|
||||
],
|
||||
'bar.html': [
|
||||
(".//div[@class='figure align-center']/p[@class='caption']/"
|
||||
(".//div[@class='figure align-default']/p[@class='caption']/"
|
||||
"span[@class='caption-number']", None, True),
|
||||
(".//table/caption/span[@class='caption-number']", None, True),
|
||||
(".//div[@class='code-block-caption']/"
|
||||
"span[@class='caption-number']", None, True),
|
||||
],
|
||||
'baz.html': [
|
||||
(".//div[@class='figure align-center']/p[@class='caption']/"
|
||||
(".//div[@class='figure align-default']/p[@class='caption']/"
|
||||
"span[@class='caption-number']", None, True),
|
||||
(".//table/caption/span[@class='caption-number']", None, True),
|
||||
(".//div[@class='code-block-caption']/"
|
||||
@@ -633,9 +633,9 @@ def test_numfig_without_numbered_toctree_warn(app, warning):
|
||||
|
||||
@pytest.mark.parametrize("fname,expect", flat_dict({
|
||||
'index.html': [
|
||||
(".//div[@class='figure align-center']/p[@class='caption']/"
|
||||
(".//div[@class='figure align-default']/p[@class='caption']/"
|
||||
"span[@class='caption-number']", '^Fig. 9 $', True),
|
||||
(".//div[@class='figure align-center']/p[@class='caption']/"
|
||||
(".//div[@class='figure align-default']/p[@class='caption']/"
|
||||
"span[@class='caption-number']", '^Fig. 10 $', True),
|
||||
(".//table/caption/span[@class='caption-number']",
|
||||
'^Table 9 $', True),
|
||||
@@ -657,13 +657,13 @@ def test_numfig_without_numbered_toctree_warn(app, warning):
|
||||
(".//li/p/code/span", '^Sect.{number}$', True),
|
||||
],
|
||||
'foo.html': [
|
||||
(".//div[@class='figure align-center']/p[@class='caption']/"
|
||||
(".//div[@class='figure align-default']/p[@class='caption']/"
|
||||
"span[@class='caption-number']", '^Fig. 1 $', True),
|
||||
(".//div[@class='figure align-center']/p[@class='caption']/"
|
||||
(".//div[@class='figure align-default']/p[@class='caption']/"
|
||||
"span[@class='caption-number']", '^Fig. 2 $', True),
|
||||
(".//div[@class='figure align-center']/p[@class='caption']/"
|
||||
(".//div[@class='figure align-default']/p[@class='caption']/"
|
||||
"span[@class='caption-number']", '^Fig. 3 $', True),
|
||||
(".//div[@class='figure align-center']/p[@class='caption']/"
|
||||
(".//div[@class='figure align-default']/p[@class='caption']/"
|
||||
"span[@class='caption-number']", '^Fig. 4 $', True),
|
||||
(".//table/caption/span[@class='caption-number']",
|
||||
'^Table 1 $', True),
|
||||
@@ -683,11 +683,11 @@ def test_numfig_without_numbered_toctree_warn(app, warning):
|
||||
"span[@class='caption-number']", '^Listing 4 $', True),
|
||||
],
|
||||
'bar.html': [
|
||||
(".//div[@class='figure align-center']/p[@class='caption']/"
|
||||
(".//div[@class='figure align-default']/p[@class='caption']/"
|
||||
"span[@class='caption-number']", '^Fig. 5 $', True),
|
||||
(".//div[@class='figure align-center']/p[@class='caption']/"
|
||||
(".//div[@class='figure align-default']/p[@class='caption']/"
|
||||
"span[@class='caption-number']", '^Fig. 7 $', True),
|
||||
(".//div[@class='figure align-center']/p[@class='caption']/"
|
||||
(".//div[@class='figure align-default']/p[@class='caption']/"
|
||||
"span[@class='caption-number']", '^Fig. 8 $', True),
|
||||
(".//table/caption/span[@class='caption-number']",
|
||||
'^Table 5 $', True),
|
||||
@@ -703,7 +703,7 @@ def test_numfig_without_numbered_toctree_warn(app, warning):
|
||||
"span[@class='caption-number']", '^Listing 8 $', True),
|
||||
],
|
||||
'baz.html': [
|
||||
(".//div[@class='figure align-center']/p[@class='caption']/"
|
||||
(".//div[@class='figure align-default']/p[@class='caption']/"
|
||||
"span[@class='caption-number']", '^Fig. 6 $', True),
|
||||
(".//table/caption/span[@class='caption-number']",
|
||||
'^Table 6 $', True),
|
||||
@@ -741,9 +741,9 @@ def test_numfig_with_numbered_toctree_warn(app, warning):
|
||||
|
||||
@pytest.mark.parametrize("fname,expect", flat_dict({
|
||||
'index.html': [
|
||||
(".//div[@class='figure align-center']/p[@class='caption']/"
|
||||
(".//div[@class='figure align-default']/p[@class='caption']/"
|
||||
"span[@class='caption-number']", '^Fig. 1 $', True),
|
||||
(".//div[@class='figure align-center']/p[@class='caption']/"
|
||||
(".//div[@class='figure align-default']/p[@class='caption']/"
|
||||
"span[@class='caption-number']", '^Fig. 2 $', True),
|
||||
(".//table/caption/span[@class='caption-number']",
|
||||
'^Table 1 $', True),
|
||||
@@ -765,13 +765,13 @@ def test_numfig_with_numbered_toctree_warn(app, warning):
|
||||
(".//li/p/a/span", '^Sect.1 Foo$', True),
|
||||
],
|
||||
'foo.html': [
|
||||
(".//div[@class='figure align-center']/p[@class='caption']/"
|
||||
(".//div[@class='figure align-default']/p[@class='caption']/"
|
||||
"span[@class='caption-number']", '^Fig. 1.1 $', True),
|
||||
(".//div[@class='figure align-center']/p[@class='caption']/"
|
||||
(".//div[@class='figure align-default']/p[@class='caption']/"
|
||||
"span[@class='caption-number']", '^Fig. 1.2 $', True),
|
||||
(".//div[@class='figure align-center']/p[@class='caption']/"
|
||||
(".//div[@class='figure align-default']/p[@class='caption']/"
|
||||
"span[@class='caption-number']", '^Fig. 1.3 $', True),
|
||||
(".//div[@class='figure align-center']/p[@class='caption']/"
|
||||
(".//div[@class='figure align-default']/p[@class='caption']/"
|
||||
"span[@class='caption-number']", '^Fig. 1.4 $', True),
|
||||
(".//table/caption/span[@class='caption-number']",
|
||||
'^Table 1.1 $', True),
|
||||
@@ -791,11 +791,11 @@ def test_numfig_with_numbered_toctree_warn(app, warning):
|
||||
"span[@class='caption-number']", '^Listing 1.4 $', True),
|
||||
],
|
||||
'bar.html': [
|
||||
(".//div[@class='figure align-center']/p[@class='caption']/"
|
||||
(".//div[@class='figure align-default']/p[@class='caption']/"
|
||||
"span[@class='caption-number']", '^Fig. 2.1 $', True),
|
||||
(".//div[@class='figure align-center']/p[@class='caption']/"
|
||||
(".//div[@class='figure align-default']/p[@class='caption']/"
|
||||
"span[@class='caption-number']", '^Fig. 2.3 $', True),
|
||||
(".//div[@class='figure align-center']/p[@class='caption']/"
|
||||
(".//div[@class='figure align-default']/p[@class='caption']/"
|
||||
"span[@class='caption-number']", '^Fig. 2.4 $', True),
|
||||
(".//table/caption/span[@class='caption-number']",
|
||||
'^Table 2.1 $', True),
|
||||
@@ -811,7 +811,7 @@ def test_numfig_with_numbered_toctree_warn(app, warning):
|
||||
"span[@class='caption-number']", '^Listing 2.4 $', True),
|
||||
],
|
||||
'baz.html': [
|
||||
(".//div[@class='figure align-center']/p[@class='caption']/"
|
||||
(".//div[@class='figure align-default']/p[@class='caption']/"
|
||||
"span[@class='caption-number']", '^Fig. 2.2 $', True),
|
||||
(".//table/caption/span[@class='caption-number']",
|
||||
'^Table 2.2 $', True),
|
||||
@@ -846,9 +846,9 @@ def test_numfig_with_prefix_warn(app, warning):
|
||||
|
||||
@pytest.mark.parametrize("fname,expect", flat_dict({
|
||||
'index.html': [
|
||||
(".//div[@class='figure align-center']/p[@class='caption']/"
|
||||
(".//div[@class='figure align-default']/p[@class='caption']/"
|
||||
"span[@class='caption-number']", '^Figure:1 $', True),
|
||||
(".//div[@class='figure align-center']/p[@class='caption']/"
|
||||
(".//div[@class='figure align-default']/p[@class='caption']/"
|
||||
"span[@class='caption-number']", '^Figure:2 $', True),
|
||||
(".//table/caption/span[@class='caption-number']",
|
||||
'^Tab_1 $', True),
|
||||
@@ -870,13 +870,13 @@ def test_numfig_with_prefix_warn(app, warning):
|
||||
(".//li/p/a/span", '^Sect.1 Foo$', True),
|
||||
],
|
||||
'foo.html': [
|
||||
(".//div[@class='figure align-center']/p[@class='caption']/"
|
||||
(".//div[@class='figure align-default']/p[@class='caption']/"
|
||||
"span[@class='caption-number']", '^Figure:1.1 $', True),
|
||||
(".//div[@class='figure align-center']/p[@class='caption']/"
|
||||
(".//div[@class='figure align-default']/p[@class='caption']/"
|
||||
"span[@class='caption-number']", '^Figure:1.2 $', True),
|
||||
(".//div[@class='figure align-center']/p[@class='caption']/"
|
||||
(".//div[@class='figure align-default']/p[@class='caption']/"
|
||||
"span[@class='caption-number']", '^Figure:1.3 $', True),
|
||||
(".//div[@class='figure align-center']/p[@class='caption']/"
|
||||
(".//div[@class='figure align-default']/p[@class='caption']/"
|
||||
"span[@class='caption-number']", '^Figure:1.4 $', True),
|
||||
(".//table/caption/span[@class='caption-number']",
|
||||
'^Tab_1.1 $', True),
|
||||
@@ -896,11 +896,11 @@ def test_numfig_with_prefix_warn(app, warning):
|
||||
"span[@class='caption-number']", '^Code-1.4 $', True),
|
||||
],
|
||||
'bar.html': [
|
||||
(".//div[@class='figure align-center']/p[@class='caption']/"
|
||||
(".//div[@class='figure align-default']/p[@class='caption']/"
|
||||
"span[@class='caption-number']", '^Figure:2.1 $', True),
|
||||
(".//div[@class='figure align-center']/p[@class='caption']/"
|
||||
(".//div[@class='figure align-default']/p[@class='caption']/"
|
||||
"span[@class='caption-number']", '^Figure:2.3 $', True),
|
||||
(".//div[@class='figure align-center']/p[@class='caption']/"
|
||||
(".//div[@class='figure align-default']/p[@class='caption']/"
|
||||
"span[@class='caption-number']", '^Figure:2.4 $', True),
|
||||
(".//table/caption/span[@class='caption-number']",
|
||||
'^Tab_2.1 $', True),
|
||||
@@ -916,7 +916,7 @@ def test_numfig_with_prefix_warn(app, warning):
|
||||
"span[@class='caption-number']", '^Code-2.4 $', True),
|
||||
],
|
||||
'baz.html': [
|
||||
(".//div[@class='figure align-center']/p[@class='caption']/"
|
||||
(".//div[@class='figure align-default']/p[@class='caption']/"
|
||||
"span[@class='caption-number']", '^Figure:2.2 $', True),
|
||||
(".//table/caption/span[@class='caption-number']",
|
||||
'^Tab_2.2 $', True),
|
||||
@@ -952,9 +952,9 @@ def test_numfig_with_secnum_depth_warn(app, warning):
|
||||
|
||||
@pytest.mark.parametrize("fname,expect", flat_dict({
|
||||
'index.html': [
|
||||
(".//div[@class='figure align-center']/p[@class='caption']/"
|
||||
(".//div[@class='figure align-default']/p[@class='caption']/"
|
||||
"span[@class='caption-number']", '^Fig. 1 $', True),
|
||||
(".//div[@class='figure align-center']/p[@class='caption']/"
|
||||
(".//div[@class='figure align-default']/p[@class='caption']/"
|
||||
"span[@class='caption-number']", '^Fig. 2 $', True),
|
||||
(".//table/caption/span[@class='caption-number']",
|
||||
'^Table 1 $', True),
|
||||
@@ -976,13 +976,13 @@ def test_numfig_with_secnum_depth_warn(app, warning):
|
||||
(".//li/p/a/span", '^Sect.1 Foo$', True),
|
||||
],
|
||||
'foo.html': [
|
||||
(".//div[@class='figure align-center']/p[@class='caption']/"
|
||||
(".//div[@class='figure align-default']/p[@class='caption']/"
|
||||
"span[@class='caption-number']", '^Fig. 1.1 $', True),
|
||||
(".//div[@class='figure align-center']/p[@class='caption']/"
|
||||
(".//div[@class='figure align-default']/p[@class='caption']/"
|
||||
"span[@class='caption-number']", '^Fig. 1.1.1 $', True),
|
||||
(".//div[@class='figure align-center']/p[@class='caption']/"
|
||||
(".//div[@class='figure align-default']/p[@class='caption']/"
|
||||
"span[@class='caption-number']", '^Fig. 1.1.2 $', True),
|
||||
(".//div[@class='figure align-center']/p[@class='caption']/"
|
||||
(".//div[@class='figure align-default']/p[@class='caption']/"
|
||||
"span[@class='caption-number']", '^Fig. 1.2.1 $', True),
|
||||
(".//table/caption/span[@class='caption-number']",
|
||||
'^Table 1.1 $', True),
|
||||
@@ -1002,11 +1002,11 @@ def test_numfig_with_secnum_depth_warn(app, warning):
|
||||
"span[@class='caption-number']", '^Listing 1.2.1 $', True),
|
||||
],
|
||||
'bar.html': [
|
||||
(".//div[@class='figure align-center']/p[@class='caption']/"
|
||||
(".//div[@class='figure align-default']/p[@class='caption']/"
|
||||
"span[@class='caption-number']", '^Fig. 2.1.1 $', True),
|
||||
(".//div[@class='figure align-center']/p[@class='caption']/"
|
||||
(".//div[@class='figure align-default']/p[@class='caption']/"
|
||||
"span[@class='caption-number']", '^Fig. 2.1.3 $', True),
|
||||
(".//div[@class='figure align-center']/p[@class='caption']/"
|
||||
(".//div[@class='figure align-default']/p[@class='caption']/"
|
||||
"span[@class='caption-number']", '^Fig. 2.2.1 $', True),
|
||||
(".//table/caption/span[@class='caption-number']",
|
||||
'^Table 2.1.1 $', True),
|
||||
@@ -1022,7 +1022,7 @@ def test_numfig_with_secnum_depth_warn(app, warning):
|
||||
"span[@class='caption-number']", '^Listing 2.2.1 $', True),
|
||||
],
|
||||
'baz.html': [
|
||||
(".//div[@class='figure align-center']/p[@class='caption']/"
|
||||
(".//div[@class='figure align-default']/p[@class='caption']/"
|
||||
"span[@class='caption-number']", '^Fig. 2.1.2 $', True),
|
||||
(".//table/caption/span[@class='caption-number']",
|
||||
'^Table 2.1.2 $', True),
|
||||
@@ -1043,9 +1043,9 @@ def test_numfig_with_secnum_depth(app, cached_etree_parse, fname, expect):
|
||||
|
||||
@pytest.mark.parametrize("fname,expect", flat_dict({
|
||||
'index.html': [
|
||||
(".//div[@class='figure align-center']/p[@class='caption']/"
|
||||
(".//div[@class='figure align-default']/p[@class='caption']/"
|
||||
"span[@class='caption-number']", '^Fig. 1 $', True),
|
||||
(".//div[@class='figure align-center']/p[@class='caption']/"
|
||||
(".//div[@class='figure align-default']/p[@class='caption']/"
|
||||
"span[@class='caption-number']", '^Fig. 2 $', True),
|
||||
(".//table/caption/span[@class='caption-number']",
|
||||
'^Table 1 $', True),
|
||||
@@ -1065,13 +1065,13 @@ def test_numfig_with_secnum_depth(app, cached_etree_parse, fname, expect):
|
||||
(".//li/p/a/span", '^Section.2.1$', True),
|
||||
(".//li/p/a/span", '^Fig.1 should be Fig.1$', True),
|
||||
(".//li/p/a/span", '^Sect.1 Foo$', True),
|
||||
(".//div[@class='figure align-center']/p[@class='caption']/"
|
||||
(".//div[@class='figure align-default']/p[@class='caption']/"
|
||||
"span[@class='caption-number']", '^Fig. 1.1 $', True),
|
||||
(".//div[@class='figure align-center']/p[@class='caption']/"
|
||||
(".//div[@class='figure align-default']/p[@class='caption']/"
|
||||
"span[@class='caption-number']", '^Fig. 1.2 $', True),
|
||||
(".//div[@class='figure align-center']/p[@class='caption']/"
|
||||
(".//div[@class='figure align-default']/p[@class='caption']/"
|
||||
"span[@class='caption-number']", '^Fig. 1.3 $', True),
|
||||
(".//div[@class='figure align-center']/p[@class='caption']/"
|
||||
(".//div[@class='figure align-default']/p[@class='caption']/"
|
||||
"span[@class='caption-number']", '^Fig. 1.4 $', True),
|
||||
(".//table/caption/span[@class='caption-number']",
|
||||
'^Table 1.1 $', True),
|
||||
@@ -1089,11 +1089,11 @@ def test_numfig_with_secnum_depth(app, cached_etree_parse, fname, expect):
|
||||
"span[@class='caption-number']", '^Listing 1.3 $', True),
|
||||
(".//div[@class='code-block-caption']/"
|
||||
"span[@class='caption-number']", '^Listing 1.4 $', True),
|
||||
(".//div[@class='figure align-center']/p[@class='caption']/"
|
||||
(".//div[@class='figure align-default']/p[@class='caption']/"
|
||||
"span[@class='caption-number']", '^Fig. 2.1 $', True),
|
||||
(".//div[@class='figure align-center']/p[@class='caption']/"
|
||||
(".//div[@class='figure align-default']/p[@class='caption']/"
|
||||
"span[@class='caption-number']", '^Fig. 2.3 $', True),
|
||||
(".//div[@class='figure align-center']/p[@class='caption']/"
|
||||
(".//div[@class='figure align-default']/p[@class='caption']/"
|
||||
"span[@class='caption-number']", '^Fig. 2.4 $', True),
|
||||
(".//table/caption/span[@class='caption-number']",
|
||||
'^Table 2.1 $', True),
|
||||
@@ -1107,7 +1107,7 @@ def test_numfig_with_secnum_depth(app, cached_etree_parse, fname, expect):
|
||||
"span[@class='caption-number']", '^Listing 2.3 $', True),
|
||||
(".//div[@class='code-block-caption']/"
|
||||
"span[@class='caption-number']", '^Listing 2.4 $', True),
|
||||
(".//div[@class='figure align-center']/p[@class='caption']/"
|
||||
(".//div[@class='figure align-default']/p[@class='caption']/"
|
||||
"span[@class='caption-number']", '^Fig. 2.2 $', True),
|
||||
(".//table/caption/span[@class='caption-number']",
|
||||
'^Table 2.2 $', True),
|
||||
@@ -1126,11 +1126,11 @@ def test_numfig_with_singlehtml(app, cached_etree_parse, fname, expect):
|
||||
|
||||
@pytest.mark.parametrize("fname,expect", flat_dict({
|
||||
'index.html': [
|
||||
(".//div[@class='figure align-center']/p[@class='caption']"
|
||||
(".//div[@class='figure align-default']/p[@class='caption']"
|
||||
"/span[@class='caption-number']", "Fig. 1", True),
|
||||
(".//div[@class='figure align-center']/p[@class='caption']"
|
||||
(".//div[@class='figure align-default']/p[@class='caption']"
|
||||
"/span[@class='caption-number']", "Fig. 2", True),
|
||||
(".//div[@class='figure align-center']/p[@class='caption']"
|
||||
(".//div[@class='figure align-default']/p[@class='caption']"
|
||||
"/span[@class='caption-number']", "Fig. 3", True),
|
||||
(".//div//span[@class='caption-number']", "No.1 ", True),
|
||||
(".//div//span[@class='caption-number']", "No.2 ", True),
|
||||
|
||||
@@ -59,3 +59,10 @@ def test_default_man_pages():
|
||||
expected = [('index', 'stasi', 'STASI™ Documentation 1.0',
|
||||
["Wolfgang Schäuble & G'Beckstein"], 1)]
|
||||
assert default_man_pages(config) == expected
|
||||
|
||||
|
||||
@pytest.mark.sphinx('man', testroot='markup-rubric')
|
||||
def test_rubric(app, status, warning):
|
||||
app.build()
|
||||
content = (app.outdir / 'python.1').text()
|
||||
assert 'This is a rubric\n' in content
|
||||
|
||||
@@ -13,6 +13,7 @@ from collections import namedtuple
|
||||
import pytest
|
||||
|
||||
from sphinx.ext.apidoc import main as apidoc_main
|
||||
from sphinx.testing.path import path
|
||||
|
||||
|
||||
@pytest.fixture()
|
||||
@@ -398,3 +399,216 @@ def test_subpackage_in_toc(make_app, apidoc):
|
||||
assert 'parent.child.foo' in parent_child
|
||||
|
||||
assert (outdir / 'parent.child.foo.rst').isfile()
|
||||
|
||||
|
||||
def test_toc_file(tempdir):
|
||||
outdir = path(tempdir)
|
||||
(outdir / 'module').makedirs()
|
||||
(outdir / 'example.py').write_text('')
|
||||
(outdir / 'module' / 'example.py').write_text('')
|
||||
apidoc_main(['-o', tempdir, tempdir])
|
||||
assert (outdir / 'modules.rst').exists()
|
||||
|
||||
content = (outdir / 'modules.rst').text()
|
||||
assert content == ("test_toc_file0\n"
|
||||
"==============\n"
|
||||
"\n"
|
||||
".. toctree::\n"
|
||||
" :maxdepth: 4\n"
|
||||
"\n"
|
||||
" example\n")
|
||||
|
||||
|
||||
def test_module_file(tempdir):
|
||||
outdir = path(tempdir)
|
||||
(outdir / 'example.py').write_text('')
|
||||
apidoc_main(['-o', tempdir, tempdir])
|
||||
assert (outdir / 'example.rst').exists()
|
||||
|
||||
content = (outdir / 'example.rst').text()
|
||||
assert content == ("example module\n"
|
||||
"==============\n"
|
||||
"\n"
|
||||
".. automodule:: example\n"
|
||||
" :members:\n"
|
||||
" :undoc-members:\n"
|
||||
" :show-inheritance:\n")
|
||||
|
||||
|
||||
def test_module_file_noheadings(tempdir):
|
||||
outdir = path(tempdir)
|
||||
(outdir / 'example.py').write_text('')
|
||||
apidoc_main(['--no-headings', '-o', tempdir, tempdir])
|
||||
assert (outdir / 'example.rst').exists()
|
||||
|
||||
content = (outdir / 'example.rst').text()
|
||||
assert content == (".. automodule:: example\n"
|
||||
" :members:\n"
|
||||
" :undoc-members:\n"
|
||||
" :show-inheritance:\n")
|
||||
|
||||
|
||||
def test_package_file(tempdir):
|
||||
outdir = path(tempdir)
|
||||
(outdir / 'testpkg').makedirs()
|
||||
(outdir / 'testpkg' / '__init__.py').write_text('')
|
||||
(outdir / 'testpkg' / 'example.py').write_text('')
|
||||
(outdir / 'testpkg' / 'subpkg').makedirs()
|
||||
(outdir / 'testpkg' / 'subpkg' / '__init__.py').write_text('')
|
||||
apidoc_main(['-o', tempdir, tempdir / 'testpkg'])
|
||||
assert (outdir / 'testpkg.rst').exists()
|
||||
assert (outdir / 'testpkg.subpkg.rst').exists()
|
||||
|
||||
content = (outdir / 'testpkg.rst').text()
|
||||
assert content == ("testpkg package\n"
|
||||
"===============\n"
|
||||
"\n"
|
||||
"Subpackages\n"
|
||||
"-----------\n"
|
||||
"\n"
|
||||
".. toctree::\n"
|
||||
"\n"
|
||||
" testpkg.subpkg\n"
|
||||
"\n"
|
||||
"Submodules\n"
|
||||
"----------\n"
|
||||
"\n"
|
||||
"testpkg.example module\n"
|
||||
"----------------------\n"
|
||||
"\n"
|
||||
".. automodule:: testpkg.example\n"
|
||||
" :members:\n"
|
||||
" :undoc-members:\n"
|
||||
" :show-inheritance:\n"
|
||||
"\n"
|
||||
"\n"
|
||||
"Module contents\n"
|
||||
"---------------\n"
|
||||
"\n"
|
||||
".. automodule:: testpkg\n"
|
||||
" :members:\n"
|
||||
" :undoc-members:\n"
|
||||
" :show-inheritance:\n")
|
||||
|
||||
content = (outdir / 'testpkg.subpkg.rst').text()
|
||||
assert content == ("testpkg.subpkg package\n"
|
||||
"======================\n"
|
||||
"\n"
|
||||
"Module contents\n"
|
||||
"---------------\n"
|
||||
"\n"
|
||||
".. automodule:: testpkg.subpkg\n"
|
||||
" :members:\n"
|
||||
" :undoc-members:\n"
|
||||
" :show-inheritance:\n")
|
||||
|
||||
|
||||
def test_package_file_separate(tempdir):
|
||||
outdir = path(tempdir)
|
||||
(outdir / 'testpkg').makedirs()
|
||||
(outdir / 'testpkg' / '__init__.py').write_text('')
|
||||
(outdir / 'testpkg' / 'example.py').write_text('')
|
||||
apidoc_main(['--separate', '-o', tempdir, tempdir / 'testpkg'])
|
||||
assert (outdir / 'testpkg.rst').exists()
|
||||
assert (outdir / 'testpkg.example.rst').exists()
|
||||
|
||||
content = (outdir / 'testpkg.rst').text()
|
||||
assert content == ("testpkg package\n"
|
||||
"===============\n"
|
||||
"\n"
|
||||
"Submodules\n"
|
||||
"----------\n"
|
||||
"\n"
|
||||
".. toctree::\n"
|
||||
"\n"
|
||||
" testpkg.example\n"
|
||||
"\n"
|
||||
"Module contents\n"
|
||||
"---------------\n"
|
||||
"\n"
|
||||
".. automodule:: testpkg\n"
|
||||
" :members:\n"
|
||||
" :undoc-members:\n"
|
||||
" :show-inheritance:\n")
|
||||
|
||||
content = (outdir / 'testpkg.example.rst').text()
|
||||
assert content == ("testpkg.example module\n"
|
||||
"======================\n"
|
||||
"\n"
|
||||
".. automodule:: testpkg.example\n"
|
||||
" :members:\n"
|
||||
" :undoc-members:\n"
|
||||
" :show-inheritance:\n")
|
||||
|
||||
|
||||
def test_package_file_module_first(tempdir):
|
||||
outdir = path(tempdir)
|
||||
(outdir / 'testpkg').makedirs()
|
||||
(outdir / 'testpkg' / '__init__.py').write_text('')
|
||||
(outdir / 'testpkg' / 'example.py').write_text('')
|
||||
apidoc_main(['--module-first', '-o', tempdir, tempdir])
|
||||
|
||||
content = (outdir / 'testpkg.rst').text()
|
||||
assert content == ("testpkg package\n"
|
||||
"===============\n"
|
||||
"\n"
|
||||
".. automodule:: testpkg\n"
|
||||
" :members:\n"
|
||||
" :undoc-members:\n"
|
||||
" :show-inheritance:\n"
|
||||
"\n"
|
||||
"Submodules\n"
|
||||
"----------\n"
|
||||
"\n"
|
||||
"testpkg.example module\n"
|
||||
"----------------------\n"
|
||||
"\n"
|
||||
".. automodule:: testpkg.example\n"
|
||||
" :members:\n"
|
||||
" :undoc-members:\n"
|
||||
" :show-inheritance:\n"
|
||||
"\n")
|
||||
|
||||
|
||||
def test_package_file_without_submodules(tempdir):
|
||||
outdir = path(tempdir)
|
||||
(outdir / 'testpkg').makedirs()
|
||||
(outdir / 'testpkg' / '__init__.py').write_text('')
|
||||
apidoc_main(['-o', tempdir, tempdir / 'testpkg'])
|
||||
assert (outdir / 'testpkg.rst').exists()
|
||||
|
||||
content = (outdir / 'testpkg.rst').text()
|
||||
assert content == ("testpkg package\n"
|
||||
"===============\n"
|
||||
"\n"
|
||||
"Module contents\n"
|
||||
"---------------\n"
|
||||
"\n"
|
||||
".. automodule:: testpkg\n"
|
||||
" :members:\n"
|
||||
" :undoc-members:\n"
|
||||
" :show-inheritance:\n")
|
||||
|
||||
|
||||
def test_namespace_package_file(tempdir):
|
||||
outdir = path(tempdir)
|
||||
(outdir / 'testpkg').makedirs()
|
||||
(outdir / 'testpkg' / 'example.py').write_text('')
|
||||
apidoc_main(['--implicit-namespace', '-o', tempdir, tempdir / 'testpkg'])
|
||||
assert (outdir / 'testpkg.rst').exists()
|
||||
|
||||
content = (outdir / 'testpkg.rst').text()
|
||||
assert content == ("testpkg namespace\n"
|
||||
"=================\n"
|
||||
"\n"
|
||||
"Submodules\n"
|
||||
"----------\n"
|
||||
"\n"
|
||||
"testpkg.example module\n"
|
||||
"----------------------\n"
|
||||
"\n"
|
||||
".. automodule:: testpkg.example\n"
|
||||
" :members:\n"
|
||||
" :undoc-members:\n"
|
||||
" :show-inheritance:\n"
|
||||
"\n")
|
||||
|
||||
@@ -21,7 +21,7 @@ def test_graphviz_png_html(app, status, warning):
|
||||
app.builder.build_all()
|
||||
|
||||
content = (app.outdir / 'index.html').text()
|
||||
html = (r'<div class="figure align-center" .*?>\s*'
|
||||
html = (r'<div class="figure align-default" .*?>\s*'
|
||||
r'<div class="graphviz"><img .*?/></div>\s*<p class="caption">'
|
||||
r'<span class="caption-text">caption of graph</span>.*</p>\s*</div>')
|
||||
assert re.search(html, content, re.S)
|
||||
@@ -52,7 +52,7 @@ def test_graphviz_svg_html(app, status, warning):
|
||||
|
||||
content = (app.outdir / 'index.html').text()
|
||||
|
||||
html = (r'<div class=\"figure align-center\" .*?>\n'
|
||||
html = (r'<div class=\"figure align-default\" .*?>\n'
|
||||
r'<div class="graphviz"><object data=\".*\.svg\".*>\n'
|
||||
r'\s*<p class=\"warning\">digraph foo {\n'
|
||||
r'bar -> baz\n'
|
||||
|
||||
@@ -1,129 +0,0 @@
|
||||
"""
|
||||
test_inheritance
|
||||
~~~~~~~~~~~~~~~~
|
||||
|
||||
Tests for :mod:`sphinx.ext.inheritance_diagram` module.
|
||||
|
||||
:copyright: Copyright 2015 by the Sphinx team, see AUTHORS.
|
||||
:license: BSD, see LICENSE for details.
|
||||
"""
|
||||
|
||||
import os
|
||||
|
||||
import pytest
|
||||
|
||||
from sphinx.ext.inheritance_diagram import InheritanceDiagram
|
||||
|
||||
|
||||
@pytest.mark.sphinx(buildername="html", testroot="inheritance")
|
||||
@pytest.mark.usefixtures('if_graphviz_found')
|
||||
def test_inheritance_diagram(app, status, warning):
|
||||
# monkey-patch InheritaceDiagram.run() so we can get access to its
|
||||
# results.
|
||||
orig_run = InheritanceDiagram.run
|
||||
graphs = {}
|
||||
|
||||
def new_run(self):
|
||||
result = orig_run(self)
|
||||
node = result[0]
|
||||
source = os.path.basename(node.document.current_source).replace(".rst", "")
|
||||
graphs[source] = node['graph']
|
||||
return result
|
||||
|
||||
InheritanceDiagram.run = new_run
|
||||
|
||||
try:
|
||||
app.builder.build_all()
|
||||
finally:
|
||||
InheritanceDiagram.run = orig_run
|
||||
|
||||
assert app.statuscode == 0
|
||||
|
||||
html_warnings = warning.getvalue()
|
||||
assert html_warnings == ""
|
||||
|
||||
# note: it is better to split these asserts into separate test functions
|
||||
# but I can't figure out how to build only a specific .rst file
|
||||
|
||||
# basic inheritance diagram showing all classes
|
||||
for cls in graphs['basic_diagram'].class_info:
|
||||
# use in b/c traversing order is different sometimes
|
||||
assert cls in [
|
||||
('dummy.test.A', 'dummy.test.A', [], None),
|
||||
('dummy.test.F', 'dummy.test.F', ['dummy.test.C'], None),
|
||||
('dummy.test.C', 'dummy.test.C', ['dummy.test.A'], None),
|
||||
('dummy.test.E', 'dummy.test.E', ['dummy.test.B'], None),
|
||||
('dummy.test.D', 'dummy.test.D', ['dummy.test.B', 'dummy.test.C'], None),
|
||||
('dummy.test.B', 'dummy.test.B', ['dummy.test.A'], None)
|
||||
]
|
||||
|
||||
# inheritance diagram using :parts: 1 option
|
||||
for cls in graphs['diagram_w_parts'].class_info:
|
||||
assert cls in [
|
||||
('A', 'dummy.test.A', [], None),
|
||||
('F', 'dummy.test.F', ['C'], None),
|
||||
('C', 'dummy.test.C', ['A'], None),
|
||||
('E', 'dummy.test.E', ['B'], None),
|
||||
('D', 'dummy.test.D', ['B', 'C'], None),
|
||||
('B', 'dummy.test.B', ['A'], None)
|
||||
]
|
||||
|
||||
# inheritance diagram with 1 top class
|
||||
# :top-classes: dummy.test.B
|
||||
# rendering should be
|
||||
# A
|
||||
# \
|
||||
# B C
|
||||
# / \ / \
|
||||
# E D F
|
||||
#
|
||||
for cls in graphs['diagram_w_1_top_class'].class_info:
|
||||
assert cls in [
|
||||
('dummy.test.A', 'dummy.test.A', [], None),
|
||||
('dummy.test.F', 'dummy.test.F', ['dummy.test.C'], None),
|
||||
('dummy.test.C', 'dummy.test.C', ['dummy.test.A'], None),
|
||||
('dummy.test.E', 'dummy.test.E', ['dummy.test.B'], None),
|
||||
('dummy.test.D', 'dummy.test.D', ['dummy.test.B', 'dummy.test.C'], None),
|
||||
('dummy.test.B', 'dummy.test.B', [], None)
|
||||
]
|
||||
|
||||
# inheritance diagram with 2 top classes
|
||||
# :top-classes: dummy.test.B, dummy.test.C
|
||||
# Note: we're specifying separate classes, not the entire module here
|
||||
# rendering should be
|
||||
#
|
||||
# B C
|
||||
# / \ / \
|
||||
# E D F
|
||||
#
|
||||
for cls in graphs['diagram_w_2_top_classes'].class_info:
|
||||
assert cls in [
|
||||
('dummy.test.F', 'dummy.test.F', ['dummy.test.C'], None),
|
||||
('dummy.test.C', 'dummy.test.C', [], None),
|
||||
('dummy.test.E', 'dummy.test.E', ['dummy.test.B'], None),
|
||||
('dummy.test.D', 'dummy.test.D', ['dummy.test.B', 'dummy.test.C'], None),
|
||||
('dummy.test.B', 'dummy.test.B', [], None)
|
||||
]
|
||||
|
||||
# inheritance diagram with 2 top classes and specifiying the entire module
|
||||
# rendering should be
|
||||
#
|
||||
# A
|
||||
# B C
|
||||
# / \ / \
|
||||
# E D F
|
||||
#
|
||||
# Note: dummy.test.A is included in the graph before its descendants are even processed
|
||||
# b/c we've specified to load the entire module. The way InheritanceGraph works it is very
|
||||
# hard to exclude parent classes once after they have been included in the graph.
|
||||
# If you'd like to not show class A in the graph don't specify the entire module.
|
||||
# this is a known issue.
|
||||
for cls in graphs['diagram_module_w_2_top_classes'].class_info:
|
||||
assert cls in [
|
||||
('dummy.test.F', 'dummy.test.F', ['dummy.test.C'], None),
|
||||
('dummy.test.C', 'dummy.test.C', [], None),
|
||||
('dummy.test.E', 'dummy.test.E', ['dummy.test.B'], None),
|
||||
('dummy.test.D', 'dummy.test.D', ['dummy.test.B', 'dummy.test.C'], None),
|
||||
('dummy.test.B', 'dummy.test.B', [], None),
|
||||
('dummy.test.A', 'dummy.test.A', [], None),
|
||||
]
|
||||
@@ -9,11 +9,128 @@
|
||||
"""
|
||||
|
||||
import re
|
||||
import os
|
||||
import sys
|
||||
|
||||
import pytest
|
||||
|
||||
from sphinx.ext.inheritance_diagram import InheritanceException, import_classes
|
||||
from sphinx.ext.inheritance_diagram import (
|
||||
InheritanceDiagram, InheritanceException, import_classes
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.sphinx(buildername="html", testroot="inheritance")
|
||||
@pytest.mark.usefixtures('if_graphviz_found')
|
||||
def test_inheritance_diagram(app, status, warning):
|
||||
# monkey-patch InheritaceDiagram.run() so we can get access to its
|
||||
# results.
|
||||
orig_run = InheritanceDiagram.run
|
||||
graphs = {}
|
||||
|
||||
def new_run(self):
|
||||
result = orig_run(self)
|
||||
node = result[0]
|
||||
source = os.path.basename(node.document.current_source).replace(".rst", "")
|
||||
graphs[source] = node['graph']
|
||||
return result
|
||||
|
||||
InheritanceDiagram.run = new_run
|
||||
|
||||
try:
|
||||
app.builder.build_all()
|
||||
finally:
|
||||
InheritanceDiagram.run = orig_run
|
||||
|
||||
assert app.statuscode == 0
|
||||
|
||||
html_warnings = warning.getvalue()
|
||||
assert html_warnings == ""
|
||||
|
||||
# note: it is better to split these asserts into separate test functions
|
||||
# but I can't figure out how to build only a specific .rst file
|
||||
|
||||
# basic inheritance diagram showing all classes
|
||||
for cls in graphs['basic_diagram'].class_info:
|
||||
# use in b/c traversing order is different sometimes
|
||||
assert cls in [
|
||||
('dummy.test.A', 'dummy.test.A', [], None),
|
||||
('dummy.test.F', 'dummy.test.F', ['dummy.test.C'], None),
|
||||
('dummy.test.C', 'dummy.test.C', ['dummy.test.A'], None),
|
||||
('dummy.test.E', 'dummy.test.E', ['dummy.test.B'], None),
|
||||
('dummy.test.D', 'dummy.test.D', ['dummy.test.B', 'dummy.test.C'], None),
|
||||
('dummy.test.B', 'dummy.test.B', ['dummy.test.A'], None)
|
||||
]
|
||||
|
||||
# inheritance diagram using :parts: 1 option
|
||||
for cls in graphs['diagram_w_parts'].class_info:
|
||||
assert cls in [
|
||||
('A', 'dummy.test.A', [], None),
|
||||
('F', 'dummy.test.F', ['C'], None),
|
||||
('C', 'dummy.test.C', ['A'], None),
|
||||
('E', 'dummy.test.E', ['B'], None),
|
||||
('D', 'dummy.test.D', ['B', 'C'], None),
|
||||
('B', 'dummy.test.B', ['A'], None)
|
||||
]
|
||||
|
||||
# inheritance diagram with 1 top class
|
||||
# :top-classes: dummy.test.B
|
||||
# rendering should be
|
||||
# A
|
||||
# \
|
||||
# B C
|
||||
# / \ / \
|
||||
# E D F
|
||||
#
|
||||
for cls in graphs['diagram_w_1_top_class'].class_info:
|
||||
assert cls in [
|
||||
('dummy.test.A', 'dummy.test.A', [], None),
|
||||
('dummy.test.F', 'dummy.test.F', ['dummy.test.C'], None),
|
||||
('dummy.test.C', 'dummy.test.C', ['dummy.test.A'], None),
|
||||
('dummy.test.E', 'dummy.test.E', ['dummy.test.B'], None),
|
||||
('dummy.test.D', 'dummy.test.D', ['dummy.test.B', 'dummy.test.C'], None),
|
||||
('dummy.test.B', 'dummy.test.B', [], None)
|
||||
]
|
||||
|
||||
# inheritance diagram with 2 top classes
|
||||
# :top-classes: dummy.test.B, dummy.test.C
|
||||
# Note: we're specifying separate classes, not the entire module here
|
||||
# rendering should be
|
||||
#
|
||||
# B C
|
||||
# / \ / \
|
||||
# E D F
|
||||
#
|
||||
for cls in graphs['diagram_w_2_top_classes'].class_info:
|
||||
assert cls in [
|
||||
('dummy.test.F', 'dummy.test.F', ['dummy.test.C'], None),
|
||||
('dummy.test.C', 'dummy.test.C', [], None),
|
||||
('dummy.test.E', 'dummy.test.E', ['dummy.test.B'], None),
|
||||
('dummy.test.D', 'dummy.test.D', ['dummy.test.B', 'dummy.test.C'], None),
|
||||
('dummy.test.B', 'dummy.test.B', [], None)
|
||||
]
|
||||
|
||||
# inheritance diagram with 2 top classes and specifiying the entire module
|
||||
# rendering should be
|
||||
#
|
||||
# A
|
||||
# B C
|
||||
# / \ / \
|
||||
# E D F
|
||||
#
|
||||
# Note: dummy.test.A is included in the graph before its descendants are even processed
|
||||
# b/c we've specified to load the entire module. The way InheritanceGraph works it is very
|
||||
# hard to exclude parent classes once after they have been included in the graph.
|
||||
# If you'd like to not show class A in the graph don't specify the entire module.
|
||||
# this is a known issue.
|
||||
for cls in graphs['diagram_module_w_2_top_classes'].class_info:
|
||||
assert cls in [
|
||||
('dummy.test.F', 'dummy.test.F', ['dummy.test.C'], None),
|
||||
('dummy.test.C', 'dummy.test.C', [], None),
|
||||
('dummy.test.E', 'dummy.test.E', ['dummy.test.B'], None),
|
||||
('dummy.test.D', 'dummy.test.D', ['dummy.test.B', 'dummy.test.C'], None),
|
||||
('dummy.test.B', 'dummy.test.B', [], None),
|
||||
('dummy.test.A', 'dummy.test.A', [], None),
|
||||
]
|
||||
|
||||
|
||||
@pytest.mark.sphinx('html', testroot='ext-inheritance_diagram')
|
||||
@@ -23,7 +140,7 @@ def test_inheritance_diagram_png_html(app, status, warning):
|
||||
|
||||
content = (app.outdir / 'index.html').text()
|
||||
|
||||
pattern = ('<div class="figure align-center" id="id1">\n'
|
||||
pattern = ('<div class="figure align-default" id="id1">\n'
|
||||
'<div class="graphviz">'
|
||||
'<img src="_images/inheritance-\\w+.png" alt="Inheritance diagram of test.Foo" '
|
||||
'class="inheritance graphviz" /></div>\n<p class="caption">'
|
||||
@@ -40,7 +157,7 @@ def test_inheritance_diagram_svg_html(app, status, warning):
|
||||
|
||||
content = (app.outdir / 'index.html').text()
|
||||
|
||||
pattern = ('<div class="figure align-center" id="id1">\n'
|
||||
pattern = ('<div class="figure align-default" id="id1">\n'
|
||||
'<div class="graphviz">'
|
||||
'<object data="_images/inheritance-\\w+.svg" '
|
||||
'type="image/svg\\+xml" class="inheritance graphviz">\n'
|
||||
@@ -80,7 +197,7 @@ def test_inheritance_diagram_latex_alias(app, status, warning):
|
||||
|
||||
content = (app.outdir / 'index.html').text()
|
||||
|
||||
pattern = ('<div class="figure align-center" id="id1">\n'
|
||||
pattern = ('<div class="figure align-default" id="id1">\n'
|
||||
'<div class="graphviz">'
|
||||
'<img src="_images/inheritance-\\w+.png" alt="Inheritance diagram of test.Foo" '
|
||||
'class="inheritance graphviz" /></div>\n<p class="caption">'
|
||||
|
||||
@@ -9,8 +9,11 @@
|
||||
"""
|
||||
|
||||
from docutils.statemachine import StringList
|
||||
from jinja2 import Environment
|
||||
|
||||
from sphinx.util.rst import append_epilog, escape, prepend_prolog
|
||||
from sphinx.util.rst import (
|
||||
append_epilog, escape, heading, prepend_prolog, textwidth
|
||||
)
|
||||
|
||||
|
||||
def test_escape():
|
||||
@@ -83,3 +86,34 @@ def test_prepend_prolog_without_CR(app):
|
||||
('<generated>', 0, ''),
|
||||
('dummy.rst', 0, 'hello Sphinx world'),
|
||||
('dummy.rst', 1, 'Sphinx is a document generator')]
|
||||
|
||||
|
||||
def test_textwidth():
|
||||
assert textwidth('Hello') == 5
|
||||
assert textwidth('русский язык') == 12
|
||||
assert textwidth('русский язык', 'WFA') == 23 # Cyrillic are ambiguous chars
|
||||
|
||||
|
||||
def test_heading():
|
||||
env = Environment()
|
||||
env.extend(language=None)
|
||||
|
||||
assert heading(env, 'Hello') == ('Hello\n'
|
||||
'=====')
|
||||
assert heading(env, 'Hello', 1) == ('Hello\n'
|
||||
'=====')
|
||||
assert heading(env, 'Hello', 2) == ('Hello\n'
|
||||
'-----')
|
||||
assert heading(env, 'Hello', 3) == ('Hello\n'
|
||||
'~~~~~')
|
||||
assert heading(env, 'русский язык', 1) == (
|
||||
'русский язык\n'
|
||||
'============'
|
||||
)
|
||||
|
||||
# language=ja: ambiguous
|
||||
env.language = 'ja'
|
||||
assert heading(env, 'русский язык', 1) == (
|
||||
'русский язык\n'
|
||||
'======================='
|
||||
)
|
||||
|
||||
37
tests/test_util_template.py
Normal file
37
tests/test_util_template.py
Normal file
@@ -0,0 +1,37 @@
|
||||
"""
|
||||
test_util_template
|
||||
~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Tests sphinx.util.template functions.
|
||||
|
||||
:copyright: Copyright 2007-2019 by the Sphinx team, see AUTHORS.
|
||||
:license: BSD, see LICENSE for details.
|
||||
"""
|
||||
|
||||
from sphinx.util.template import ReSTRenderer
|
||||
|
||||
|
||||
def test_ReSTRenderer_escape():
|
||||
r = ReSTRenderer()
|
||||
template = '{{ "*hello*" | e }}'
|
||||
assert r.render_string(template, {}) == r'\*hello\*'
|
||||
|
||||
|
||||
def test_ReSTRenderer_heading():
|
||||
r = ReSTRenderer()
|
||||
|
||||
template = '{{ "hello" | heading }}'
|
||||
assert r.render_string(template, {}) == 'hello\n====='
|
||||
|
||||
template = '{{ "hello" | heading(1) }}'
|
||||
assert r.render_string(template, {}) == 'hello\n====='
|
||||
|
||||
template = '{{ "русский язык" | heading(2) }}'
|
||||
assert r.render_string(template, {}) == ('русский язык\n'
|
||||
'------------')
|
||||
|
||||
# language: ja
|
||||
r.env.language = 'ja'
|
||||
template = '{{ "русский язык" | heading }}'
|
||||
assert r.render_string(template, {}) == ('русский язык\n'
|
||||
'=======================')
|
||||
5
tox.ini
5
tox.ini
@@ -15,7 +15,6 @@ deps =
|
||||
du14: docutils==0.14
|
||||
extras =
|
||||
test
|
||||
websupport
|
||||
setenv =
|
||||
PYTHONWARNINGS = all,ignore::ImportWarning:pkgutil,ignore::ImportWarning:importlib._bootstrap,ignore::ImportWarning:importlib._bootstrap_external,ignore::ImportWarning:pytest_cov.plugin,ignore::DeprecationWarning:site,ignore::DeprecationWarning:_pytest.assertion.rewrite,ignore::DeprecationWarning:_pytest.fixtures,ignore::DeprecationWarning:distutils
|
||||
commands=
|
||||
@@ -62,8 +61,8 @@ commands=
|
||||
basepython = python3
|
||||
description =
|
||||
Build documentation.
|
||||
deps =
|
||||
sphinxcontrib-websupport
|
||||
extras =
|
||||
docs
|
||||
commands =
|
||||
python setup.py build_sphinx {posargs}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user