mirror of
https://github.com/sphinx-doc/sphinx.git
synced 2025-02-25 18:55:22 -06:00
Merge branch '2.0'
This commit is contained in:
commit
5640cf879f
4
CHANGES
4
CHANGES
@ -46,6 +46,8 @@ Incompatible changes
|
||||
Deprecated
|
||||
----------
|
||||
|
||||
* ``sphinx.environment.BuildEnvironment.indexentries``
|
||||
* ``sphinx.environment.collectors.indexentries.IndexEntriesCollector``
|
||||
* ``sphinx.io.FiletypeNotFoundError``
|
||||
* ``sphinx.io.get_filetype()``
|
||||
|
||||
@ -55,11 +57,13 @@ Features added
|
||||
* #6910: inheritance_diagram: Make the background of diagrams transparent
|
||||
* #6446: duration: Add ``sphinx.ext.durations`` to inspect which documents slow
|
||||
down the build
|
||||
* #6837: LaTeX: Support a nested table
|
||||
|
||||
Bugs fixed
|
||||
----------
|
||||
|
||||
* #6925: html: Remove redundant type="text/javascript" from <script> elements
|
||||
* #6906: autodoc: failed to read the source codes encoeded in cp1251
|
||||
|
||||
Testing
|
||||
--------
|
||||
|
@ -26,6 +26,16 @@ The following is a list of deprecated interfaces.
|
||||
- (will be) Removed
|
||||
- Alternatives
|
||||
|
||||
* - ``sphinx.environment.BuildEnvironment.indexentries``
|
||||
- 2.4
|
||||
- 4.0
|
||||
- ``sphinx.domains.index.IndexDomain``
|
||||
|
||||
* - ``sphinx.environment.collectors.indexentries.IndexEntriesCollector``
|
||||
- 2.4
|
||||
- 4.0
|
||||
- ``sphinx.domains.index.IndexDomain``
|
||||
|
||||
* - ``sphinx.io.FiletypeNotFoundError``
|
||||
- 2.4
|
||||
- 4.0
|
||||
|
@ -78,6 +78,7 @@ builtin_extensions = (
|
||||
'sphinx.domains.changeset',
|
||||
'sphinx.domains.citation',
|
||||
'sphinx.domains.cpp',
|
||||
'sphinx.domains.index',
|
||||
'sphinx.domains.javascript',
|
||||
'sphinx.domains.math',
|
||||
'sphinx.domains.python',
|
||||
@ -106,7 +107,6 @@ builtin_extensions = (
|
||||
'sphinx.environment.collectors.metadata',
|
||||
'sphinx.environment.collectors.title',
|
||||
'sphinx.environment.collectors.toctree',
|
||||
'sphinx.environment.collectors.indexentries',
|
||||
# 1st party extensions
|
||||
'sphinxcontrib.applehelp',
|
||||
'sphinxcontrib.devhelp',
|
||||
@ -350,8 +350,8 @@ class Sphinx:
|
||||
if self._warncount and self.keep_going:
|
||||
self.statuscode = 1
|
||||
|
||||
status = (self.statuscode == 0 and
|
||||
__('succeeded') or __('finished with problems'))
|
||||
status = (__('succeeded') if self.statuscode == 0
|
||||
else __('finished with problems'))
|
||||
if self._warncount:
|
||||
if self.warningiserror:
|
||||
msg = __('build %s, %s warning (with warnings treated as errors).',
|
||||
@ -494,7 +494,7 @@ class Sphinx:
|
||||
logger.debug('[app] adding config value: %r',
|
||||
(name, default, rebuild) + ((types,) if types else ())) # type: ignore
|
||||
if rebuild in (False, True):
|
||||
rebuild = rebuild and 'env' or ''
|
||||
rebuild = 'env' if rebuild else ''
|
||||
self.config.add(name, default, rebuild, types)
|
||||
|
||||
def add_event(self, name):
|
||||
|
@ -432,11 +432,8 @@ class StandaloneHTMLBuilder(Builder):
|
||||
else:
|
||||
self.last_updated = None
|
||||
|
||||
logo = self.config.html_logo and \
|
||||
path.basename(self.config.html_logo) or ''
|
||||
|
||||
favicon = self.config.html_favicon and \
|
||||
path.basename(self.config.html_favicon) or ''
|
||||
logo = path.basename(self.config.html_logo) if self.config.html_logo else ''
|
||||
favicon = path.basename(self.config.html_favicon) if self.config.html_favicon else ''
|
||||
|
||||
if not isinstance(self.config.html_use_opensearch, str):
|
||||
logger.warning(__('html_use_opensearch config value must now be a string'))
|
||||
@ -538,7 +535,7 @@ class StandaloneHTMLBuilder(Builder):
|
||||
|
||||
# title rendered as HTML
|
||||
title_node = self.env.longtitles.get(docname)
|
||||
title = title_node and self.render_partial(title_node)['title'] or ''
|
||||
title = self.render_partial(title_node)['title'] if title_node else ''
|
||||
|
||||
# Suffix for the document
|
||||
source_suffix = path.splitext(self.env.doc2path(docname))[1]
|
||||
@ -595,7 +592,7 @@ class StandaloneHTMLBuilder(Builder):
|
||||
self.imgpath = relative_uri(self.get_target_uri(docname), self.imagedir)
|
||||
self.post_process_images(doctree)
|
||||
title_node = self.env.longtitles.get(docname)
|
||||
title = title_node and self.render_partial(title_node)['title'] or ''
|
||||
title = self.render_partial(title_node)['title'] if title_node else ''
|
||||
self.index_page(docname, doctree, title)
|
||||
|
||||
def finish(self) -> None:
|
||||
|
@ -243,7 +243,7 @@ class LaTeXBuilder(Builder):
|
||||
|
||||
doctree = self.assemble_doctree(
|
||||
docname, toctree_only,
|
||||
appendices=((docclass != 'howto') and self.config.latex_appendices or []))
|
||||
appendices=(self.config.latex_appendices if docclass != 'howto' else []))
|
||||
doctree['tocdepth'] = tocdepth
|
||||
self.post_process_images(doctree)
|
||||
self.update_doc_context(title, author)
|
||||
@ -360,13 +360,6 @@ class LaTeXBuilder(Builder):
|
||||
copy_asset_file(path.join(staticdirname, 'Makefile_t'),
|
||||
self.outdir, context=context)
|
||||
|
||||
# the logo is handled differently
|
||||
if self.config.latex_logo:
|
||||
if not path.isfile(path.join(self.confdir, self.config.latex_logo)):
|
||||
raise SphinxError(__('logo file %r does not exist') % self.config.latex_logo)
|
||||
else:
|
||||
copy_asset_file(path.join(self.confdir, self.config.latex_logo), self.outdir)
|
||||
|
||||
@progress_message(__('copying additional files'))
|
||||
def copy_latex_additional_files(self) -> None:
|
||||
for filename in self.config.latex_additional_files:
|
||||
@ -386,6 +379,11 @@ class LaTeXBuilder(Builder):
|
||||
except Exception as err:
|
||||
logger.warning(__('cannot copy image file %r: %s'),
|
||||
path.join(self.srcdir, src), err)
|
||||
if self.config.latex_logo:
|
||||
if not path.isfile(path.join(self.confdir, self.config.latex_logo)):
|
||||
raise SphinxError(__('logo file %r does not exist') % self.config.latex_logo)
|
||||
else:
|
||||
copy_asset_file(path.join(self.confdir, self.config.latex_logo), self.outdir)
|
||||
|
||||
def write_message_catalog(self) -> None:
|
||||
formats = self.config.numfig_format
|
||||
|
@ -359,7 +359,7 @@ def generate(d: Dict, overwrite: bool = True, silent: bool = False, templatedir:
|
||||
|
||||
ensuredir(d['path'])
|
||||
|
||||
srcdir = d['sep'] and path.join(d['path'], 'source') or d['path']
|
||||
srcdir = path.join(d['path'], 'source') if d['sep'] else d['path']
|
||||
|
||||
ensuredir(srcdir)
|
||||
if d['sep']:
|
||||
@ -405,15 +405,15 @@ def generate(d: Dict, overwrite: bool = True, silent: bool = False, templatedir:
|
||||
batchfile_template = 'quickstart/make.bat_t'
|
||||
|
||||
if d['makefile'] is True:
|
||||
d['rsrcdir'] = d['sep'] and 'source' or '.'
|
||||
d['rbuilddir'] = d['sep'] and 'build' or d['dot'] + 'build'
|
||||
d['rsrcdir'] = 'source' if d['sep'] else '.'
|
||||
d['rbuilddir'] = 'build' if d['sep'] else d['dot'] + 'build'
|
||||
# use binary mode, to avoid writing \r\n on Windows
|
||||
write_file(path.join(d['path'], 'Makefile'),
|
||||
template.render(makefile_template, d), '\n')
|
||||
|
||||
if d['batchfile'] is True:
|
||||
d['rsrcdir'] = d['sep'] and 'source' or '.'
|
||||
d['rbuilddir'] = d['sep'] and 'build' or d['dot'] + 'build'
|
||||
d['rsrcdir'] = 'source' if d['sep'] else '.'
|
||||
d['rbuilddir'] = 'build' if d['sep'] else d['dot'] + 'build'
|
||||
write_file(path.join(d['path'], 'make.bat'),
|
||||
template.render(batchfile_template, d), '\r\n')
|
||||
|
||||
|
@ -300,7 +300,7 @@ class HList(SphinxDirective):
|
||||
index = 0
|
||||
newnode = addnodes.hlist()
|
||||
for column in range(ncolumns):
|
||||
endindex = index + (column < nmore and (npercol + 1) or npercol)
|
||||
endindex = index + ((npercol + 1) if column < nmore else npercol)
|
||||
bullet_list = nodes.bullet_list()
|
||||
bullet_list += fulllist.children[index:endindex]
|
||||
newnode += addnodes.hlistcol('', bullet_list)
|
||||
|
65
sphinx/domains/index.py
Normal file
65
sphinx/domains/index.py
Normal file
@ -0,0 +1,65 @@
|
||||
"""
|
||||
sphinx.domains.index
|
||||
~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
The index domain.
|
||||
|
||||
:copyright: Copyright 2007-2019 by the Sphinx team, see AUTHORS.
|
||||
:license: BSD, see LICENSE for details.
|
||||
"""
|
||||
|
||||
from typing import Any, Dict, Iterable, List, Tuple
|
||||
|
||||
from docutils.nodes import Node
|
||||
|
||||
from sphinx import addnodes
|
||||
from sphinx.application import Sphinx
|
||||
from sphinx.domains import Domain
|
||||
from sphinx.environment import BuildEnvironment
|
||||
from sphinx.util import logging
|
||||
from sphinx.util import split_index_msg
|
||||
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class IndexDomain(Domain):
|
||||
"""Mathematics domain."""
|
||||
name = 'index'
|
||||
label = 'index'
|
||||
|
||||
@property
|
||||
def entries(self) -> Dict[str, List[Tuple[str, str, str, str, str]]]:
|
||||
return self.data.setdefault('entries', {})
|
||||
|
||||
def clear_doc(self, docname: str) -> None:
|
||||
self.entries.pop(docname, None)
|
||||
|
||||
def merge_domaindata(self, docnames: Iterable[str], otherdata: Dict) -> None:
|
||||
for docname in docnames:
|
||||
self.entries[docname] = otherdata['entries'][docname]
|
||||
|
||||
def process_doc(self, env: BuildEnvironment, docname: str, document: Node) -> None:
|
||||
"""Process a document after it is read by the environment."""
|
||||
entries = self.entries.setdefault(env.docname, [])
|
||||
for node in document.traverse(addnodes.index):
|
||||
try:
|
||||
for entry in node['entries']:
|
||||
split_index_msg(entry[0], entry[1])
|
||||
except ValueError as exc:
|
||||
logger.warning(str(exc), location=node)
|
||||
node.parent.remove(node)
|
||||
else:
|
||||
for entry in node['entries']:
|
||||
entries.append(entry)
|
||||
|
||||
|
||||
def setup(app: Sphinx) -> Dict[str, Any]:
|
||||
app.add_domain(IndexDomain)
|
||||
|
||||
return {
|
||||
'version': 'builtin',
|
||||
'env_version': 1,
|
||||
'parallel_read_safe': True,
|
||||
'parallel_write_safe': True,
|
||||
}
|
@ -105,7 +105,7 @@ class JSObject(ObjectDescription):
|
||||
def add_target_and_index(self, name_obj: Tuple[str, str], sig: str,
|
||||
signode: desc_signature) -> None:
|
||||
mod_name = self.env.ref_context.get('js:module')
|
||||
fullname = (mod_name and mod_name + '.' or '') + name_obj[0]
|
||||
fullname = (mod_name + '.' if mod_name else '') + name_obj[0]
|
||||
if fullname not in self.state.document.ids:
|
||||
signode['names'].append(fullname)
|
||||
signode['ids'].append(fullname.replace('$', '_S_'))
|
||||
@ -385,7 +385,7 @@ class JavaScriptDomain(Domain):
|
||||
) -> Element:
|
||||
mod_name = node.get('js:module')
|
||||
prefix = node.get('js:object')
|
||||
searchorder = node.hasattr('refspecific') and 1 or 0
|
||||
searchorder = 1 if node.hasattr('refspecific') else 0
|
||||
name, obj = self.find_obj(env, mod_name, prefix, target, typ, searchorder)
|
||||
if not obj:
|
||||
return None
|
||||
|
@ -306,7 +306,7 @@ class PyObject(ObjectDescription):
|
||||
def add_target_and_index(self, name_cls: Tuple[str, str], sig: str,
|
||||
signode: desc_signature) -> None:
|
||||
modname = self.options.get('module', self.env.ref_context.get('py:module'))
|
||||
fullname = (modname and modname + '.' or '') + name_cls[0]
|
||||
fullname = (modname + '.' if modname else '') + name_cls[0]
|
||||
# note target
|
||||
if fullname not in self.state.document.ids:
|
||||
signode['names'].append(fullname)
|
||||
@ -821,7 +821,7 @@ class PythonModuleIndex(Index):
|
||||
num_toplevels += 1
|
||||
subtype = 0
|
||||
|
||||
qualifier = deprecated and _('Deprecated') or ''
|
||||
qualifier = _('Deprecated') if deprecated else ''
|
||||
entries.append(IndexEntry(stripped + modname, subtype, docname,
|
||||
'module-' + stripped + modname, platforms,
|
||||
qualifier, synopsis))
|
||||
@ -999,7 +999,7 @@ class PythonDomain(Domain):
|
||||
) -> Element:
|
||||
modname = node.get('py:module')
|
||||
clsname = node.get('py:class')
|
||||
searchmode = node.hasattr('refspecific') and 1 or 0
|
||||
searchmode = 1 if node.hasattr('refspecific') else 0
|
||||
matches = self.find_obj(env, modname, clsname, target,
|
||||
type, searchmode)
|
||||
if not matches:
|
||||
|
@ -14,7 +14,8 @@ import warnings
|
||||
from collections import defaultdict
|
||||
from copy import copy
|
||||
from os import path
|
||||
from typing import Any, Callable, Dict, Generator, Iterator, List, Set, Tuple, Union
|
||||
from typing import Any, Callable, Dict, Generator, IO, Iterator, List, Set, Tuple, Union
|
||||
from typing import cast
|
||||
|
||||
from docutils import nodes
|
||||
from docutils.nodes import Node
|
||||
@ -167,11 +168,6 @@ class BuildEnvironment:
|
||||
self.domaindata = {} # type: Dict[str, Dict]
|
||||
# domainname -> domain-specific dict
|
||||
|
||||
# Other inventories
|
||||
self.indexentries = {} # type: Dict[str, List[Tuple[str, str, str, str, str]]]
|
||||
# docname -> list of
|
||||
# (type, str, target, aliasname)
|
||||
|
||||
# these map absolute path -> (docnames, unique filename)
|
||||
self.images = FilenameUniqDict() # type: FilenameUniqDict
|
||||
self.dlfiles = DownloadFiles() # type: DownloadFiles
|
||||
@ -640,3 +636,131 @@ class BuildEnvironment:
|
||||
for domain in self.domains.values():
|
||||
domain.check_consistency()
|
||||
self.events.emit('env-check-consistency', self)
|
||||
<<<<<<< HEAD
|
||||
=======
|
||||
|
||||
# --------- METHODS FOR COMPATIBILITY --------------------------------------
|
||||
|
||||
def update(self, config: Config, srcdir: str, doctreedir: str) -> List[str]:
|
||||
warnings.warn('env.update() is deprecated. Please use builder.read() instead.',
|
||||
RemovedInSphinx30Warning, stacklevel=2)
|
||||
return self.app.builder.read()
|
||||
|
||||
def _read_serial(self, docnames: List[str], app: "Sphinx") -> None:
|
||||
warnings.warn('env._read_serial() is deprecated. Please use builder.read() instead.',
|
||||
RemovedInSphinx30Warning, stacklevel=2)
|
||||
return self.app.builder._read_serial(docnames)
|
||||
|
||||
def _read_parallel(self, docnames: List[str], app: "Sphinx", nproc: int) -> None:
|
||||
warnings.warn('env._read_parallel() is deprecated. Please use builder.read() instead.',
|
||||
RemovedInSphinx30Warning, stacklevel=2)
|
||||
return self.app.builder._read_parallel(docnames, nproc)
|
||||
|
||||
def read_doc(self, docname: str, app: "Sphinx" = None) -> None:
|
||||
warnings.warn('env.read_doc() is deprecated. Please use builder.read_doc() instead.',
|
||||
RemovedInSphinx30Warning, stacklevel=2)
|
||||
self.app.builder.read_doc(docname)
|
||||
|
||||
def write_doctree(self, docname: str, doctree: nodes.document) -> None:
|
||||
warnings.warn('env.write_doctree() is deprecated. '
|
||||
'Please use builder.write_doctree() instead.',
|
||||
RemovedInSphinx30Warning, stacklevel=2)
|
||||
self.app.builder.write_doctree(docname, doctree)
|
||||
|
||||
@property
|
||||
def _nitpick_ignore(self) -> List[str]:
|
||||
warnings.warn('env._nitpick_ignore is deprecated. '
|
||||
'Please use config.nitpick_ignore instead.',
|
||||
RemovedInSphinx30Warning, stacklevel=2)
|
||||
return self.config.nitpick_ignore
|
||||
|
||||
@staticmethod
|
||||
def load(f: IO, app: "Sphinx" = None) -> "BuildEnvironment":
|
||||
warnings.warn('BuildEnvironment.load() is deprecated. '
|
||||
'Please use pickle.load() instead.',
|
||||
RemovedInSphinx30Warning, stacklevel=2)
|
||||
try:
|
||||
env = pickle.load(f)
|
||||
except Exception as exc:
|
||||
# This can happen for example when the pickle is from a
|
||||
# different version of Sphinx.
|
||||
raise OSError(exc)
|
||||
if app:
|
||||
env.app = app
|
||||
env.config.values = app.config.values
|
||||
return env
|
||||
|
||||
@classmethod
|
||||
def loads(cls, string: bytes, app: "Sphinx" = None) -> "BuildEnvironment":
|
||||
warnings.warn('BuildEnvironment.loads() is deprecated. '
|
||||
'Please use pickle.loads() instead.',
|
||||
RemovedInSphinx30Warning, stacklevel=2)
|
||||
io = BytesIO(string)
|
||||
return cls.load(io, app)
|
||||
|
||||
@classmethod
|
||||
def frompickle(cls, filename: str, app: "Sphinx") -> "BuildEnvironment":
|
||||
warnings.warn('BuildEnvironment.frompickle() is deprecated. '
|
||||
'Please use pickle.load() instead.',
|
||||
RemovedInSphinx30Warning, stacklevel=2)
|
||||
with open(filename, 'rb') as f:
|
||||
return cls.load(f, app)
|
||||
|
||||
@staticmethod
|
||||
def dump(env: "BuildEnvironment", f: IO) -> None:
|
||||
warnings.warn('BuildEnvironment.dump() is deprecated. '
|
||||
'Please use pickle.dump() instead.',
|
||||
RemovedInSphinx30Warning, stacklevel=2)
|
||||
pickle.dump(env, f, pickle.HIGHEST_PROTOCOL)
|
||||
|
||||
@classmethod
|
||||
def dumps(cls, env: "BuildEnvironment") -> bytes:
|
||||
warnings.warn('BuildEnvironment.dumps() is deprecated. '
|
||||
'Please use pickle.dumps() instead.',
|
||||
RemovedInSphinx30Warning, stacklevel=2)
|
||||
io = BytesIO()
|
||||
cls.dump(env, io)
|
||||
return io.getvalue()
|
||||
|
||||
def topickle(self, filename: str) -> None:
|
||||
warnings.warn('env.topickle() is deprecated. '
|
||||
'Please use pickle.dump() instead.',
|
||||
RemovedInSphinx30Warning, stacklevel=2)
|
||||
with open(filename, 'wb') as f:
|
||||
self.dump(self, f)
|
||||
|
||||
@property
|
||||
def versionchanges(self) -> Dict[str, List[Tuple[str, str, int, str, str, str]]]:
|
||||
warnings.warn('env.versionchanges() is deprecated. '
|
||||
'Please use ChangeSetDomain instead.',
|
||||
RemovedInSphinx30Warning, stacklevel=2)
|
||||
return self.domaindata['changeset']['changes']
|
||||
|
||||
def note_versionchange(self, type: str, version: str,
|
||||
node: addnodes.versionmodified, lineno: int) -> None:
|
||||
warnings.warn('env.note_versionchange() is deprecated. '
|
||||
'Please use ChangeSetDomain.note_changeset() instead.',
|
||||
RemovedInSphinx30Warning, stacklevel=2)
|
||||
node['type'] = type
|
||||
node['version'] = version
|
||||
node.line = lineno
|
||||
self.get_domain('changeset').note_changeset(node) # type: ignore
|
||||
|
||||
@property
|
||||
def indexentries(self) -> Dict[str, List[Tuple[str, str, str, str, str]]]:
|
||||
warnings.warn('env.indexentries() is deprecated. Please use IndexDomain instead.',
|
||||
RemovedInSphinx40Warning)
|
||||
from sphinx.domains.index import IndexDomain
|
||||
domain = cast(IndexDomain, self.get_domain('index'))
|
||||
return domain.entries
|
||||
|
||||
|
||||
from sphinx.errors import NoUri # NOQA
|
||||
|
||||
|
||||
deprecated_alias('sphinx.environment',
|
||||
{
|
||||
'NoUri': NoUri,
|
||||
},
|
||||
RemovedInSphinx30Warning)
|
||||
>>>>>>> 2.0
|
||||
|
@ -252,7 +252,7 @@ class TocTree:
|
||||
|
||||
# prune the tree to maxdepth, also set toc depth and current classes
|
||||
_toctree_add_classes(newnode, 1)
|
||||
self._toctree_prune(newnode, 1, prune and maxdepth or 0, collapse)
|
||||
self._toctree_prune(newnode, 1, maxdepth if prune else 0, collapse)
|
||||
|
||||
if isinstance(newnode[-1], nodes.Element) and len(newnode[-1]) == 0: # No titles found
|
||||
return None
|
||||
|
@ -8,22 +8,29 @@
|
||||
:license: BSD, see LICENSE for details.
|
||||
"""
|
||||
|
||||
import warnings
|
||||
from typing import Any, Dict, Set
|
||||
|
||||
from docutils import nodes
|
||||
|
||||
from sphinx import addnodes
|
||||
from sphinx.application import Sphinx
|
||||
from sphinx.deprecation import RemovedInSphinx40Warning
|
||||
from sphinx.environment import BuildEnvironment
|
||||
from sphinx.environment.collectors import EnvironmentCollector
|
||||
from sphinx.util import split_index_msg, logging
|
||||
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class IndexEntriesCollector(EnvironmentCollector):
|
||||
name = 'indices'
|
||||
|
||||
def __init__(self) -> None:
|
||||
warnings.warn('IndexEntriesCollector is deprecated.',
|
||||
RemovedInSphinx40Warning)
|
||||
|
||||
def clear_doc(self, app: Sphinx, env: BuildEnvironment, docname: str) -> None:
|
||||
env.indexentries.pop(docname, None)
|
||||
|
||||
|
@ -301,7 +301,7 @@ class Documenter:
|
||||
# support explicit module and class name separation via ::
|
||||
if explicit_modname is not None:
|
||||
modname = explicit_modname[:-2]
|
||||
parents = path and path.rstrip('.').split('.') or []
|
||||
parents = path.rstrip('.').split('.') if path else []
|
||||
else:
|
||||
modname = None
|
||||
parents = []
|
||||
@ -314,7 +314,7 @@ class Documenter:
|
||||
self.args = args
|
||||
self.retann = retann
|
||||
self.fullname = (self.modname or '') + \
|
||||
(self.objpath and '.' + '.'.join(self.objpath) or '')
|
||||
('.' + '.'.join(self.objpath) if self.objpath else '')
|
||||
return True
|
||||
|
||||
def import_object(self) -> bool:
|
||||
@ -405,7 +405,7 @@ class Documenter:
|
||||
args, retann = result
|
||||
|
||||
if args is not None:
|
||||
return args + (retann and (' -> %s' % retann) or '')
|
||||
return args + ((' -> %s' % retann) if retann else '')
|
||||
else:
|
||||
return ''
|
||||
|
||||
@ -1126,9 +1126,9 @@ class ClassDocumenter(DocstringSignatureMixin, ModuleLevelDocumenter): # type:
|
||||
sourcename = self.get_sourcename()
|
||||
self.add_line('', sourcename)
|
||||
if hasattr(self.object, '__bases__') and len(self.object.__bases__):
|
||||
bases = [b.__module__ in ('__builtin__', 'builtins') and
|
||||
':class:`%s`' % b.__name__ or
|
||||
':class:`%s.%s`' % (b.__module__, b.__name__)
|
||||
bases = [':class:`%s`' % b.__name__
|
||||
if b.__module__ in ('__builtin__', 'builtins')
|
||||
else ':class:`%s.%s`' % (b.__module__, b.__name__)
|
||||
for b in self.object.__bases__]
|
||||
self.add_line(' ' + _('Bases: %s') % ', '.join(bases),
|
||||
sourcename)
|
||||
|
@ -722,7 +722,7 @@ def process_generate_options(app: Sphinx) -> None:
|
||||
if os.path.isfile(env.doc2path(x))]
|
||||
else:
|
||||
ext = list(app.config.source_suffix)
|
||||
genfiles = [genfile + (not genfile.endswith(tuple(ext)) and ext[0] or '')
|
||||
genfiles = [genfile + (ext[0] if not genfile.endswith(tuple(ext)) else '')
|
||||
for genfile in genfiles]
|
||||
|
||||
for entry in genfiles[:]:
|
||||
|
@ -326,7 +326,7 @@ class DocTestBuilder(Builder):
|
||||
def finish(self) -> None:
|
||||
# write executive summary
|
||||
def s(v: int) -> str:
|
||||
return v != 1 and 's' or ''
|
||||
return 's' if v != 1 else ''
|
||||
repl = (self.total_tries, s(self.total_tries),
|
||||
self.total_failures, s(self.total_failures),
|
||||
self.setup_failures, s(self.setup_failures),
|
||||
@ -523,8 +523,8 @@ Doctest summary
|
||||
self.type = 'single' # as for ordinary doctests
|
||||
else:
|
||||
# testcode and output separate
|
||||
output = code[1] and code[1].code or ''
|
||||
options = code[1] and code[1].options or {}
|
||||
output = code[1].code if code[1] else ''
|
||||
options = code[1].options if code[1] else {}
|
||||
# disable <BLANKLINE> processing as it is not needed
|
||||
options[doctest.DONT_ACCEPT_BLANKLINE] = True
|
||||
# find out if we're testing an exception
|
||||
|
@ -176,7 +176,7 @@ def fetch_inventory(app: Sphinx, uri: str, inv: Any) -> Any:
|
||||
uri = path.dirname(newinv)
|
||||
with f:
|
||||
try:
|
||||
join = localuri and path.join or posixpath.join
|
||||
join = path.join if localuri else posixpath.join
|
||||
invdata = InventoryFile.load(f, uri, join)
|
||||
except ValueError as exc:
|
||||
raise ValueError('unknown or unsupported inventory version: %r' % exc)
|
||||
@ -401,7 +401,7 @@ def inspect_main(argv: List[str]) -> None:
|
||||
print(key)
|
||||
for entry, einfo in sorted(invdata[key].items()):
|
||||
print('\t%-40s %s%s' % (entry,
|
||||
einfo[3] != '-' and '%-40s: ' % einfo[3] or '',
|
||||
'%-40s: ' % einfo[3] if einfo[3] != '-' else '',
|
||||
einfo[2]))
|
||||
except ValueError as exc:
|
||||
print(exc.args[0] % exc.args[1:])
|
||||
|
@ -112,7 +112,7 @@ class GoogleDocstring:
|
||||
|
||||
if not self._config:
|
||||
from sphinx.ext.napoleon import Config
|
||||
self._config = self._app and self._app.config or Config() # type: ignore
|
||||
self._config = self._app.config if self._app else Config() # type: ignore
|
||||
|
||||
if not what:
|
||||
if inspect.isclass(obj):
|
||||
@ -385,7 +385,7 @@ class GoogleDocstring:
|
||||
def _format_field(self, _name: str, _type: str, _desc: List[str]) -> List[str]:
|
||||
_desc = self._strip_empty(_desc)
|
||||
has_desc = any(_desc)
|
||||
separator = has_desc and ' -- ' or ''
|
||||
separator = ' -- ' if has_desc else ''
|
||||
if _name:
|
||||
if _type:
|
||||
if '`' in _type:
|
||||
|
@ -188,7 +188,7 @@ class BuiltinTemplateLoader(TemplateBridge, BaseLoader):
|
||||
self.loaders = [SphinxFileSystemLoader(x) for x in loaderchain]
|
||||
|
||||
use_i18n = builder.app.translator is not None
|
||||
extensions = use_i18n and ['jinja2.ext.i18n'] or []
|
||||
extensions = ['jinja2.ext.i18n'] if use_i18n else []
|
||||
self.environment = SandboxedEnvironment(loader=self,
|
||||
extensions=extensions)
|
||||
self.environment.filters['tobool'] = _tobool
|
||||
|
@ -479,7 +479,7 @@ class Parser:
|
||||
|
||||
def parse_comments(self) -> None:
|
||||
"""Parse the code and pick up comments."""
|
||||
tree = ast.parse(self.code.encode())
|
||||
tree = ast.parse(self.code)
|
||||
picker = VariableCommentPicker(self.code.splitlines(True), self.encoding)
|
||||
picker.visit(tree)
|
||||
self.comments = picker.comments
|
||||
|
@ -296,7 +296,7 @@ class FilterSystemMessages(SphinxTransform):
|
||||
default_priority = 999
|
||||
|
||||
def apply(self, **kwargs) -> None:
|
||||
filterlevel = self.config.keep_warnings and 2 or 5
|
||||
filterlevel = 2 if self.config.keep_warnings else 5
|
||||
for node in self.document.traverse(nodes.system_message):
|
||||
if node['level'] < filterlevel:
|
||||
logger.debug('%s [filtered system message]', node.astext())
|
||||
|
@ -155,7 +155,7 @@ class ReferencesResolver(SphinxPostTransform):
|
||||
if self.config.nitpicky:
|
||||
warn = True
|
||||
if self.config.nitpick_ignore:
|
||||
dtype = domain and '%s:%s' % (domain.name, typ) or typ
|
||||
dtype = '%s:%s' % (domain.name, typ) if domain else typ
|
||||
if (dtype, target) in self.config.nitpick_ignore:
|
||||
warn = False
|
||||
# for "std" types also try without domain name
|
||||
|
@ -83,7 +83,7 @@ def dumps(obj: Any, key: bool = False) -> str:
|
||||
if obj is None:
|
||||
return 'null'
|
||||
elif obj is True or obj is False:
|
||||
return obj and 'true' or 'false'
|
||||
return 'true' if obj else 'false'
|
||||
elif isinstance(obj, (int, float)):
|
||||
return str(obj)
|
||||
elif isinstance(obj, dict):
|
||||
|
@ -89,7 +89,7 @@ class HTMLTranslator(SphinxTranslator, BaseTranslator):
|
||||
self.permalink_text = self.config.html_add_permalinks
|
||||
# support backwards-compatible setting to a bool
|
||||
if not isinstance(self.permalink_text, str):
|
||||
self.permalink_text = self.permalink_text and '¶' or ''
|
||||
self.permalink_text = '¶' if self.permalink_text else ''
|
||||
self.permalink_text = self.encode(self.permalink_text)
|
||||
self.secnumber_suffix = self.config.html_secnumber_suffix
|
||||
self.param_separator = ''
|
||||
|
@ -61,7 +61,7 @@ class HTML5Translator(SphinxTranslator, BaseTranslator):
|
||||
self.permalink_text = self.config.html_add_permalinks
|
||||
# support backwards-compatible setting to a bool
|
||||
if not isinstance(self.permalink_text, str):
|
||||
self.permalink_text = self.permalink_text and '¶' or ''
|
||||
self.permalink_text = '¶' if self.permalink_text else ''
|
||||
self.permalink_text = self.encode(self.permalink_text)
|
||||
self.secnumber_suffix = self.config.html_secnumber_suffix
|
||||
self.param_separator = ''
|
||||
|
@ -631,7 +631,7 @@ class LaTeXTranslator(SphinxTranslator):
|
||||
latex_engine=self.config.latex_engine)
|
||||
self.context = [] # type: List[Any]
|
||||
self.descstack = [] # type: List[str]
|
||||
self.table = None # type: Table
|
||||
self.tables = [] # type: List[Table]
|
||||
self.next_table_colspec = None # type: str
|
||||
self.bodystack = [] # type: List[List[str]]
|
||||
self.footnote_restricted = None # type: nodes.Element
|
||||
@ -665,7 +665,7 @@ class LaTeXTranslator(SphinxTranslator):
|
||||
def hypertarget(self, id: str, withdoc: bool = True, anchor: bool = True) -> str:
|
||||
if withdoc:
|
||||
id = self.curfilestack[-1] + ':' + id
|
||||
return (anchor and '\\phantomsection' or '') + \
|
||||
return ('\\phantomsection' if anchor else '') + \
|
||||
'\\label{%s}' % self.idescape(id)
|
||||
|
||||
def hypertarget_to(self, node: Element, anchor: bool = False) -> str:
|
||||
@ -749,6 +749,14 @@ class LaTeXTranslator(SphinxTranslator):
|
||||
|
||||
return renderer.render(template_name, variables)
|
||||
|
||||
@property
|
||||
def table(self) -> Table:
|
||||
"""Get current table."""
|
||||
if self.tables:
|
||||
return self.tables[-1]
|
||||
else:
|
||||
return None
|
||||
|
||||
def visit_document(self, node: Element) -> None:
|
||||
self.curfilestack.append(node.get('docname', ''))
|
||||
if self.first_document == 1:
|
||||
@ -1045,11 +1053,21 @@ class LaTeXTranslator(SphinxTranslator):
|
||||
raise nodes.SkipNode
|
||||
|
||||
def visit_table(self, node: Element) -> None:
|
||||
if self.table:
|
||||
if len(self.tables) == 1:
|
||||
if self.table.get_table_type() == 'longtable':
|
||||
raise UnsupportedError(
|
||||
'%s:%s: longtable does not support nesting a table.' %
|
||||
(self.curfilestack[-1], node.line or ''))
|
||||
else:
|
||||
# change type of parent table to tabular
|
||||
# see https://groups.google.com/d/msg/sphinx-users/7m3NeOBixeo/9LKP2B4WBQAJ
|
||||
self.table.has_problematic = True
|
||||
elif len(self.tables) > 2:
|
||||
raise UnsupportedError(
|
||||
'%s:%s: nested tables are not yet implemented.' %
|
||||
'%s:%s: deeply nested tables are not implemented.' %
|
||||
(self.curfilestack[-1], node.line or ''))
|
||||
self.table = Table(node)
|
||||
|
||||
self.tables.append(Table(node))
|
||||
if self.next_table_colspec:
|
||||
self.table.colspec = '{%s}\n' % self.next_table_colspec
|
||||
if 'colwidths-given' in node.get('classes', []):
|
||||
@ -1066,7 +1084,7 @@ class LaTeXTranslator(SphinxTranslator):
|
||||
self.body.append(table)
|
||||
self.body.append("\n")
|
||||
|
||||
self.table = None
|
||||
self.tables.pop()
|
||||
|
||||
def visit_colspec(self, node: Element) -> None:
|
||||
self.table.colcount += 1
|
||||
@ -1491,7 +1509,7 @@ class LaTeXTranslator(SphinxTranslator):
|
||||
elif isinstance(node[0], nodes.image) and 'width' in node[0]:
|
||||
length = self.latex_image_length(node[0]['width'])
|
||||
self.body.append('\\begin{wrapfigure}{%s}{%s}\n\\centering' %
|
||||
(node['align'] == 'right' and 'r' or 'l', length or '0pt'))
|
||||
('r' if node['align'] == 'right' else 'l', length or '0pt'))
|
||||
self.context.append('\\end{wrapfigure}\n')
|
||||
elif self.in_minipage:
|
||||
self.body.append('\n\\begin{center}')
|
||||
|
@ -240,7 +240,7 @@ class TexinfoTranslator(SphinxTranslator):
|
||||
title = self.settings.title # type: str
|
||||
if not title:
|
||||
title_node = self.document.next_node(nodes.title)
|
||||
title = (title_node and title_node.astext()) or '<untitled>'
|
||||
title = title_node.astext() if title_node else '<untitled>'
|
||||
elements['title'] = self.escape_id(title) or '<untitled>'
|
||||
# filename
|
||||
if not elements['filename']:
|
||||
@ -290,7 +290,7 @@ class TexinfoTranslator(SphinxTranslator):
|
||||
# each section is also a node
|
||||
for section in self.document.traverse(nodes.section):
|
||||
title = cast(nodes.TextElement, section.next_node(nodes.Titular))
|
||||
name = (title and title.astext()) or '<untitled>'
|
||||
name = title.astext() if title else '<untitled>'
|
||||
section['node_name'] = add_node_name(name)
|
||||
|
||||
def collect_node_menus(self) -> None:
|
||||
@ -304,7 +304,7 @@ class TexinfoTranslator(SphinxTranslator):
|
||||
node_menus[node['node_name']] = entries
|
||||
# try to find a suitable "Top" node
|
||||
title = self.document.next_node(nodes.title)
|
||||
top = (title and title.parent) or self.document
|
||||
top = title.parent if title else self.document
|
||||
if not isinstance(top, (nodes.document, nodes.section)):
|
||||
top = self.document
|
||||
if top is not self.document:
|
||||
|
0
tests/roots/test-nested-tables/conf.py
Normal file
0
tests/roots/test-nested-tables/conf.py
Normal file
16
tests/roots/test-nested-tables/index.rst
Normal file
16
tests/roots/test-nested-tables/index.rst
Normal file
@ -0,0 +1,16 @@
|
||||
nested-tables
|
||||
=============
|
||||
|
||||
.. list-table::
|
||||
:header-rows: 1
|
||||
|
||||
* - heading
|
||||
- heading
|
||||
* - content
|
||||
- .. list-table::
|
||||
:header-rows: 1
|
||||
|
||||
* - heading
|
||||
- heading
|
||||
* - content
|
||||
- content
|
@ -28,9 +28,9 @@ ENV_WARNINGS = """\
|
||||
WARNING: Explicit markup ends without a blank line; unexpected unindent.
|
||||
%(root)s/index.rst:\\d+: WARNING: Encoding 'utf-8-sig' used for reading included \
|
||||
file '%(root)s/wrongenc.inc' seems to be wrong, try giving an :encoding: option
|
||||
%(root)s/index.rst:\\d+: WARNING: invalid single index entry ''
|
||||
%(root)s/index.rst:\\d+: WARNING: image file not readable: foo.png
|
||||
%(root)s/index.rst:\\d+: WARNING: download file not readable: %(root)s/nonexisting.png
|
||||
%(root)s/index.rst:\\d+: WARNING: invalid single index entry ''
|
||||
%(root)s/undecodable.rst:\\d+: WARNING: undecodable source characters, replacing \
|
||||
with "\\?": b?'here: >>>(\\\\|/)xbb<<<((\\\\|/)r)?'
|
||||
"""
|
||||
|
@ -1470,3 +1470,9 @@ def test_latex_elements_extrapackages(app, status, warning):
|
||||
app.builder.build_all()
|
||||
result = (app.outdir / 'test.tex').text()
|
||||
assert r'\usepackage{foo}' in result
|
||||
|
||||
|
||||
@pytest.mark.sphinx('latex', testroot='nested-tables')
|
||||
def test_latex_nested_tables(app, status, warning):
|
||||
app.builder.build_all()
|
||||
assert '' == warning.getvalue()
|
||||
|
Loading…
Reference in New Issue
Block a user