mirror of
https://github.com/sphinx-doc/sphinx.git
synced 2025-02-25 18:55:22 -06:00
Merge branch '2.0' into refactor_citations
This commit is contained in:
20
CHANGES
20
CHANGES
@@ -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
|
||||
--------
|
||||
|
||||
@@ -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
|
||||
---------------------
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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')
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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')
|
||||
|
||||
@@ -17,7 +17,7 @@ if False:
|
||||
from typing import Any, Dict, Type # NOQA
|
||||
|
||||
|
||||
class RemovedInSphinx30Warning(PendingDeprecationWarning):
|
||||
class RemovedInSphinx30Warning(DeprecationWarning):
|
||||
pass
|
||||
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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':
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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]
|
||||
|
||||
|
||||
|
||||
@@ -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 ''
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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():
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
from .autosummary_dummy_module import Bar, foo
|
||||
@@ -0,0 +1,8 @@
|
||||
class Bar:
|
||||
"""Bar class"""
|
||||
pass
|
||||
|
||||
|
||||
def foo():
|
||||
"""Foo function"""
|
||||
pass
|
||||
@@ -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
|
||||
@@ -0,0 +1,7 @@
|
||||
test-ext-autosummary-mock_imports
|
||||
=================================
|
||||
|
||||
.. autosummary::
|
||||
:toctree: generated
|
||||
|
||||
autosummary_dummy_package
|
||||
@@ -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:
|
||||
|
||||
@@ -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')], [], 'ス'])])
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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')
|
||||
|
||||
@@ -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.
|
||||
"""),
|
||||
################################
|
||||
("""
|
||||
|
||||
Reference in New Issue
Block a user