Merge branch '2.0' into refactor_citations

This commit is contained in:
Takeshi KOMIYA
2019-04-06 14:23:04 +09:00
28 changed files with 328 additions and 205 deletions

20
CHANGES
View File

@@ -9,6 +9,8 @@ Incompatible changes
* Ignore filenames without file extension given to ``Builder.build_specific()``
API directly
* #6230: The anchor of term in glossary directive is changed if it is consisted
by non-ASCII characters
Deprecated
----------
@@ -58,10 +60,17 @@ Features added
* #6180: Support ``--keep-going`` with BuildDoc setup command
* ``math`` directive now supports ``:class:`` option
* todo: ``todo`` directive now supports ``:name:`` option
* #6232: Enable CLI override of Makefile variables
* #6212 autosummary: Add :confval:`autosummary_imported_members` to display
imported members on autosummary
Bugs fixed
----------
* #6230: Inappropriate node_id has been generated by glossary directive if term
is consisted by non-ASCII characters
* #6213: ifconfig: contents after headings are not shown
Testing
--------
@@ -84,6 +93,17 @@ Bugs fixed
----------
* LaTeX: some system labels are not translated
* RemovedInSphinx30Warning is marked as pending
* deprecation warnings are not emitted
- sphinx.application.CONFIG_FILENAME
- sphinx.builders.htmlhelp
- :confval:`viewcode_import`
* #6208: C++, properly parse full xrefs that happen to have a short xref as prefix.
* #6220, #6225: napoleon: AttributeError is raised for raised section having
references
* #6245: circular import error on importing SerializingHTMLBuilder
Testing
--------

View File

@@ -149,6 +149,16 @@ also use these config values:
:confval:`autodoc_mock_imports` for more details. It defaults to
:confval:`autodoc_mock_imports`.
.. versionadded:: 2.0
.. confval:: autosummary_imported_members
A boolean flag indicating whether to document classes and functions imported
in modules. Default is ``False``
.. versionadded:: 2.1
Customizing templates
---------------------

View File

@@ -8,6 +8,11 @@
This extension is quite simple, and features only one directive:
.. warning::
This directive is designed to control only content of document. It could
not control sections, labels and so on.
.. rst:directive:: ifconfig
Include content of the directive only if the Python expression given as an

View File

@@ -24,9 +24,8 @@ from docutils.parsers.rst import Directive, roles
import sphinx
from sphinx import package_dir, locale
from sphinx.config import Config
from sphinx.config import CONFIG_FILENAME # NOQA # for compatibility (RemovedInSphinx30)
from sphinx.deprecation import (
RemovedInSphinx30Warning, RemovedInSphinx40Warning
RemovedInSphinx30Warning, RemovedInSphinx40Warning, deprecated_alias
)
from sphinx.environment import BuildEnvironment
from sphinx.errors import ApplicationError, ConfigError, VersionRequirementError
@@ -1253,3 +1252,12 @@ class TemplateBridge:
specified context (a Python dictionary).
"""
raise NotImplementedError('must be implemented in subclasses')
from sphinx.config import CONFIG_FILENAME # NOQA
deprecated_alias('sphinx.application',
{
'CONFIG_FILENAME': CONFIG_FILENAME,
},
RemovedInSphinx30Warning)

View File

@@ -11,6 +11,7 @@
from os import path
from sphinx.builders.html import StandaloneHTMLBuilder
from sphinx.deprecation import RemovedInSphinx40Warning, deprecated_alias
from sphinx.util import logging
from sphinx.util.osutil import SEP, os_path
@@ -55,6 +56,14 @@ class DirectoryHTMLBuilder(StandaloneHTMLBuilder):
self.globalcontext['no_search_suffix'] = True
# for compatibility
deprecated_alias('sphinx.builders.html',
{
'DirectoryHTMLBuilder': DirectoryHTMLBuilder,
},
RemovedInSphinx40Warning)
def setup(app):
# type: (Sphinx) -> Dict[str, Any]
app.setup_extension('sphinx.builders.html')

View File

@@ -24,9 +24,7 @@ from docutils.utils import relative_path
from sphinx import package_dir, __display_version__
from sphinx.builders import Builder
from sphinx.deprecation import (
RemovedInSphinx30Warning, RemovedInSphinx40Warning, deprecated_alias
)
from sphinx.deprecation import RemovedInSphinx30Warning, RemovedInSphinx40Warning
from sphinx.environment.adapters.asset import ImageAdapter
from sphinx.environment.adapters.indexentries import IndexEntries
from sphinx.environment.adapters.toctree import TocTree
@@ -1232,23 +1230,9 @@ def validate_math_renderer(app):
# for compatibility
from sphinx.builders.dirhtml import DirectoryHTMLBuilder # NOQA
from sphinx.builders.singlehtml import SingleFileHTMLBuilder # NOQA
from sphinxcontrib.serializinghtml import ( # NOQA
LAST_BUILD_FILENAME, JSONHTMLBuilder, PickleHTMLBuilder, SerializingHTMLBuilder
)
deprecated_alias('sphinx.builders.html',
{
'LAST_BUILD_FILENAME': LAST_BUILD_FILENAME,
'DirectoryHTMLBuilder': DirectoryHTMLBuilder,
'JSONHTMLBuilder': JSONHTMLBuilder,
'PickleHTMLBuilder': PickleHTMLBuilder,
'SerializingHTMLBuilder': SerializingHTMLBuilder,
'SingleFileHTMLBuilder': SingleFileHTMLBuilder,
'WebHTMLBuilder': PickleHTMLBuilder,
},
RemovedInSphinx40Warning)
import sphinx.builders.dirhtml # NOQA
import sphinx.builders.singlehtml # NOQA
import sphinxcontrib.serializinghtml # NOQA
def setup(app):

View File

@@ -24,7 +24,7 @@ if False:
from sphinx.application import Sphinx # NOQA
deprecated_alias('sphinx.builders.devhelp',
deprecated_alias('sphinx.builders.htmlhelp',
{
'chm_locales': chm_locales,
'chm_htmlescape': chm_htmlescape,

View File

@@ -13,6 +13,7 @@ from os import path
from docutils import nodes
from sphinx.builders.html import StandaloneHTMLBuilder
from sphinx.deprecation import RemovedInSphinx40Warning, deprecated_alias
from sphinx.environment.adapters.toctree import TocTree
from sphinx.locale import __
from sphinx.util import logging
@@ -201,6 +202,14 @@ class SingleFileHTMLBuilder(StandaloneHTMLBuilder):
self.handle_page('opensearch', {}, 'opensearch.xml', outfilename=fn)
# for compatibility
deprecated_alias('sphinx.builders.html',
{
'SingleFileHTMLBuilder': SingleFileHTMLBuilder,
},
RemovedInSphinx40Warning)
def setup(app):
# type: (Sphinx) -> Dict[str, Any]
app.setup_extension('sphinx.builders.html')

View File

@@ -17,7 +17,7 @@ if False:
from typing import Any, Dict, Type # NOQA
class RemovedInSphinx30Warning(PendingDeprecationWarning):
class RemovedInSphinx30Warning(DeprecationWarning):
pass

View File

@@ -166,7 +166,7 @@ class ObjectDescription(SphinxDirective):
node['objtype'] = node['desctype'] = self.objtype
node['noindex'] = noindex = ('noindex' in self.options)
self.names = [] # type: List[str]
self.names = [] # type: List[Any]
signatures = self.get_signatures()
for i, sig in enumerate(signatures):
# add a signature node for each signature in the current unit

View File

@@ -6391,6 +6391,7 @@ class DefinitionParser:
# if there are '()' left, just skip them
self.skip_ws()
self.skip_string('()')
self.assert_end()
templatePrefix = self._check_template_consistency(name, templatePrefix,
fullSpecShorthand=True)
res1 = ASTNamespace(name, templatePrefix)
@@ -6403,6 +6404,7 @@ class DefinitionParser:
# if there are '()' left, just skip them
self.skip_ws()
self.skip_string('()')
self.assert_end()
return res2, False
except DefinitionError as e2:
errs = []
@@ -7145,7 +7147,6 @@ class CPPDomain(Domain):
parser = DefinitionParser(target, warner, env.config)
try:
ast, isShorthand = parser.parse_xref_object()
parser.assert_end()
except DefinitionError as e:
def findWarning(e): # as arg to stop flake8 from complaining
if typ != 'any' and typ != 'func':
@@ -7154,7 +7155,6 @@ class CPPDomain(Domain):
parser2 = DefinitionParser(target[:-2], warner, env.config)
try:
parser2.parse_xref_object()
parser2.assert_end()
except DefinitionError as e2:
return target[:-2], e2
# strange, that we don't get the error now, use the original

View File

@@ -317,14 +317,13 @@ class PyObject(ObjectDescription):
return fullname, prefix
def get_index_text(self, modname, name):
# type: (str, str) -> str
# type: (str, Tuple[str, str]) -> str
"""Return the text for the index entry of the object."""
raise NotImplementedError('must be implemented in subclasses')
def add_target_and_index(self, name_cls, sig, signode):
# type: (str, str, addnodes.desc_signature) -> None
modname = self.options.get(
'module', self.env.ref_context.get('py:module'))
# type: (Tuple[str, str], str, addnodes.desc_signature) -> None
modname = self.options.get('module', self.env.ref_context.get('py:module'))
fullname = (modname and modname + '.' or '') + name_cls[0]
# note target
if fullname not in self.state.document.ids:
@@ -418,7 +417,7 @@ class PyModulelevel(PyObject):
return self.objtype == 'function'
def get_index_text(self, modname, name_cls):
# type: (str, str) -> str
# type: (str, Tuple[str, str]) -> str
if self.objtype == 'function':
if not modname:
return _('%s() (built-in function)') % name_cls[0]
@@ -443,7 +442,7 @@ class PyClasslike(PyObject):
return self.objtype + ' '
def get_index_text(self, modname, name_cls):
# type: (str, str) -> str
# type: (str, Tuple[str, str]) -> str
if self.objtype == 'class':
if not modname:
return _('%s (built-in class)') % name_cls[0]
@@ -472,7 +471,7 @@ class PyClassmember(PyObject):
return ''
def get_index_text(self, modname, name_cls):
# type: (str, str) -> str
# type: (str, Tuple[str, str]) -> str
name, cls = name_cls
add_modules = self.env.config.add_module_names
if self.objtype == 'method':

View File

@@ -257,6 +257,9 @@ def make_glossary_term(env, textnodes, index_key, source, lineno, new_id=None):
termtext = term.astext()
if new_id is None:
new_id = nodes.make_id('term-' + termtext)
if new_id == 'term':
# the term is not good for node_id. Generate it by sequence number instead.
new_id = 'term-' + str(len(gloss_entries))
if new_id in gloss_entries:
new_id = 'term-' + str(len(gloss_entries))
gloss_entries.add(new_id)
@@ -533,40 +536,60 @@ class StandardDomain(Domain):
for node, settings in env.app.registry.enumerable_nodes.items():
self.enumerable_nodes[node] = settings
@property
def objects(self):
# type: () -> Dict[Tuple[str, str], Tuple[str, str]]
return self.data.setdefault('objects', {}) # (objtype, name) -> docname, labelid
@property
def progoptions(self):
# type: () -> Dict[Tuple[str, str], Tuple[str, str]]
return self.data.setdefault('progoptions', {}) # (program, name) -> docname, labelid
@property
def labels(self):
# type: () -> Dict[str, Tuple[str, str, str]]
return self.data.setdefault('labels', {}) # labelname -> docname, labelid, sectionname
@property
def anonlabels(self):
# type: () -> Dict[str, Tuple[str, str]]
return self.data.setdefault('anonlabels', {}) # labelname -> docname, labelid
def clear_doc(self, docname):
# type: (str) -> None
for key, (fn, _l) in list(self.data['progoptions'].items()):
key = None # type: Any
for key, (fn, _l) in list(self.progoptions.items()):
if fn == docname:
del self.data['progoptions'][key]
for key, (fn, _l) in list(self.data['objects'].items()):
del self.progoptions[key]
for key, (fn, _l) in list(self.objects.items()):
if fn == docname:
del self.data['objects'][key]
for key, (fn, _l, _l) in list(self.data['labels'].items()):
del self.objects[key]
for key, (fn, _l, _l) in list(self.labels.items()):
if fn == docname:
del self.data['labels'][key]
for key, (fn, _l) in list(self.data['anonlabels'].items()):
del self.labels[key]
for key, (fn, _l) in list(self.anonlabels.items()):
if fn == docname:
del self.data['anonlabels'][key]
del self.anonlabels[key]
def merge_domaindata(self, docnames, otherdata):
# type: (List[str], Dict) -> None
# XXX duplicates?
for key, data in otherdata['progoptions'].items():
if data[0] in docnames:
self.data['progoptions'][key] = data
self.progoptions[key] = data
for key, data in otherdata['objects'].items():
if data[0] in docnames:
self.data['objects'][key] = data
self.objects[key] = data
for key, data in otherdata['labels'].items():
if data[0] in docnames:
self.data['labels'][key] = data
self.labels[key] = data
for key, data in otherdata['anonlabels'].items():
if data[0] in docnames:
self.data['anonlabels'][key] = data
self.anonlabels[key] = data
def process_doc(self, env, docname, document):
# type: (BuildEnvironment, str, nodes.document) -> None
labels, anonlabels = self.data['labels'], self.data['anonlabels']
for name, explicit in document.nametypes.items():
if not explicit:
continue
@@ -584,11 +607,11 @@ class StandardDomain(Domain):
# ignore footnote labels, labels automatically generated from a
# link and object descriptions
continue
if name in labels:
if name in self.labels:
logger.warning(__('duplicate label %s, other instance in %s'),
name, env.doc2path(labels[name][0]),
name, env.doc2path(self.labels[name][0]),
location=node)
anonlabels[name] = docname, labelid
self.anonlabels[name] = docname, labelid
if node.tagname in ('section', 'rubric'):
title = cast(nodes.title, node[0])
sectname = clean_astext(title)
@@ -605,15 +628,15 @@ class StandardDomain(Domain):
else:
# anonymous-only labels
continue
labels[name] = docname, labelid, sectname
self.labels[name] = docname, labelid, sectname
def add_object(self, objtype, name, docname, labelid):
# type: (str, str, str, str) -> None
self.data['objects'][objtype, name] = (docname, labelid)
self.objects[objtype, name] = (docname, labelid)
def add_program_option(self, program, name, docname, labelid):
# type: (str, str, str, str) -> None
self.data['progoptions'][program, name] = (docname, labelid)
self.progoptions[program, name] = (docname, labelid)
def build_reference_node(self, fromdocname, builder, docname, labelid,
sectname, rolename, **options):
@@ -667,13 +690,12 @@ class StandardDomain(Domain):
if node['refexplicit']:
# reference to anonymous label; the reference uses
# the supplied link caption
docname, labelid = self.data['anonlabels'].get(target, ('', ''))
docname, labelid = self.anonlabels.get(target, ('', ''))
sectname = node.astext()
else:
# reference to named label; the final node will
# contain the section name after the label
docname, labelid, sectname = self.data['labels'].get(target,
('', '', ''))
docname, labelid, sectname = self.labels.get(target, ('', '', ''))
if not docname:
return None
@@ -682,10 +704,10 @@ class StandardDomain(Domain):
def _resolve_numref_xref(self, env, fromdocname, builder, typ, target, node, contnode):
# type: (BuildEnvironment, str, Builder, str, str, addnodes.pending_xref, nodes.Element) -> nodes.Element # NOQA
if target in self.data['labels']:
docname, labelid, figname = self.data['labels'].get(target, ('', '', ''))
if target in self.labels:
docname, labelid, figname = self.labels.get(target, ('', '', ''))
else:
docname, labelid = self.data['anonlabels'].get(target, ('', ''))
docname, labelid = self.anonlabels.get(target, ('', ''))
figname = None
if not docname:
@@ -744,7 +766,7 @@ class StandardDomain(Domain):
def _resolve_keyword_xref(self, env, fromdocname, builder, typ, target, node, contnode):
# type: (BuildEnvironment, str, Builder, str, str, addnodes.pending_xref, nodes.Element) -> nodes.Element # NOQA
# keywords are oddballs: they are referenced by named labels
docname, labelid, _ = self.data['labels'].get(target, ('', '', ''))
docname, labelid, _ = self.labels.get(target, ('', '', ''))
if not docname:
return None
return make_refnode(builder, fromdocname, docname,
@@ -770,7 +792,7 @@ class StandardDomain(Domain):
# type: (BuildEnvironment, str, Builder, str, str, addnodes.pending_xref, nodes.Element) -> nodes.Element # NOQA
progname = node.get('std:program')
target = target.strip()
docname, labelid = self.data['progoptions'].get((progname, target), ('', ''))
docname, labelid = self.progoptions.get((progname, target), ('', ''))
if not docname:
commands = []
while ws_re.search(target):
@@ -778,8 +800,7 @@ class StandardDomain(Domain):
commands.append(subcommand)
progname = "-".join(commands)
docname, labelid = self.data['progoptions'].get((progname, target),
('', ''))
docname, labelid = self.progoptions.get((progname, target), ('', ''))
if docname:
break
else:
@@ -815,8 +836,8 @@ class StandardDomain(Domain):
# type: (BuildEnvironment, str, Builder, str, str, addnodes.pending_xref, nodes.Element) -> nodes.Element # NOQA
objtypes = self.objtypes_for_role(typ) or []
for objtype in objtypes:
if (objtype, target) in self.data['objects']:
docname, labelid = self.data['objects'][objtype, target]
if (objtype, target) in self.objects:
docname, labelid = self.objects[objtype, target]
break
else:
docname, labelid = '', ''
@@ -840,8 +861,8 @@ class StandardDomain(Domain):
key = (objtype, target)
if objtype == 'term':
key = (objtype, ltarget)
if key in self.data['objects']:
docname, labelid = self.data['objects'][key]
if key in self.objects:
docname, labelid = self.objects[key]
results.append(('std:' + self.role_for_objtype(objtype),
make_refnode(builder, fromdocname, docname,
labelid, contnode)))
@@ -852,22 +873,22 @@ class StandardDomain(Domain):
# handle the special 'doc' reference here
for doc in self.env.all_docs:
yield (doc, clean_astext(self.env.titles[doc]), 'doc', doc, '', -1)
for (prog, option), info in self.data['progoptions'].items():
for (prog, option), info in self.progoptions.items():
if prog:
fullname = ".".join([prog, option])
yield (fullname, fullname, 'cmdoption', info[0], info[1], 1)
else:
yield (option, option, 'cmdoption', info[0], info[1], 1)
for (type, name), info in self.data['objects'].items():
for (type, name), info in self.objects.items():
yield (name, name, type, info[0], info[1],
self.object_types[type].attrs['searchprio'])
for name, info in self.data['labels'].items():
yield (name, info[2], 'label', info[0], info[1], -1)
for name, (docname, labelid, sectionname) in self.labels.items():
yield (name, sectionname, 'label', docname, labelid, -1)
# add anonymous-only labels as well
non_anon_labels = set(self.data['labels'])
for name, info in self.data['anonlabels'].items():
non_anon_labels = set(self.labels)
for name, (docname, labelid) in self.anonlabels.items():
if name not in non_anon_labels:
yield (name, name, 'label', info[0], info[1], -1)
yield (name, name, 'label', docname, labelid, -1)
def get_type_name(self, type, primary=False):
# type: (ObjType, bool) -> str

View File

@@ -733,11 +733,12 @@ def process_generate_options(app):
'But your source_suffix does not contain .rst. Skipped.'))
return
imported_members = app.config.autosummary_imported_members
with mock(app.config.autosummary_mock_imports):
generate_autosummary_docs(genfiles, builder=app.builder,
warn=logger.warning, info=logger.info,
suffix=suffix, base_path=app.srcdir,
app=app)
app=app, imported_members=imported_members)
def setup(app):
@@ -763,5 +764,6 @@ def setup(app):
app.add_config_value('autosummary_generate', [], True, [bool])
app.add_config_value('autosummary_mock_imports',
lambda config: config.autodoc_mock_imports, 'env')
app.add_config_value('autosummary_imported_members', [], False, [bool])
return {'version': sphinx.__display_version__, 'parallel_read_safe': True}

View File

@@ -23,6 +23,7 @@ from docutils import nodes
import sphinx
from sphinx.util.docutils import SphinxDirective
from sphinx.util.nodes import nested_parse_with_titles
if False:
# For type annotation
@@ -48,8 +49,7 @@ class IfConfig(SphinxDirective):
node.document = self.state.document
self.set_source_info(node)
node['expr'] = self.arguments[0]
self.state.nested_parse(self.content, self.content_offset,
node, match_titles=True)
nested_parse_with_titles(self.state, self.content, node)
return [node]

View File

@@ -100,7 +100,7 @@ class GoogleDocstring:
"""
_name_rgx = re.compile(r"^\s*(:(?P<role>\w+):`(?P<name>[a-zA-Z0-9_.-]+)`|"
_name_rgx = re.compile(r"^\s*((?::(?P<role>\S+):)?`(?P<name>[a-zA-Z0-9_.-]+)`|"
r" (?P<name2>[a-zA-Z0-9_.-]+))\s*", re.X)
def __init__(self, docstring, config=None, app=None, what='', name='',
@@ -700,9 +700,9 @@ class GoogleDocstring:
fields = self._consume_fields(parse_type=False, prefer_type=True)
lines = [] # type: List[str]
for _name, _type, _desc in fields:
m = self._name_rgx.match(_type).groupdict()
if m['role']:
_type = m['name']
m = self._name_rgx.match(_type)
if m and m.group('name'):
_type = m.group('name')
_type = ' ' + _type if _type else ''
_desc = self._strip_empty(_desc)
_descs = ' ' + '\n '.join(_desc) if any(_desc) else ''

View File

@@ -251,6 +251,7 @@ def setup(app):
app.add_config_value('viewcode_import', None, False)
app.add_config_value('viewcode_enable_epub', False, False)
app.add_config_value('viewcode_follow_imported_members', True, False)
app.connect('config-inited', migrate_viewcode_import)
app.connect('doctree-read', doctree_read)
app.connect('env-merge-info', env_merge_info)
app.connect('html-collect-pages', collect_pages)

View File

@@ -1,11 +1,14 @@
# Minimal makefile for Sphinx documentation
#
# You can set these variables from the command line.
SPHINXOPTS =
SPHINXBUILD = sphinx-build
SOURCEDIR = {{ rsrcdir }}
BUILDDIR = {{ rbuilddir }}
# You can set these variables from the command line. For example:
# SPHINXOPTS='-E -W -n' make html
# will run the html builder in a clean environment (-E), treating warnings
# as errors (-W), in nitpicky mode (-n).
SPHINXOPTS ?=
SPHINXBUILD ?= sphinx-build
SOURCEDIR ?= {{ rsrcdir }}
BUILDDIR ?= {{ rbuilddir }}
# Put it first so that "make" without argument is like "make help".
help:
@@ -17,3 +20,4 @@ help:
# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
%: Makefile
@$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)

View File

@@ -21,6 +21,7 @@ from io import StringIO
from os import path
from sphinx.deprecation import RemovedInSphinx30Warning, RemovedInSphinx40Warning
from sphinx.testing.path import path as Path
if False:
# For type annotation
@@ -190,15 +191,18 @@ fs_encoding = sys.getfilesystemencoding() or sys.getdefaultencoding()
def abspath(pathdir):
# type: (str) -> str
pathdir = path.abspath(pathdir)
if isinstance(pathdir, bytes):
try:
pathdir = pathdir.decode(fs_encoding)
except UnicodeDecodeError:
raise UnicodeDecodeError('multibyte filename not supported on '
'this filesystem encoding '
'(%r)' % fs_encoding)
return pathdir
if isinstance(pathdir, Path):
return pathdir.abspath()
else:
pathdir = path.abspath(pathdir)
if isinstance(pathdir, bytes):
try:
pathdir = pathdir.decode(fs_encoding)
except UnicodeDecodeError:
raise UnicodeDecodeError('multibyte filename not supported on '
'this filesystem encoding '
'(%r)' % fs_encoding)
return pathdir
def getcwd():

View File

@@ -0,0 +1 @@
from .autosummary_dummy_module import Bar, foo

View File

@@ -0,0 +1,8 @@
class Bar:
"""Bar class"""
pass
def foo():
"""Foo function"""
pass

View File

@@ -0,0 +1,7 @@
import os
import sys
sys.path.insert(0, os.path.abspath('.'))
extensions = ['sphinx.ext.autosummary']
autosummary_generate = True
autosummary_imported_members = True

View File

@@ -0,0 +1,7 @@
test-ext-autosummary-mock_imports
=================================
.. autosummary::
:toctree: generated
autosummary_dummy_package

View File

@@ -755,6 +755,20 @@ def test_attributes():
check('member', 'int *[[attr]] *i', {1: 'i__iPP', 2: '1i'})
def test_xref_parsing():
def check(target):
class Config:
cpp_id_attributes = ["id_attr"]
cpp_paren_attributes = ["paren_attr"]
parser = DefinitionParser(target, None, Config())
ast, isShorthand = parser.parse_xref_object()
parser.assert_end()
check('f')
check('f()')
check('void f()')
check('T f()')
# def test_print():
# # used for getting all the ids out for checking
# for a in ids:

View File

@@ -8,135 +8,116 @@
:license: BSD, see LICENSE for details.
"""
from collections import namedtuple
from unittest import mock
import pytest
from sphinx import locale
from sphinx.environment.adapters.indexentries import IndexEntries
Environment = namedtuple('Environment', 'indexentries')
dummy_builder = mock.Mock()
dummy_builder.get_relative_uri.return_value = ''
from sphinx.testing import restructuredtext
def test_create_single_index():
# type, value, tid, main, index_key
env = Environment({
'index': [
('single', 'docutils', 'id1', '', None),
('single', 'Python', 'id2', '', None),
('single', 'pip; install', 'id3', '', None),
('single', 'pip; upgrade', 'id4', '', None),
('single', 'Sphinx', 'id5', '', None),
('single', 'Ель', 'id6', '', None),
('single', 'ёлка', 'id7', '', None),
('single', '‏תירבע‎', 'id8', '', None),
('single', '9-symbol', 'id9', '', None),
('single', '&-symbol', 'id10', '', None),
],
})
index = IndexEntries(env).create_index(dummy_builder)
@pytest.mark.sphinx('dummy')
def test_create_single_index(app):
app.env.indexentries.clear()
text = (".. index:: docutils\n"
".. index:: Python\n"
".. index:: pip; install\n"
".. index:: pip; upgrade\n"
".. index:: Sphinx\n"
".. index:: Ель\n"
".. index:: ёлка\n"
".. index:: ‏תירבע‎\n"
".. index:: 9-symbol\n"
".. index:: &-symbol\n")
restructuredtext.parse(app, text)
index = IndexEntries(app.env).create_index(app.builder)
assert len(index) == 6
assert index[0] == ('Symbols', [('&-symbol', [[('', '#id10')], [], None]),
('9-symbol', [[('', '#id9')], [], None])])
assert index[1] == ('D', [('docutils', [[('', '#id1')], [], None])])
assert index[2] == ('P', [('pip', [[], [('install', [('', '#id3')]),
('upgrade', [('', '#id4')])], None]),
('Python', [[('', '#id2')], [], None])])
assert index[3] == ('S', [('Sphinx', [[('', '#id5')], [], None])])
assert index[4] == ('Е', [('ёлка', [[('', '#id7')], [], None]),
('Ель', [[('', '#id6')], [], None])])
assert index[5] == ('ת', [('‏תירבע‎', [[('', '#id8')], [], None])])
assert index[0] == ('Symbols', [('&-symbol', [[('', '#index-9')], [], None]),
('9-symbol', [[('', '#index-8')], [], None])])
assert index[1] == ('D', [('docutils', [[('', '#index-0')], [], None])])
assert index[2] == ('P', [('pip', [[], [('install', [('', '#index-2')]),
('upgrade', [('', '#index-3')])], None]),
('Python', [[('', '#index-1')], [], None])])
assert index[3] == ('S', [('Sphinx', [[('', '#index-4')], [], None])])
assert index[4] == ('Е', [('ёлка', [[('', '#index-6')], [], None]),
('Ель', [[('', '#index-5')], [], None])])
assert index[5] == ('ת', [('‏תירבע‎', [[('', '#index-7')], [], None])])
def test_create_pair_index():
# type, value, tid, main, index_key
env = Environment({
'index': [
('pair', 'docutils; reStructuredText', 'id1', '', None),
('pair', 'Python; interpreter', 'id2', '', None),
('pair', 'Sphinx; documentation tool', 'id3', '', None),
],
})
index = IndexEntries(env).create_index(dummy_builder)
@pytest.mark.sphinx('dummy')
def test_create_pair_index(app):
app.env.indexentries.clear()
text = (".. index:: pair: docutils; reStructuredText\n"
".. index:: pair: Python; interpreter\n"
".. index:: pair: Sphinx; documentation tool\n")
restructuredtext.parse(app, text)
index = IndexEntries(app.env).create_index(app.builder)
assert len(index) == 5
assert index[0] == ('D',
[('documentation tool', [[], [('Sphinx', [('', '#id3')])], None]),
('docutils', [[], [('reStructuredText', [('', '#id1')])], None])])
assert index[1] == ('I', [('interpreter', [[], [('Python', [('', '#id2')])], None])])
assert index[2] == ('P', [('Python', [[], [('interpreter', [('', '#id2')])], None])])
[('documentation tool', [[], [('Sphinx', [('', '#index-2')])], None]),
('docutils', [[], [('reStructuredText', [('', '#index-0')])], None])])
assert index[1] == ('I', [('interpreter', [[], [('Python', [('', '#index-1')])], None])])
assert index[2] == ('P', [('Python', [[], [('interpreter', [('', '#index-1')])], None])])
assert index[3] == ('R',
[('reStructuredText', [[], [('docutils', [('', '#id1')])], None])])
[('reStructuredText', [[], [('docutils', [('', '#index-0')])], None])])
assert index[4] == ('S',
[('Sphinx', [[], [('documentation tool', [('', '#id3')])], None])])
[('Sphinx', [[], [('documentation tool', [('', '#index-2')])], None])])
def test_create_triple_index():
# type, value, tid, main, index_key
env = Environment({
'index': [
('triple', 'foo; bar; baz', 'id1', '', None),
('triple', 'Python; Sphinx; reST', 'id2', '', None),
],
})
index = IndexEntries(env).create_index(dummy_builder)
@pytest.mark.sphinx('dummy')
def test_create_triple_index(app):
app.env.indexentries.clear()
text = (".. index:: triple: foo; bar; baz\n"
".. index:: triple: Python; Sphinx; reST\n")
restructuredtext.parse(app, text)
index = IndexEntries(app.env).create_index(app.builder)
assert len(index) == 5
assert index[0] == ('B', [('bar', [[], [('baz, foo', [('', '#id1')])], None]),
('baz', [[], [('foo bar', [('', '#id1')])], None])])
assert index[1] == ('F', [('foo', [[], [('bar baz', [('', '#id1')])], None])])
assert index[2] == ('P', [('Python', [[], [('Sphinx reST', [('', '#id2')])], None])])
assert index[3] == ('R', [('reST', [[], [('Python Sphinx', [('', '#id2')])], None])])
assert index[4] == ('S', [('Sphinx', [[], [('reST, Python', [('', '#id2')])], None])])
assert index[0] == ('B', [('bar', [[], [('baz, foo', [('', '#index-0')])], None]),
('baz', [[], [('foo bar', [('', '#index-0')])], None])])
assert index[1] == ('F', [('foo', [[], [('bar baz', [('', '#index-0')])], None])])
assert index[2] == ('P', [('Python', [[], [('Sphinx reST', [('', '#index-1')])], None])])
assert index[3] == ('R', [('reST', [[], [('Python Sphinx', [('', '#index-1')])], None])])
assert index[4] == ('S', [('Sphinx', [[], [('reST, Python', [('', '#index-1')])], None])])
def test_create_see_index():
locale.init([], None)
# type, value, tid, main, index_key
env = Environment({
'index': [
('see', 'docutils; reStructuredText', 'id1', '', None),
('see', 'Python; interpreter', 'id2', '', None),
('see', 'Sphinx; documentation tool', 'id3', '', None),
],
})
index = IndexEntries(env).create_index(dummy_builder)
@pytest.mark.sphinx('dummy')
def test_create_see_index(app):
app.env.indexentries.clear()
text = (".. index:: see: docutils; reStructuredText\n"
".. index:: see: Python; interpreter\n"
".. index:: see: Sphinx; documentation tool\n")
restructuredtext.parse(app, text)
index = IndexEntries(app.env).create_index(app.builder)
assert len(index) == 3
assert index[0] == ('D', [('docutils', [[], [('see reStructuredText', [])], None])])
assert index[1] == ('P', [('Python', [[], [('see interpreter', [])], None])])
assert index[2] == ('S', [('Sphinx', [[], [('see documentation tool', [])], None])])
def test_create_seealso_index():
locale.init([], None)
# type, value, tid, main, index_key
env = Environment({
'index': [
('seealso', 'docutils; reStructuredText', 'id1', '', None),
('seealso', 'Python; interpreter', 'id2', '', None),
('seealso', 'Sphinx; documentation tool', 'id3', '', None),
],
})
index = IndexEntries(env).create_index(dummy_builder)
@pytest.mark.sphinx('dummy')
def test_create_seealso_index(app):
app.env.indexentries.clear()
text = (".. index:: seealso: docutils; reStructuredText\n"
".. index:: seealso: Python; interpreter\n"
".. index:: seealso: Sphinx; documentation tool\n")
restructuredtext.parse(app, text)
index = IndexEntries(app.env).create_index(app.builder)
assert len(index) == 3
assert index[0] == ('D', [('docutils', [[], [('see also reStructuredText', [])], None])])
assert index[1] == ('P', [('Python', [[], [('see also interpreter', [])], None])])
assert index[2] == ('S', [('Sphinx', [[], [('see also documentation tool', [])], None])])
def test_create_index_by_key():
# type, value, tid, main, index_key
env = Environment({
'index': [
('single', 'docutils', 'id1', '', None),
('single', 'Python', 'id2', '', None),
('single', 'スフィンクス', 'id3', '', ''),
],
})
index = IndexEntries(env).create_index(dummy_builder)
@pytest.mark.sphinx('dummy')
def test_create_index_by_key(app):
app.env.indexentries.clear()
# At present, only glossary directive is able to create index key
text = (".. glossary::\n"
"\n"
" docutils\n"
" Python\n"
" スフィンクス : ス\n")
restructuredtext.parse(app, text)
index = IndexEntries(app.env).create_index(app.builder)
assert len(index) == 3
assert index[0] == ('D', [('docutils', [[('', '#id1')], [], None])])
assert index[1] == ('P', [('Python', [[('', '#id2')], [], None])])
assert index[2] == ('', [('スフィンクス', [[('', '#id3')], [], ''])])
assert index[0] == ('D', [('docutils', [[('main', '#term-docutils')], [], None])])
assert index[1] == ('P', [('Python', [[('main', '#term-python')], [], None])])
assert index[2] == ('', [('スフィンクス', [[('main', '#term-2')], [], ''])])

View File

@@ -242,3 +242,23 @@ def test_autosummary_mock_imports(app, status, warning):
assert app.env.get_doctree('generated/foo')
finally:
sys.modules.pop('foo', None) # unload foo module
@pytest.mark.sphinx('dummy', testroot='ext-autosummary-imported_members')
def test_autosummary_imported_members(app, status, warning):
try:
app.build()
# generated/foo is generated successfully
assert app.env.get_doctree('generated/autosummary_dummy_package')
module = (app.srcdir / 'generated' / 'autosummary_dummy_package.rst').text()
assert (' .. autosummary::\n'
' \n'
' Bar\n'
' \n' in module)
assert (' .. autosummary::\n'
' \n'
' foo\n'
' \n' in module)
finally:
sys.modules.pop('autosummary_dummy_package', None)

View File

@@ -90,7 +90,7 @@ def test_inheritance_diagram_latex_alias(app, status, warning):
def test_import_classes(rootdir):
from sphinx.application import Sphinx, TemplateBridge
from sphinx.parsers import Parser, RSTParser
from sphinx.util.i18n import CatalogInfo
try:
@@ -120,16 +120,16 @@ def test_import_classes(rootdir):
assert classes == []
# all of classes in the module
classes = import_classes('sphinx.application', None)
assert set(classes) == {Sphinx, TemplateBridge}
classes = import_classes('sphinx.parsers', None)
assert set(classes) == {Parser, RSTParser}
# specified class in the module
classes = import_classes('sphinx.application.Sphinx', None)
assert classes == [Sphinx]
classes = import_classes('sphinx.parsers.Parser', None)
assert classes == [Parser]
# specified class in current module
classes = import_classes('Sphinx', 'sphinx.application')
assert classes == [Sphinx]
classes = import_classes('Parser', 'sphinx.parsers')
assert classes == [Parser]
# relative module name to current module
classes = import_classes('i18n.CatalogInfo', 'sphinx.util')

View File

@@ -473,12 +473,21 @@ Raises:
A setting wasn't specified, or was invalid.
ValueError:
Something something value error.
:py:class:`AttributeError`
errors for missing attributes.
~InvalidDimensionsError
If the dimensions couldn't be parsed.
`InvalidArgumentsError`
If the arguments are invalid.
""", """
Example Function
:raises RuntimeError: A setting wasn't specified, or was invalid.
:raises ValueError: Something something value error.
:raises AttributeError: errors for missing attributes.
:raises ~InvalidDimensionsError: If the dimensions couldn't be parsed.
:raises InvalidArgumentsError: If the arguments are invalid.
"""),
################################
("""