Merge remote-tracking branch 'upstream/master'

Conflicts:
	tests/test_build_html.py
This commit is contained in:
Jellby 2017-04-15 15:54:06 +02:00
commit 1cfed262d2
331 changed files with 3516 additions and 1645 deletions

View File

@ -42,6 +42,7 @@ addons:
- texlive-luatex
- texlive-xetex
- lmodern
- latex-xcolor
install:
- pip install -U pip setuptools
- pip install docutils==$DOCUTILS

77
CHANGES
View File

@ -1,6 +1,15 @@
Release 1.6 (in development)
============================
Dependencies
------------
* (updated) latex output is tested with Ubuntu trusty's texlive packages (Feb.
2014) and earlier tex installations may not be fully compliant, particularly
regarding Unicode engines xelatex and lualatex
* (added) latexmk is requirement for ``make latexpdf`` on Unix-like platforms
(refs: #3082)
Incompatible changes
--------------------
@ -18,14 +27,21 @@ Incompatible changes
relative (refs #3412)
* ``literalinclude`` directive does not allow the combination of ``:diff:``
option and other options (refs: #3416)
* Default config for LuaLaTeX engine uses ``fontspec`` hence needs TeXLive 2013
or more recent TeX installation for compatibility. This also means that the
fonts used by default have changed to the defaults as chosen by ``fontspec``.
(refs #3070, #3466)
* LuaLaTeX engine uses ``fontspec`` like XeLaTeX. It is advised ``latex_engine
= 'lualatex'`` be used only on up-to-date TeX installs (refs #3070, #3466)
* :confval:`latex_keep_old_macro_names` default value has been changed from
``True`` to ``False``. This means that some LaTeX macros for styling are
by default defined only with ``\sphinx..`` prefixed names. (refs: #3429)
* Footer "Continued on next page" of LaTeX longtable's now not framed (refs: #3497)
* #3529: The arguments of ``BuildEnvironment.__init__`` is changed
* #3082: Use latexmk for pdf (and dvi) targets (Unix-like platforms only)
* latex made available (non documented) colour macros from a file distributed
with pdftex engine for Plain TeX. This is removed in order to provide better
support for multiple TeX engines. Only interface from ``color`` or
``xcolor`` packages should be used by extensions of Sphinx latex writer.
(refs #3550)
* ``Builder.env`` is not filled at instantiation
* #3594: LaTeX: single raw directive has been considered as block level element
Features removed
----------------
@ -80,6 +96,13 @@ Features added
as do normal cells (refs: #3435)
* HTML buildre uses experimental HTML5 writer if ``html_experimental_html5_builder`` is True
and docutils 0.13 and newer is installed.
* LaTeX macros to customize space before and after tables in PDF output (refs #3504)
* #3348: Show decorators in literalinclude and viewcode directives
* #3108: Show warning if :start-at: and other literalinclude options does not
match to the text
* #3609: Allow to suppress "duplicate citation" warnings using
``suppress_warnings``
* #2803: Discovery of builders by entry point
Bugs fixed
----------
@ -87,6 +110,13 @@ Bugs fixed
* ``literalinclude`` directive expands tabs after dedent-ing (refs: #3416)
* #1574: Paragraphs in table cell doesn't work in Latex output
* #3288: Table with merged headers not wrapping text
* #3491: Inconsistent vertical space around table and longtable in PDF
* #3506: Depart functions for all admonitions in HTML writer now properly pass ``node`` to ``depart_admonition``.
* #2693: Sphinx latex style file wrongly inhibits colours for section headings
for latex+dvi(ps,pdf,pdfmx)
* C++, properly look up ``any`` references.
* #3624: sphinx.ext.intersphinx couldn't load inventories compressed with gzip
* #3551: PDF information dictionary is lacking author and title data
Deprecated
----------
@ -116,8 +146,9 @@ Deprecated
* #3429: deprecate config setting ``latex_keep_old_macro_names``. It will be
removed at 1.7, and already its default value has changed from ``True`` to
``False``.
* #3221: epub2 builder is deprecated
Release 1.5.4 (in development)
Release 1.5.6 (in development)
==============================
Incompatible changes
@ -129,6 +160,27 @@ Deprecated
Features added
--------------
Bugs fixed
----------
Testing
--------
Release 1.5.5 (released Apr 03, 2017)
=====================================
Bugs fixed
----------
* #3597: python domain raises UnboundLocalError if invalid name given
* #3599: Move to new Mathjax CDN
Release 1.5.4 (released Apr 02, 2017)
=====================================
Features added
--------------
* #3470: Make genindex support all kinds of letters, not only Latin ones
Bugs fixed
@ -144,9 +196,17 @@ Bugs fixed
are same without creation date. Thanks to Yoshiki Shibukawa.
* #3487: intersphinx: failed to refer options
* #3496: latex longtable's last column may be much wider than its contents
Testing
--------
* #3507: wrong quotes in latex output for productionlist directive
* #3533: Moving from Sphinx 1.3.1 to 1.5.3 breaks LaTeX compilation of links
rendered as code
* #2665, #2607: Link names in C++ docfields, and make it possible for other domains.
* #3542: C++, fix parsing error of non-type template argument with template.
* #3065, #3520: python domain fails to recognize nested class
* #3575: Problems with pdflatex in a Turkish document built with sphinx has
reappeared (refs #2997, #2397)
* #3577: Fix intersphinx debug tool
* A LaTeX command such as ``\\large`` inserted in the title items of
:confval:`latex_documents` causes failed PDF build (refs #3551, #3567)
Release 1.5.3 (released Feb 26, 2017)
=====================================
@ -181,6 +241,7 @@ Bugs fixed
* #3450: &nbsp is appeared in EPUB docs
* #3418: Search button is misaligned in nature and pyramid theme
* #3421: Could not translate a caption of tables
* #3552: linkcheck raises UnboundLocalError
Release 1.5.2 (released Jan 22, 2017)
=====================================

View File

@ -1,3 +1,5 @@
.. highlight:: console
Sphinx Developer's Guide
========================
@ -127,6 +129,11 @@ These are the basic steps needed to start developing on Sphinx.
cd doc
make clean html latexpdf
* Run code style checks and type checks (type checks require mypy)::
make style-check
make type-check
* Run the unit tests under different Python environments using
:program:`tox`::
@ -269,14 +276,12 @@ Debugging Tips
* Set the debugging options in the `Docutils configuration file
<http://docutils.sourceforge.net/docs/user/config.html>`_.
* JavaScript stemming algorithms in `sphinx/search/*.py` (except `en.py`) are
* JavaScript stemming algorithms in ``sphinx/search/*.py`` (except ``en.py``) are
generated by this
`modified snowballcode generator <https://github.com/shibukawa/snowball>`_.
Generated `JSX <http://jsx.github.io/>`_ files are
in `this repository <https://github.com/shibukawa/snowball-stemmer.jsx>`_.
You can get the resulting JavaScript files using the following command:
.. code-block:: bash
You can get the resulting JavaScript files using the following command::
$ npm install
$ node_modules/.bin/grunt build # -> dest/*.global.js

View File

@ -1,7 +1,7 @@
License for Sphinx
==================
Copyright (c) 2007-2016 by the Sphinx team (see AUTHORS file).
Copyright (c) 2007-2017 by the Sphinx team (see AUTHORS file).
All rights reserved.
Redistribution and use in source and binary forms, with or without

View File

@ -4,7 +4,7 @@
Sphinx layout template for the sphinxdoc theme.
:copyright: Copyright 2007-2016 by the Sphinx team, see AUTHORS.
:copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
#}
{%- extends "basic/layout.html" %}

View File

@ -4,7 +4,7 @@
*
* Sphinx stylesheet -- sphinx13 theme.
*
* :copyright: Copyright 2007-2016 by the Sphinx team, see AUTHORS.
* :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
* :license: BSD, see LICENSE for details.
*
*/

View File

@ -178,28 +178,29 @@ The builder's "name" must be given to the **-b** command-line option of
.. note::
The produced LaTeX file uses several LaTeX packages that may not be
present in a "minimal" TeX distribution installation. For TeXLive,
the following packages need to be installed:
present in a "minimal" TeX distribution installation. For example, on
Ubuntu, the following packages need to be installed for successful PDF
builds:
* texlive-latex-recommended
* texlive-fonts-recommended
* texlive-latex-extra
* latexmk (for ``make latexpdf``)
You may also need latex-xcolor, but Sphinx does not require it (and
recent distributions have ``xcolor.sty`` included in latex-recommended).
Unicode engines will need their respective packages texlive-luatex or
Sphinx will use ``xcolor.sty`` if present: recent Ubuntu distributions
have ``xcolor.sty`` included in latex-recommended, earlier ones have it
in latex-xcolor. Unicode engines will need texlive-luatex or
texlive-xetex.
The testing of Sphinx LaTeX is done on Ubuntu trusty with the above
texlive packages. They are from a `TeXLive 2013 snapshot dated
20140215`__.
mentioned packages, which are from a TeXLive 2013 snapshot dated
February 2014.
__ http://packages.ubuntu.com/trusty/texlive-latex-recommended
.. versionchanged::
1.6 Formerly, testing was done for some years on Ubuntu precise
.. versionchanged:: 1.6
Formerly, testing had been done for some years on Ubuntu precise
(based on TeXLive 2009).
.. versionchanged:: 1.6
Use of ``latexmk`` on GNU/Linux or Mac OS X.
.. autoattribute:: name

View File

@ -15,7 +15,7 @@ templates_path = ['_templates']
exclude_patterns = ['_build']
project = 'Sphinx'
copyright = '2007-2016, Georg Brandl and the Sphinx team'
copyright = '2007-2017, Georg Brandl and the Sphinx team'
version = sphinx.__released__
release = version
show_authors = True

View File

@ -76,9 +76,10 @@ directive name.
.. rubric:: Default Domain
To avoid having to writing the domain name all the time when you e.g. only
describe Python objects, a default domain can be selected with either the config
value :confval:`primary_domain` or this directive:
For documentation describing objects from solely one domain, authors will not
have to state again its name at each directive, role, etc... after
having specified a default. This can be done either via the config
value :confval:`primary_domain` or via this directive:
.. rst:directive:: .. default-domain:: name
@ -1103,6 +1104,22 @@ The JavaScript Domain
The JavaScript domain (name **js**) provides the following directives:
.. rst:directive:: .. js:module:: name
This directive sets the module name for object declarations that follow
after. The module name is used in the global module index and in cross
references. This directive does not create an object heading like
:rst:dir:`py:class` would, for example.
By default, this directive will create a linkable entity and will cause an
entry in the global module index, unless the ``noindex`` option is specified.
If this option is specified, the directive will only update the current
module name.
To clear the current module, set the module name to ``null`` or ``None``
.. versionadded:: 1.6
.. rst:directive:: .. js:function:: name(signature)
Describes a JavaScript function or method. If you want to describe
@ -1135,6 +1152,13 @@ The JavaScript domain (name **js**) provides the following directives:
:throws SomeError: For whatever reason in that case.
:returns: Something.
.. rst:directive:: .. js:method:: name(signature)
This directive is an alias for :rst:dir:`js:function`, however it describes a
function that is implemented as a method on a class object.
.. versionadded:: 1.6
.. rst:directive:: .. js:class:: name
Describes a constructor that creates an object. This is basically like
@ -1164,7 +1188,9 @@ The JavaScript domain (name **js**) provides the following directives:
These roles are provided to refer to the described objects:
.. rst:role:: js:func
.. rst:role:: js:mod
js:func
js:meth
js:class
js:data
js:attr

View File

@ -158,7 +158,7 @@ is not supported.)
may indicate that it's a better idea to write custom narrative documentation
instead.
Autosummary uses the following template files:
Autosummary uses the following Jinja template files:
- :file:`autosummary/base.rst` -- fallback template
- :file:`autosummary/module.rst` -- template for modules
@ -194,7 +194,8 @@ The following variables available in the templates:
.. data:: underline
A string containing ``len(full_name) * '='``.
A string containing ``len(full_name) * '='``. Use the ``underline`` filter
instead.
.. data:: members
@ -227,7 +228,25 @@ The following variables available in the templates:
List containing names of "public" attributes in the class. Only available
for classes.
Additionally, the following filters are available
.. function:: escape(s)
Escape any special characters in the text to be used in formatting RST
contexts. For instance, this prevents asterisks making things bolt. This
replaces the builtin Jinja `escape filter`_ that does html-escaping.
.. function:: underline(s, line='=')
Add a title underline to a piece of text.
For instance, ``{{ fullname | escape | underline }}`` should be used to produce
the title of a page.
.. note::
You can use the :rst:dir:`autosummary` directive in the stub pages.
Stub pages are generated also based on these directives.
.. _`escape filter`: http://jinja.pocoo.org/docs/2.9/templates/#escape

View File

@ -288,6 +288,11 @@ package.
Add the standard docutils :class:`Transform` subclass *transform* to the list
of transforms that are applied after Sphinx parses a reST document.
.. method:: Sphinx.add_post_transform(transform)
Add the standard docutils :class:`Transform` subclass *transform* to the list
of transforms that are applied before Sphinx writes a document.
.. method:: Sphinx.add_javascript(filename)
Add *filename* to the list of JavaScript files that the default HTML template

View File

@ -11,6 +11,12 @@ Builder API
This is the base class for all builders.
These attributes should be set on builder classes:
.. autoattribute:: name
.. autoattribute:: format
.. autoattribute:: supported_image_types
These methods are predefined and will be called from the application:
.. automethod:: get_relative_uri

View File

@ -22,6 +22,36 @@ The configuration file itself can be treated as an extension if it contains a
``setup()`` function. All other extensions to load must be listed in the
:confval:`extensions` configuration value.
Discovery of builders by entry point
------------------------------------
.. versionadded:: 1.6
:term:`Builder` extensions can be discovered by means of `entry points`_ so
that they do not have to be listed in the :confval:`extensions` configuration
value.
Builder extensions should define an entry point in the ``sphinx.builders``
group. The name of the entry point needs to match your builder's
:attr:`~.Builder.name` attribute, which is the name passed to the
:option:`sphinx-build -b` option. The entry point value should equal the
dotted name of the extension module. Here is an example of how an entry point
for 'mybuilder' can be defined in the extension's ``setup.py``::
setup(
# ...
entry_points={
'sphinx.builders': [
'mybuilder = my.extension.module',
],
}
)
Note that it is still necessary to register the builder using
:meth:`~.Sphinx.add_builder` in the extension's :func:`setup` function.
.. _entry points: https://setuptools.readthedocs.io/en/latest/setuptools.html#dynamic-discovery-of-services-and-plugins
Extension metadata
------------------

View File

@ -98,8 +98,9 @@ in which a Sphinx project is built: this works in several phases.
Now that the metadata and cross-reference data of all existing documents is
known, all temporary nodes are replaced by nodes that can be converted into
output. For example, links are created for object references that exist, and
simple literal nodes are created for those that don't.
output using components called tranform. For example, links are created for
object references that exist, and simple literal nodes are created for those
that don't.
**Phase 4: Writing**

View File

@ -201,6 +201,11 @@ Including content based on tags
.. versionchanged:: 1.2
Added the name of the builder and the prefixes.
.. warning::
This directive is designed to control only content of document. It could
not control sections, labels and so on.
Tables
------

View File

@ -3,7 +3,7 @@ tag_build = .dev
tag_date = true
[aliases]
release = egg_info -RDb ''
release = egg_info -Db ''
upload = upload --sign --identity=36580288
[extract_messages]

View File

@ -52,6 +52,7 @@ requires = [
'imagesize',
'requests>=2.0.0',
'typing',
'setuptools',
]
extras_require = {
# Environment Marker works for wheel 0.24 or later

View File

@ -4,7 +4,7 @@
Sphinx - Python documentation toolchain
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
:copyright: Copyright 2007-2016 by the Sphinx team, see AUTHORS.
:copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""

View File

@ -4,7 +4,7 @@
Sphinx - Python documentation toolchain
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
:copyright: Copyright 2007-2016 by the Sphinx team, see AUTHORS.
:copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""

View File

@ -4,7 +4,7 @@
Sphinx - Python documentation toolchain
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
:copyright: Copyright 2007-2016 by the Sphinx team, see AUTHORS.
:copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""

View File

@ -4,7 +4,7 @@
Sphinx - Python documentation toolchain
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
:copyright: Copyright 2007-2016 by the Sphinx team, see AUTHORS.
:copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""

View File

@ -5,7 +5,7 @@
The Sphinx documentation toolchain.
:copyright: Copyright 2007-2016 by the Sphinx team, see AUTHORS.
:copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""

View File

@ -5,7 +5,7 @@
The Sphinx documentation toolchain.
:copyright: Copyright 2007-2016 by the Sphinx team, see AUTHORS.
:copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
import sys

View File

@ -5,7 +5,7 @@
Additional docutils nodes.
:copyright: Copyright 2007-2016 by the Sphinx team, see AUTHORS.
:copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""

View File

@ -11,7 +11,7 @@
Copyright 2008 Société des arts technologiques (SAT),
http://www.sat.qc.ca/
:copyright: Copyright 2007-2016 by the Sphinx team, see AUTHORS.
:copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
from __future__ import print_function
@ -23,9 +23,10 @@ from os import path
from six import binary_type
from fnmatch import fnmatch
from sphinx.util.osutil import FileAvoidWrite, walk
from sphinx import __display_version__
from sphinx.quickstart import EXTENSIONS
from sphinx.util import rst
from sphinx.util.osutil import FileAvoidWrite, walk
if False:
# For type annotation
@ -74,9 +75,11 @@ def write_file(name, text, opts):
f.write(text)
def format_heading(level, text):
# type: (int, unicode) -> unicode
def format_heading(level, text, escape=True):
# type: (int, unicode, bool) -> unicode
"""Create a heading of <level> [1, 2 or 3 supported]."""
if escape:
text = rst.escape(text)
underlining = ['=', '-', '~', ][level - 1] * len(text)
return '%s\n%s\n\n' % (text, underlining)
@ -161,7 +164,7 @@ def create_package_file(root, master_package, subroot, py_files, opts, subs, is_
def create_modules_toc_file(modules, opts, name='modules'):
# type: (List[unicode], Any, unicode) -> None
"""Create the module's index."""
text = format_heading(1, '%s' % opts.header)
text = format_heading(1, '%s' % opts.header, escape=False)
text += '.. toctree::\n'
text += ' :maxdepth: %s\n\n' % opts.maxdepth

View File

@ -7,7 +7,7 @@
Gracefully adapted from the TextPress system by Armin.
:copyright: Copyright 2007-2016 by the Sphinx team, see AUTHORS.
:copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
from __future__ import print_function
@ -17,16 +17,16 @@ import sys
import types
import warnings
import posixpath
import traceback
from os import path
from collections import deque
from six import iteritems, itervalues
from six import iteritems
from six.moves import cStringIO
from docutils import nodes
from docutils.parsers.rst import convert_directive_function, \
directives, roles
from pkg_resources import iter_entry_points
import sphinx
from sphinx import package_dir, locale
@ -37,7 +37,10 @@ from sphinx.domains import ObjType
from sphinx.domains.std import GenericObject, Target, StandardDomain
from sphinx.deprecation import RemovedInSphinx17Warning, RemovedInSphinx20Warning
from sphinx.environment import BuildEnvironment
from sphinx.events import EventManager
from sphinx.extension import load_extension, verify_required_extensions
from sphinx.io import SphinxStandaloneReader
from sphinx.locale import _
from sphinx.roles import XRefRole
from sphinx.util import pycompat # noqa: F401
from sphinx.util import import_object
@ -57,24 +60,8 @@ if False:
from sphinx.builders import Builder # NOQA
from sphinx.domains import Domain, Index # NOQA
from sphinx.environment.collectors import EnvironmentCollector # NOQA
from sphinx.extension import Extension # NOQA
# List of all known core events. Maps name to arguments description.
events = {
'builder-inited': '',
'env-get-outdated': 'env, added, changed, removed',
'env-get-updated': 'env',
'env-purge-doc': 'env, docname',
'env-before-read-docs': 'env, docnames',
'source-read': 'docname, source text',
'doctree-read': 'the doctree before being pickled',
'env-merge-info': 'env, read docnames, other env instance',
'missing-reference': 'env, node, contnode',
'doctree-resolved': 'doctree, docname',
'env-updated': 'env',
'html-collect-pages': 'builder',
'html-page-context': 'pagename, context, doctree or None',
'build-finished': 'exception',
} # type: Dict[unicode, unicode]
builtin_extensions = (
'sphinx.builders.applehelp',
'sphinx.builders.changes',
@ -104,6 +91,7 @@ builtin_extensions = (
'sphinx.directives.other',
'sphinx.directives.patches',
'sphinx.roles',
'sphinx.transforms.post_transforms',
# collectors should be loaded by specific order
'sphinx.environment.collectors.dependencies',
'sphinx.environment.collectors.asset',
@ -111,15 +99,14 @@ builtin_extensions = (
'sphinx.environment.collectors.title',
'sphinx.environment.collectors.toctree',
'sphinx.environment.collectors.indexentries',
# Strictly, alabaster theme is not a builtin extension,
# but it is loaded automatically to use it as default theme.
'alabaster',
) # type: Tuple[unicode, ...]
CONFIG_FILENAME = 'conf.py'
ENV_PICKLE_FILENAME = 'environment.pickle'
# list of deprecated extensions. Keys are extension name.
# Values are Sphinx version that merge the extension.
EXTENSION_BLACKLIST = {"sphinxjp.themecore": "1.2"} # type: Dict[unicode, unicode]
logger = logging.getLogger(__name__)
@ -131,18 +118,15 @@ class Sphinx(object):
parallel=0):
# type: (unicode, unicode, unicode, unicode, unicode, Dict, IO, IO, bool, bool, List[unicode], int, int) -> None # NOQA
self.verbosity = verbosity
self.next_listener_id = 0
self._extensions = {} # type: Dict[unicode, Any]
self._extension_metadata = {} # type: Dict[unicode, Dict[unicode, Any]]
self.extensions = {} # type: Dict[unicode, Extension]
self._additional_source_parsers = {} # type: Dict[unicode, Parser]
self._listeners = {} # type: Dict[unicode, Dict[int, Callable]]
self._setting_up_extension = ['?'] # type: List[unicode]
self.domains = {} # type: Dict[unicode, Type[Domain]]
self.buildername = buildername
self.builderclasses = {} # type: Dict[unicode, Type[Builder]]
self.builder = None # type: Builder
self.env = None # type: BuildEnvironment
self.enumerable_nodes = {} # type: Dict[nodes.Node, Tuple[unicode, Callable]] # NOQA
self.post_transforms = [] # type: List[Transform]
self.srcdir = srcdir
self.confdir = confdir
@ -166,10 +150,11 @@ class Sphinx(object):
self.warningiserror = warningiserror
logging.setup(self, self._status, self._warning)
self._events = events.copy()
self.events = EventManager()
self._translators = {} # type: Dict[unicode, nodes.GenericNodeVisitor]
# keep last few messages for traceback
# This will be filled by sphinx.util.logging.LastMessagesWriter
self.messagelog = deque(maxlen=10) # type: deque
# say hello to the world
@ -189,14 +174,18 @@ class Sphinx(object):
self.config.check_unicode()
# defer checking types until i18n has been initialized
# initialize some limited config variables before loading extensions
# initialize some limited config variables before initialize i18n and loading
# extensions
self.config.pre_init_values()
# set up translation infrastructure
self._init_i18n()
# check the Sphinx version if requested
if self.config.needs_sphinx and self.config.needs_sphinx > sphinx.__display_version__:
raise VersionRequirementError(
'This project needs at least Sphinx v%s and therefore cannot '
'be built with this version.' % self.config.needs_sphinx)
_('This project needs at least Sphinx v%s and therefore cannot '
'be built with this version.') % self.config.needs_sphinx)
# set confdir to srcdir if -C given (!= no confdir); a few pieces
# of code expect a confdir to be set
@ -207,12 +196,6 @@ class Sphinx(object):
for extension in builtin_extensions:
self.setup_extension(extension)
# extension loading support for alabaster theme
# self.config.html_theme is not set from conf.py at here
# for now, sphinx always load a 'alabaster' extension.
if 'alabaster' not in self.config.extensions:
self.config.extensions.append('alabaster')
# load all user-given extension modules
for extension in self.config.extensions:
self.setup_extension(extension)
@ -224,35 +207,24 @@ class Sphinx(object):
self.config.setup(self)
else:
raise ConfigError(
"'setup' that is specified in the conf.py has not been " +
"callable. Please provide a callable `setup` function " +
"in order to behave as a sphinx extension conf.py itself."
_("'setup' that is specified in the conf.py has not been "
"callable. Please provide a callable `setup` function "
"in order to behave as a sphinx extension conf.py itself.")
)
# now that we know all config values, collect them from conf.py
self.config.init_values()
# check extension versions if requested
if self.config.needs_extensions:
for extname, needs_ver in self.config.needs_extensions.items():
if extname not in self._extensions:
logger.warning('needs_extensions config value specifies a '
'version requirement for extension %s, but it is '
'not loaded', extname)
continue
has_ver = self._extension_metadata[extname]['version']
if has_ver == 'unknown version' or needs_ver > has_ver:
raise VersionRequirementError(
'This project needs the extension %s at least in '
'version %s and therefore cannot be built with the '
'loaded version (%s).' % (extname, needs_ver, has_ver))
verify_required_extensions(self, self.config.needs_extensions)
# check primary_domain if requested
if self.config.primary_domain and self.config.primary_domain not in self.domains:
logger.warning('primary_domain %r not found, ignored.', self.config.primary_domain)
logger.warning(_('primary_domain %r not found, ignored.'),
self.config.primary_domain)
# set up translation infrastructure
self._init_i18n()
# create the builder
self.builder = self.create_builder(buildername)
# check all configuration values for permissible types
self.config.check_types()
# set up source_parsers
@ -260,7 +232,7 @@ class Sphinx(object):
# set up the build environment
self._init_env(freshenv)
# set up the builder
self._init_builder(self.buildername)
self._init_builder()
# set up the enumerable nodes
self._init_enumerable_nodes()
@ -286,7 +258,7 @@ class Sphinx(object):
if self.config.language is not None:
if has_translation or self.config.language == 'en':
# "en" never needs to be translated
logger.info('done')
logger.info(_('done'))
else:
logger.info('not available for built-in messages')
@ -301,37 +273,47 @@ class Sphinx(object):
def _init_env(self, freshenv):
# type: (bool) -> None
if freshenv:
self.env = BuildEnvironment(self.srcdir, self.doctreedir, self.config)
self.env.find_files(self.config, self.buildername)
self.env = BuildEnvironment(self)
self.env.find_files(self.config, self.builder)
for domain in self.domains.keys():
self.env.domains[domain] = self.domains[domain](self.env)
else:
try:
logger.info(bold('loading pickled environment... '), nonl=True)
self.env = BuildEnvironment.frompickle(
self.srcdir, self.config, path.join(self.doctreedir, ENV_PICKLE_FILENAME))
logger.info(bold(_('loading pickled environment... ')), nonl=True)
filename = path.join(self.doctreedir, ENV_PICKLE_FILENAME)
self.env = BuildEnvironment.frompickle(filename, self)
self.env.domains = {}
for domain in self.domains.keys():
# this can raise if the data version doesn't fit
self.env.domains[domain] = self.domains[domain](self.env)
logger.info('done')
logger.info(_('done'))
except Exception as err:
if isinstance(err, IOError) and err.errno == ENOENT:
logger.info('not yet created')
logger.info(_('not yet created'))
else:
logger.info('failed: %s', err)
logger.info(_('failed: %s'), err)
self._init_env(freshenv=True)
def _init_builder(self, buildername):
# type: (unicode) -> None
def create_builder(self, buildername):
# type: (unicode) -> Builder
if buildername is None:
print('No builder selected, using default: html', file=self._status)
logger.info(_('No builder selected, using default: html'))
buildername = 'html'
if buildername not in self.builderclasses:
raise SphinxError('Builder name %s not registered' % buildername)
entry_points = iter_entry_points('sphinx.builders', buildername)
try:
entry_point = next(entry_points)
except StopIteration:
raise SphinxError('Builder name %s not registered or available'
' through entry point' % buildername)
load_extension(self, entry_point.module_name)
builderclass = self.builderclasses[buildername]
self.builder = builderclass(self)
return builderclass(self)
def _init_builder(self):
# type: () -> None
self.builder.set_environment(self.env)
self.builder.init()
self.emit('builder-inited')
def _init_enumerable_nodes(self):
@ -339,6 +321,13 @@ class Sphinx(object):
for node, settings in iteritems(self.enumerable_nodes):
self.env.get_domain('std').enumerable_nodes[node] = settings # type: ignore
@property
def buildername(self):
# type: () -> unicode
warnings.warn('app.buildername is deprecated. Please use app.builder.name instead',
RemovedInSphinx17Warning)
return self.builder.name
# ---- main "build" method -------------------------------------------------
def build(self, force_all=False, filenames=None):
@ -355,13 +344,13 @@ class Sphinx(object):
self.builder.build_update()
status = (self.statuscode == 0 and
'succeeded' or 'finished with problems')
_('succeeded') or _('finished with problems'))
if self._warncount:
logger.info(bold('build %s, %s warning%s.' %
logger.info(bold(_('build %s, %s warning%s.') %
(status, self._warncount,
self._warncount != 1 and 's' or '')))
else:
logger.info(bold('build %s.' % status))
logger.info(bold(_('build %s.') % status))
except Exception as err:
# delete the saved env to force a fresh build next time
envfile = path.join(self.doctreedir, ENV_PICKLE_FILENAME)
@ -465,53 +454,11 @@ class Sphinx(object):
# ---- general extensibility interface -------------------------------------
def setup_extension(self, extension):
def setup_extension(self, extname):
# type: (unicode) -> None
"""Import and setup a Sphinx extension module. No-op if called twice."""
logger.debug('[app] setting up extension: %r', extension)
if extension in self._extensions:
return
if extension in EXTENSION_BLACKLIST:
logger.warning('the extension %r was already merged with Sphinx since version %s; '
'this extension is ignored.',
extension, EXTENSION_BLACKLIST[extension])
return
self._setting_up_extension.append(extension)
try:
mod = __import__(extension, None, None, ['setup'])
except ImportError as err:
logger.verbose('Original exception:\n' + traceback.format_exc())
raise ExtensionError('Could not import extension %s' % extension,
err)
if not hasattr(mod, 'setup'):
logger.warning('extension %r has no setup() function; is it really '
'a Sphinx extension module?', extension)
ext_meta = None
else:
try:
ext_meta = mod.setup(self)
except VersionRequirementError as err:
# add the extension name to the version required
raise VersionRequirementError(
'The %s extension used by this project needs at least '
'Sphinx v%s; it therefore cannot be built with this '
'version.' % (extension, err))
if ext_meta is None:
ext_meta = {}
# special-case for compatibility
if extension == 'rst2pdf.pdfbuilder':
ext_meta = {'parallel_read_safe': True}
try:
if not ext_meta.get('version'):
ext_meta['version'] = 'unknown version'
except Exception:
logger.warning('extension %r returned an unsupported object from '
'its setup() function; it should return None or a '
'metadata dictionary', extension)
ext_meta = {'version': 'unknown version'}
self._extensions[extension] = mod
self._extension_metadata[extension] = ext_meta
self._setting_up_extension.pop()
logger.debug('[app] setting up extension: %r', extname)
load_extension(self, extname)
def require_sphinx(self, version):
# type: (unicode) -> None
@ -525,30 +472,16 @@ class Sphinx(object):
return import_object(objname, source=None)
# event interface
def _validate_event(self, event):
# type: (unicode) -> None
if event not in self._events:
raise ExtensionError('Unknown event name: %s' % event)
def connect(self, event, callback):
# type: (unicode, Callable) -> int
self._validate_event(event)
listener_id = self.next_listener_id
if event not in self._listeners:
self._listeners[event] = {listener_id: callback}
else:
self._listeners[event][listener_id] = callback
self.next_listener_id += 1
logger.debug('[app] connecting event %r: %r [id=%s]',
event, callback, listener_id)
listener_id = self.events.connect(event, callback)
logger.debug('[app] connecting event %r: %r', event, callback, listener_id)
return listener_id
def disconnect(self, listener_id):
# type: (int) -> None
logger.debug('[app] disconnecting event: [id=%s]', listener_id)
for event in itervalues(self._listeners):
event.pop(listener_id, None)
self.events.disconnect(listener_id)
def emit(self, event, *args):
# type: (unicode, Any) -> List
@ -558,18 +491,11 @@ class Sphinx(object):
# not every object likes to be repr()'d (think
# random stuff coming via autodoc)
pass
results = []
if event in self._listeners:
for _, callback in iteritems(self._listeners[event]):
results.append(callback(self, *args))
return results
return self.events.emit(event, self, *args)
def emit_firstresult(self, event, *args):
# type: (unicode, Any) -> Any
for result in self.emit(event, *args):
if result is not None:
return result
return None
return self.events.emit_firstresult(event, self, *args)
# registering addon parts
@ -577,12 +503,11 @@ class Sphinx(object):
# type: (Type[Builder]) -> None
logger.debug('[app] adding builder: %r', builder)
if not hasattr(builder, 'name'):
raise ExtensionError('Builder class %s has no "name" attribute'
raise ExtensionError(_('Builder class %s has no "name" attribute')
% builder)
if builder.name in self.builderclasses:
raise ExtensionError(
'Builder %r already exists (in module %s)' % (
builder.name, self.builderclasses[builder.name].__module__))
raise ExtensionError(_('Builder %r already exists (in module %s)') %
(builder.name, self.builderclasses[builder.name].__module__))
self.builderclasses[builder.name] = builder
def add_config_value(self, name, default, rebuild, types=()):
@ -590,7 +515,7 @@ class Sphinx(object):
logger.debug('[app] adding config value: %r',
(name, default, rebuild) + ((types,) if types else ())) # type: ignore
if name in self.config:
raise ExtensionError('Config value %r already present' % name)
raise ExtensionError(_('Config value %r already present') % name)
if rebuild in (False, True):
rebuild = rebuild and 'env' or ''
self.config.add(name, default, rebuild, types)
@ -598,13 +523,11 @@ class Sphinx(object):
def add_event(self, name):
# type: (unicode) -> None
logger.debug('[app] adding event: %r', name)
if name in self._events:
raise ExtensionError('Event %r already present' % name)
self._events[name] = ''
self.events.add(name)
def set_translator(self, name, translator_class):
# type: (unicode, Any) -> None
logger.info(bold('A Translator for the %s builder is changed.' % name))
logger.info(bold(_('A Translator for the %s builder is changed.') % name))
self._translators[name] = translator_class
def add_node(self, node, **kwds):
@ -612,8 +535,8 @@ class Sphinx(object):
logger.debug('[app] adding node: %r', (node, kwds))
if not kwds.pop('override', False) and \
hasattr(nodes.GenericNodeVisitor, 'visit_' + node.__name__):
logger.warning('while setting up extension %s: node class %r is '
'already registered, its visitors will be overridden',
logger.warning(_('while setting up extension %s: node class %r is '
'already registered, its visitors will be overridden'),
self._setting_up_extension, node.__name__,
type='app', subtype='add_node')
nodes._add_node_class_names([node.__name__])
@ -621,8 +544,8 @@ class Sphinx(object):
try:
visit, depart = val
except ValueError:
raise ExtensionError('Value for key %r must be a '
'(visit, depart) function tuple' % key)
raise ExtensionError(_('Value for key %r must be a '
'(visit, depart) function tuple') % key)
translator = self._translators.get(key)
translators = []
if translator is not None:
@ -665,8 +588,8 @@ class Sphinx(object):
return convert_directive_function(obj)
else:
if content or arguments or options:
raise ExtensionError('when adding directive classes, no '
'additional arguments may be given')
raise ExtensionError(_('when adding directive classes, no '
'additional arguments may be given'))
return obj
def add_directive(self, name, obj, content=None, arguments=None, **options):
@ -674,8 +597,8 @@ class Sphinx(object):
logger.debug('[app] adding directive: %r',
(name, obj, content, arguments, options))
if name in directives._directives:
logger.warning('while setting up extension %s: directive %r is '
'already registered, it will be overridden',
logger.warning(_('while setting up extension %s: directive %r is '
'already registered, it will be overridden'),
self._setting_up_extension[-1], name,
type='app', subtype='add_directive')
directives.register_directive(
@ -685,8 +608,8 @@ class Sphinx(object):
# type: (unicode, Any) -> None
logger.debug('[app] adding role: %r', (name, role))
if name in roles._roles:
logger.warning('while setting up extension %s: role %r is '
'already registered, it will be overridden',
logger.warning(_('while setting up extension %s: role %r is '
'already registered, it will be overridden'),
self._setting_up_extension[-1], name,
type='app', subtype='add_role')
roles.register_local_role(name, role)
@ -697,8 +620,8 @@ class Sphinx(object):
# register_canonical_role
logger.debug('[app] adding generic role: %r', (name, nodeclass))
if name in roles._roles:
logger.warning('while setting up extension %s: role %r is '
'already registered, it will be overridden',
logger.warning(_('while setting up extension %s: role %r is '
'already registered, it will be overridden'),
self._setting_up_extension[-1], name,
type='app', subtype='add_generic_role')
role = roles.GenericRole(name, nodeclass)
@ -708,17 +631,17 @@ class Sphinx(object):
# type: (Type[Domain]) -> None
logger.debug('[app] adding domain: %r', domain)
if domain.name in self.domains:
raise ExtensionError('domain %s already registered' % domain.name)
raise ExtensionError(_('domain %s already registered') % domain.name)
self.domains[domain.name] = domain
def override_domain(self, domain):
# type: (Type[Domain]) -> None
logger.debug('[app] overriding domain: %r', domain)
if domain.name not in self.domains:
raise ExtensionError('domain %s not yet registered' % domain.name)
raise ExtensionError(_('domain %s not yet registered') % domain.name)
if not issubclass(domain, self.domains[domain.name]):
raise ExtensionError('new domain not a subclass of registered %s '
'domain' % domain.name)
raise ExtensionError(_('new domain not a subclass of registered %s '
'domain') % domain.name)
self.domains[domain.name] = domain
def add_directive_to_domain(self, domain, name, obj,
@ -727,7 +650,7 @@ class Sphinx(object):
logger.debug('[app] adding directive to domain: %r',
(domain, name, obj, content, arguments, options))
if domain not in self.domains:
raise ExtensionError('domain %s not yet registered' % domain)
raise ExtensionError(_('domain %s not yet registered') % domain)
self.domains[domain].directives[name] = \
self._directive_helper(obj, content, arguments, **options)
@ -735,14 +658,14 @@ class Sphinx(object):
# type: (unicode, unicode, Any) -> None
logger.debug('[app] adding role to domain: %r', (domain, name, role))
if domain not in self.domains:
raise ExtensionError('domain %s not yet registered' % domain)
raise ExtensionError(_('domain %s not yet registered') % domain)
self.domains[domain].roles[name] = role
def add_index_to_domain(self, domain, index):
# type: (unicode, Type[Index]) -> None
logger.debug('[app] adding index to domain: %r', (domain, index))
if domain not in self.domains:
raise ExtensionError('domain %s not yet registered' % domain)
raise ExtensionError(_('domain %s not yet registered') % domain)
self.domains[domain].indices.append(index)
def add_object_type(self, directivename, rolename, indextemplate='',
@ -782,10 +705,15 @@ class Sphinx(object):
StandardDomain.roles[rolename] = XRefRole(innernodeclass=ref_nodeclass)
def add_transform(self, transform):
# type: (Transform) -> None
# type: (Type[Transform]) -> None
logger.debug('[app] adding transform: %r', transform)
SphinxStandaloneReader.transforms.append(transform)
def add_post_transform(self, transform):
# type: (Type[Transform]) -> None
logger.debug('[app] adding post transform: %r', transform)
self.post_transforms.append(transform)
def add_javascript(self, filename):
# type: (unicode) -> None
logger.debug('[app] adding javascript: %r', filename)
@ -850,8 +778,8 @@ class Sphinx(object):
# type: (unicode, Parser) -> None
logger.debug('[app] adding search source_parser: %r, %r', suffix, parser)
if suffix in self._additional_source_parsers:
logger.warning('while setting up extension %s: source_parser for %r is '
'already registered, it will be overridden',
logger.warning(_('while setting up extension %s: source_parser for %r is '
'already registered, it will be overridden'),
self._setting_up_extension[-1], suffix,
type='app', subtype='add_source_parser')
self._additional_source_parsers[suffix] = parser

View File

@ -5,7 +5,7 @@
Builder superclass for all builders.
:copyright: Copyright 2007-2016 by the Sphinx team, see AUTHORS.
:copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
@ -17,6 +17,7 @@ try:
except ImportError:
multiprocessing = None
from six import itervalues
from docutils import nodes
from sphinx.util import i18n, path_stabilize, logging, status_iterator
@ -48,21 +49,20 @@ class Builder(object):
Builds target formats from the reST sources.
"""
# builder's name, for the -b command line options
#: The builder's name, for the -b command line option.
name = '' # type: unicode
# builder's output format, or '' if no document output is produced
#: The builder's output format, or '' if no document output is produced.
format = '' # type: unicode
# doctree versioning method
versioning_method = 'none' # type: unicode
versioning_compare = False
# allow parallel write_doc() calls
allow_parallel = False
# support translation
use_message_catalog = True
def __init__(self, app):
# type: (Sphinx) -> None
self.env = app.env # type: BuildEnvironment
self.env.set_versioning_method(self.versioning_method,
self.versioning_compare)
self.srcdir = app.srcdir
self.confdir = app.confdir
self.outdir = app.outdir
@ -71,6 +71,7 @@ class Builder(object):
os.makedirs(self.doctreedir)
self.app = app # type: Sphinx
self.env = None # type: BuildEnvironment
self.warn = app.warn # type: Callable
self.info = app.info # type: Callable
self.config = app.config # type: Config
@ -97,7 +98,12 @@ class Builder(object):
# load default translator class
self.translator_class = app._translators.get(self.name)
self.init()
def set_environment(self, env):
# type: (BuildEnvironment) -> None
"""Store BuildEnvironment object."""
self.env = env
self.env.set_versioning_method(self.versioning_method,
self.versioning_compare)
# helper methods
def init(self):
@ -146,6 +152,13 @@ class Builder(object):
"""
raise NotImplementedError
def get_asset_paths(self):
# type: () -> List[unicode]
"""Return list of paths for assets (ex. templates, CSS, etc.)."""
return []
#: The list of MIME types of image formats supported by the builder.
#: Image files are searched in the order in which they appear here.
supported_image_types = [] # type: List[unicode]
def post_process_images(self, doctree):
@ -289,8 +302,7 @@ class Builder(object):
# while reading, collect all warnings from docutils
with logging.pending_warnings():
updated_docnames = set(self.env.update(self.config, self.srcdir,
self.doctreedir, self.app))
updated_docnames = set(self.env.update(self.config, self.srcdir, self.doctreedir))
doccount = len(updated_docnames)
logger.info(bold('looking for now-outdated files... '), nonl=1)
@ -328,11 +340,10 @@ class Builder(object):
self.parallel_ok = False
if parallel_available and self.app.parallel > 1 and self.allow_parallel:
self.parallel_ok = True
for extname, md in self.app._extension_metadata.items():
par_ok = md.get('parallel_write_safe', True)
if not par_ok:
for extension in itervalues(self.app.extensions):
if not extension.parallel_write_safe:
logger.warning('the %s extension is not safe for parallel '
'writing, doing serial write', extname)
'writing, doing serial write', extension.name)
self.parallel_ok = False
break

View File

@ -5,7 +5,7 @@
Build Apple help books.
:copyright: Copyright 2007-2016 by the Sphinx team, see AUTHORS.
:copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
from __future__ import print_function

View File

@ -5,7 +5,7 @@
Changelog builder.
:copyright: Copyright 2007-2016 by the Sphinx team, see AUTHORS.
:copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""

View File

@ -7,7 +7,7 @@
.. _Devhelp: http://live.gnome.org/devhelp
:copyright: Copyright 2007-2016 by the Sphinx team, see AUTHORS.
:copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
from __future__ import absolute_import

View File

@ -6,12 +6,13 @@
Build epub files.
Originally derived from qthelp.py.
:copyright: Copyright 2007-2016 by the Sphinx team, see AUTHORS.
:copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
import os
import re
import warnings
from os import path
from zipfile import ZIP_DEFLATED, ZIP_STORED, ZipFile
from datetime import datetime
@ -30,6 +31,7 @@ from docutils import nodes
from sphinx import addnodes
from sphinx import package_dir
from sphinx.builders.html import StandaloneHTMLBuilder
from sphinx.deprecation import RemovedInSphinx17Warning
from sphinx.util import logging
from sphinx.util import status_iterator
from sphinx.util.osutil import ensuredir, copyfile, make_filename
@ -713,10 +715,18 @@ class EpubBuilder(StandaloneHTMLBuilder):
epub.write(path.join(outdir, filename), filename, ZIP_DEFLATED) # type: ignore
def emit_deprecation_warning(app):
# type: (Sphinx) -> None
if app.builder.__class__ is EpubBuilder:
warnings.warn('epub2 builder is deprecated. Please use epub3 builder instead.',
RemovedInSphinx17Warning)
def setup(app):
# type: (Sphinx) -> Dict[unicode, Any]
app.setup_extension('sphinx.builders.html')
app.add_builder(EpubBuilder)
app.connect('builder-inited', emit_deprecation_warning)
# config values
app.add_config_value('epub_basename', lambda self: make_filename(self.project), None)

View File

@ -5,7 +5,7 @@
The MessageCatalogBuilder class.
:copyright: Copyright 2007-2016 by the Sphinx team, see AUTHORS.
:copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
@ -112,16 +112,15 @@ class I18nBuilder(Builder):
"""
name = 'i18n'
versioning_method = 'text'
versioning_compare = None # be set by `gettext_uuid`
def __init__(self, app):
# type: (Sphinx) -> None
self.versioning_compare = app.env.config.gettext_uuid
super(I18nBuilder, self).__init__(app)
versioning_compare = None # type: bool
# be set by `gettext_uuid`
use_message_catalog = False
def init(self):
# type: () -> None
Builder.init(self)
self.env.set_versioning_method(self.versioning_method,
self.env.config.gettext_uuid)
self.tags = I18nTags()
self.catalogs = defaultdict(Catalog) # type: defaultdict[unicode, Catalog]

View File

@ -5,7 +5,7 @@
Several HTML builders.
:copyright: Copyright 2007-2016 by the Sphinx team, see AUTHORS.
:copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
@ -120,9 +120,6 @@ class StandaloneHTMLBuilder(Builder):
imgpath = None # type: unicode
domain_indices = [] # type: List[Tuple[unicode, Type[Index], List[Tuple[unicode, List[List[Union[unicode, int]]]]], bool]] # NOQA
default_sidebars = ['localtoc.html', 'relations.html',
'sourcelink.html', 'searchbox.html']
# cached publisher object for snippets
_publisher = None
@ -265,6 +262,10 @@ class StandaloneHTMLBuilder(Builder):
# source doesn't exist anymore
pass
def get_asset_paths(self):
# type: () -> List[unicode]
return self.config.html_extra_path
def render_partial(self, node):
# type: (nodes.Nodes) -> Dict[unicode, unicode]
"""Utility: Render a lone doctree node."""
@ -1022,7 +1023,8 @@ class SingleFileHTMLBuilder(StandaloneHTMLBuilder):
if 'includehidden' not in kwds:
kwds['includehidden'] = False
toctree = TocTree(self.env).get_toctree_for(docname, self, collapse, **kwds)
self.fix_refuris(toctree)
if toctree is not None:
self.fix_refuris(toctree)
return self.render_partial(toctree)['fragment']
def assemble_doctree(self):

View File

@ -6,7 +6,7 @@
Build HTML help support files.
Parts adapted from Python's Doc/tools/prechm.py.
:copyright: Copyright 2007-2016 by the Sphinx team, see AUTHORS.
:copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
from __future__ import print_function

View File

@ -5,7 +5,7 @@
LaTeX builder.
:copyright: Copyright 2007-2016 by the Sphinx team, see AUTHORS.
:copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""

View File

@ -5,7 +5,7 @@
The CheckExternalLinksBuilder class.
:copyright: Copyright 2007-2016 by the Sphinx team, see AUTHORS.
:copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
@ -182,7 +182,9 @@ class CheckExternalLinksBuilder(Builder):
# history contains any redirects, get last
if response.history:
code = response.history[-1].status_code
return 'redirected', new_url, code
return 'redirected', new_url, code
else:
return 'redirected', new_url, 0
def check():
# type: () -> Tuple[unicode, unicode, int]

View File

@ -5,7 +5,7 @@
Manual pages builder.
:copyright: Copyright 2007-2016 by the Sphinx team, see AUTHORS.
:copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""

View File

@ -5,7 +5,7 @@
Build input files for the Qt collection generator.
:copyright: Copyright 2007-2016 by the Sphinx team, see AUTHORS.
:copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""

View File

@ -5,7 +5,7 @@
Texinfo builder.
:copyright: Copyright 2007-2016 by the Sphinx team, see AUTHORS.
:copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""

View File

@ -5,7 +5,7 @@
Plain-text Sphinx builder.
:copyright: Copyright 2007-2016 by the Sphinx team, see AUTHORS.
:copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""

View File

@ -5,7 +5,7 @@
Builder for the web support package.
:copyright: Copyright 2007-2016 by the Sphinx team, see AUTHORS.
:copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""

View File

@ -5,7 +5,7 @@
Docutils-native XML and pseudo-XML builders.
:copyright: Copyright 2007-2016 by the Sphinx team, see AUTHORS.
:copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""

View File

@ -5,7 +5,7 @@
sphinx-build command-line handling.
:copyright: Copyright 2007-2016 by the Sphinx team, see AUTHORS.
:copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
from __future__ import print_function

View File

@ -5,7 +5,7 @@
Build configuration file handling.
:copyright: Copyright 2007-2016 by the Sphinx team, see AUTHORS.
:copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
@ -16,7 +16,7 @@ from six import PY2, PY3, iteritems, string_types, binary_type, text_type, integ
from typing import Any, NamedTuple, Union
from sphinx.errors import ConfigError
from sphinx.locale import l_
from sphinx.locale import l_, _
from sphinx.util import logging
from sphinx.util.i18n import format_date
from sphinx.util.osutil import cd
@ -56,7 +56,7 @@ class ENUM(object):
"""represents the config value should be a one of candidates.
Example:
app.add_config_value('latex_show_urls', 'no', ENUM('no', 'footnote', 'inline'))
app.add_config_value('latex_show_urls', 'no', None, ENUM('no', 'footnote', 'inline'))
"""
def __init__(self, *candidates):
# type: (unicode) -> None
@ -233,8 +233,8 @@ class Config(object):
else:
defvalue = self.values[name][0]
if isinstance(defvalue, dict):
raise ValueError('cannot override dictionary config setting %r, '
'ignoring (use %r to set individual elements)' %
raise ValueError(_('cannot override dictionary config setting %r, '
'ignoring (use %r to set individual elements)') %
(name, name + '.key=value'))
elif isinstance(defvalue, list):
return value.split(',')
@ -242,20 +242,22 @@ class Config(object):
try:
return int(value)
except ValueError:
raise ValueError('invalid number %r for config value %r, ignoring' %
raise ValueError(_('invalid number %r for config value %r, ignoring') %
(value, name))
elif hasattr(defvalue, '__call__'):
return value
elif defvalue is not None and not isinstance(defvalue, string_types):
raise ValueError('cannot override config setting %r with unsupported '
'type, ignoring' % name)
raise ValueError(_('cannot override config setting %r with unsupported '
'type, ignoring') % name)
else:
return value
def pre_init_values(self):
# type: () -> None
"""Initialize some limited config variables before loading extensions"""
variables = ['needs_sphinx', 'suppress_warnings']
"""
Initialize some limited config variables before initialize i18n and loading extensions
"""
variables = ['needs_sphinx', 'suppress_warnings', 'language', 'locale_dirs']
for name in variables:
try:
if name in self.overrides:
@ -275,7 +277,7 @@ class Config(object):
config.setdefault(realvalname, {})[key] = value
continue
elif valname not in self.values:
logger.warning('unknown config value %r in override, ignoring', valname)
logger.warning(_('unknown config value %r in override, ignoring'), valname)
continue
if isinstance(value, string_types):
config[valname] = self.convert_overrides(valname, value)
@ -294,7 +296,7 @@ class Config(object):
if name.startswith('_'):
raise AttributeError(name)
if name not in self.values:
raise AttributeError('No such config value: %s' % name)
raise AttributeError(_('No such config value: %s') % name)
default = self.values[name][0]
if hasattr(default, '__call__'):
return default(self)

View File

@ -5,7 +5,7 @@
Sphinx deprecation classes and utilities.
:copyright: Copyright 2007-2016 by the Sphinx team, see AUTHORS.
:copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""

View File

@ -5,7 +5,7 @@
Handlers for additional ReST directives.
:copyright: Copyright 2007-2016 by the Sphinx team, see AUTHORS.
:copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""

View File

@ -3,7 +3,7 @@
sphinx.directives.code
~~~~~~~~~~~~~~~~~~~~~~
:copyright: Copyright 2007-2016 by the Sphinx team, see AUTHORS.
:copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
@ -314,6 +314,11 @@ class LiteralIncludeReader(object):
self.lineno_start += lineno
return lines[lineno:]
else:
if inclusive is True:
raise ValueError('start-after pattern not found: %s' % start)
else:
raise ValueError('start-at pattern not found: %s' % start)
return lines
@ -338,6 +343,11 @@ class LiteralIncludeReader(object):
return []
else:
return lines[:lineno]
else:
if inclusive is True:
raise ValueError('end-at pattern not found: %s' % end)
else:
raise ValueError('end-before pattern not found: %s' % end)
return lines
@ -424,7 +434,7 @@ class LiteralInclude(Directive):
'lineno-match' in self.options)
retnode['classes'] += self.options.get('class', [])
extra_args = retnode['highlight_args'] = {}
if 'empahsize-lines' in self.options:
if 'emphasize-lines' in self.options:
hl_lines = parselinenos(self.options['emphasize-lines'], lines)
if any(i >= lines for i in hl_lines):
logger.warning('line number spec is out of range(1-%d): %r' %

View File

@ -3,7 +3,7 @@
sphinx.directives.other
~~~~~~~~~~~~~~~~~~~~~~~
:copyright: Copyright 2007-2016 by the Sphinx team, see AUTHORS.
:copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""

View File

@ -3,7 +3,7 @@
sphinx.directives.patches
~~~~~~~~~~~~~~~~~~~~~~~~~
:copyright: Copyright 2007-2016 by the Sphinx team, see AUTHORS.
:copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""

View File

@ -6,7 +6,7 @@
Support for domains, which are groupings of description directives
and roles describing e.g. constructs of one programming language.
:copyright: Copyright 2007-2016 by the Sphinx team, see AUTHORS.
:copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
@ -240,6 +240,13 @@ class Domain(object):
"""Process a document after it is read by the environment."""
pass
def process_field_xref(self, pnode):
# type: (nodes.Node) -> None
"""Process a pending xref created in a doc field.
For example, attach information about the current scope.
"""
pass
def resolve_xref(self, env, fromdocname, builder,
typ, target, node, contnode):
# type: (BuildEnvironment, unicode, Builder, unicode, unicode, nodes.Node, nodes.Node) -> nodes.Node # NOQA

View File

@ -5,7 +5,7 @@
The C language domain.
:copyright: Copyright 2007-2016 by the Sphinx team, see AUTHORS.
:copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""

View File

@ -5,7 +5,7 @@
The C++ language domain.
:copyright: Copyright 2007-2016 by the Sphinx team, see AUTHORS.
:copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
@ -3480,7 +3480,7 @@ class DefinitionParser(object):
value = self.matched_text
else:
# TODO: add handling of more bracket-like things, and quote handling
brackets = {'(': ')', '[': ']'} # type: Dict[unicode, unicode]
brackets = {'(': ')', '[': ']', '<': '>'} # type: Dict[unicode, unicode]
symbols = [] # type: List[unicode]
while not self.eof:
if (len(symbols) == 0 and self.current_char in end):
@ -4552,10 +4552,11 @@ class CPPObject(ObjectDescription):
def handle_signature(self, sig, signode):
# type: (unicode, addnodes.desc_signature) -> Any
if 'cpp:parent_symbol' not in self.env.ref_context:
if 'cpp:parent_symbol' not in self.env.temp_data:
root = self.env.domaindata['cpp']['root_symbol']
self.env.ref_context['cpp:parent_symbol'] = root
parentSymbol = self.env.ref_context['cpp:parent_symbol']
self.env.temp_data['cpp:parent_symbol'] = root
self.env.ref_context['cpp:parent_key'] = root.get_lookup_key()
parentSymbol = self.env.temp_data['cpp:parent_symbol']
parser = DefinitionParser(sig, self, self.env.config)
try:
@ -4567,16 +4568,16 @@ class CPPObject(ObjectDescription):
# the possibly inner declarations.
name = _make_phony_error_name()
symbol = parentSymbol.add_name(name)
self.env.ref_context['cpp:last_symbol'] = symbol
self.env.temp_data['cpp:last_symbol'] = symbol
raise ValueError
try:
symbol = parentSymbol.add_declaration(ast, docname=self.env.docname)
self.env.ref_context['cpp:last_symbol'] = symbol
self.env.temp_data['cpp:last_symbol'] = symbol
except _DuplicateSymbolError as e:
# Assume we are actually in the old symbol,
# instead of the newly created duplicate.
self.env.ref_context['cpp:last_symbol'] = e.symbol
self.env.temp_data['cpp:last_symbol'] = e.symbol
if ast.objectType == 'enumerator':
self._add_enumerator_to_parent(ast)
@ -4587,14 +4588,17 @@ class CPPObject(ObjectDescription):
def before_content(self):
# type: () -> None
lastSymbol = self.env.ref_context['cpp:last_symbol']
lastSymbol = self.env.temp_data['cpp:last_symbol']
assert lastSymbol
self.oldParentSymbol = self.env.ref_context['cpp:parent_symbol']
self.env.ref_context['cpp:parent_symbol'] = lastSymbol
self.oldParentSymbol = self.env.temp_data['cpp:parent_symbol']
self.oldParentKey = self.env.ref_context['cpp:parent_key']
self.env.temp_data['cpp:parent_symbol'] = lastSymbol
self.env.ref_context['cpp:parent_key'] = lastSymbol.get_lookup_key()
def after_content(self):
# type: () -> None
self.env.ref_context['cpp:parent_symbol'] = self.oldParentSymbol
self.env.temp_data['cpp:parent_symbol'] = self.oldParentSymbol
self.env.ref_context['cpp:parent_key'] = self.oldParentKey
class CPPTypeObject(CPPObject):
@ -4711,8 +4715,9 @@ class CPPNamespaceObject(Directive):
ast = ASTNamespace(name, None)
symbol = rootSymbol.add_name(ast.nestedName, ast.templatePrefix)
stack = [symbol]
env.ref_context['cpp:parent_symbol'] = symbol
env.temp_data['cpp:parent_symbol'] = symbol
env.temp_data['cpp:namespace_stack'] = stack
env.ref_context['cpp:parent_key'] = symbol.get_lookup_key()
return []
@ -4740,14 +4745,15 @@ class CPPNamespacePushObject(Directive):
self.warn(e.description)
name = _make_phony_error_name()
ast = ASTNamespace(name, None)
oldParent = env.ref_context.get('cpp:parent_symbol', None)
oldParent = env.temp_data.get('cpp:parent_symbol', None)
if not oldParent:
oldParent = env.domaindata['cpp']['root_symbol']
symbol = oldParent.add_name(ast.nestedName, ast.templatePrefix)
stack = env.temp_data.get('cpp:namespace_stack', [])
stack.append(symbol)
env.ref_context['cpp:parent_symbol'] = symbol
env.temp_data['cpp:parent_symbol'] = symbol
env.temp_data['cpp:namespace_stack'] = stack
env.ref_context['cpp:parent_key'] = symbol.get_lookup_key()
return []
@ -4775,17 +4781,16 @@ class CPPNamespacePopObject(Directive):
symbol = stack[-1]
else:
symbol = env.domaindata['cpp']['root_symbol']
env.ref_context['cpp:parent_symbol'] = symbol
env.temp_data['cpp:parent_symbol'] = symbol
env.temp_data['cpp:namespace_stack'] = stack
env.ref_context['cpp:parent_key'] = symbol.get_lookup_key()
return []
class CPPXRefRole(XRefRole):
def process_link(self, env, refnode, has_explicit_title, title, target):
# type: (BuildEnvironment, nodes.Node, bool, unicode, unicode) -> Tuple[unicode, unicode] # NOQA
parent = env.ref_context.get('cpp:parent_symbol', None)
if parent:
refnode['cpp:parent_key'] = parent.get_lookup_key()
refnode.attributes.update(env.ref_context)
if refnode['reftype'] == 'any':
# Assume the removal part of fix_parens for :any: refs.
# The addition part is done with the reference is resolved.
@ -4866,6 +4871,10 @@ class CPPDomain(Domain):
# print(self.data['root_symbol'].dump(0))
pass
def process_field_xref(self, pnode):
# type: (nodes.Node) -> None
pnode.attributes.update(self.env.ref_context)
def merge_domaindata(self, docnames, otherdata):
# type: (List[unicode], Dict) -> None
self.data['root_symbol'].merge_with(otherdata['root_symbol'],
@ -4897,8 +4906,21 @@ class CPPDomain(Domain):
ast = 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':
return target, e
# hax on top of the paren hax to try to get correct errors
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
return target, e
t, ex = findWarning(e)
warner.warn('Unparseable C++ cross-reference: %r\n%s'
% (target, text_type(e.description)))
% (t, text_type(ex.description)))
return None, None
parentKey = node.get("cpp:parent_key", None)
rootSymbol = self.data['root_symbol']
@ -4991,7 +5013,10 @@ class CPPDomain(Domain):
'any', target, node, contnode,
emitWarnings=False)
if node:
return [('cpp:' + self.role_for_objtype(objtype), node)]
if objtype == 'templateParam':
return [('cpp:templateParam', node)]
else:
return [('cpp:' + self.role_for_objtype(objtype), node)]
return []
def get_objects(self):

View File

@ -5,10 +5,13 @@
The JavaScript domain.
:copyright: Copyright 2007-2016 by the Sphinx team, see AUTHORS.
:copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
from docutils import nodes
from docutils.parsers.rst import Directive, directives
from sphinx import addnodes
from sphinx.domains import Domain, ObjType
from sphinx.locale import l_, _
@ -38,57 +41,68 @@ class JSObject(ObjectDescription):
#: what is displayed right before the documentation entry
display_prefix = None # type: unicode
#: If ``allow_nesting`` is ``True``, the object prefixes will be accumulated
#: based on directive nesting
allow_nesting = False
def handle_signature(self, sig, signode):
# type: (unicode, addnodes.desc_signature) -> Tuple[unicode, unicode]
"""Breaks down construct signatures
Parses out prefix and argument list from construct definition. The
namespace and class will be determined by the nesting of domain
directives.
"""
sig = sig.strip()
if '(' in sig and sig[-1:] == ')':
prefix, arglist = sig.split('(', 1)
prefix = prefix.strip()
member, arglist = sig.split('(', 1)
member = member.strip()
arglist = arglist[:-1].strip()
else:
prefix = sig
member = sig
arglist = None
if '.' in prefix:
nameprefix, name = prefix.rsplit('.', 1)
else:
nameprefix = None
name = prefix
# If construct is nested, prefix the current prefix
prefix = self.env.ref_context.get('js:object', None)
mod_name = self.env.ref_context.get('js:module')
name = member
try:
member_prefix, member_name = member.rsplit('.', 1)
except ValueError:
member_name = name
member_prefix = ''
finally:
name = member_name
if prefix and member_prefix:
prefix = '.'.join([prefix, member_prefix])
elif prefix is None and member_prefix:
prefix = member_prefix
fullname = name
if prefix:
fullname = '.'.join([prefix, name])
objectname = self.env.ref_context.get('js:object')
if nameprefix:
if objectname:
# someone documenting the method of an attribute of the current
# object? shouldn't happen but who knows...
nameprefix = objectname + '.' + nameprefix
fullname = nameprefix + '.' + name
elif objectname:
fullname = objectname + '.' + name
else:
# just a function or constructor
objectname = ''
fullname = name
signode['object'] = objectname
signode['module'] = mod_name
signode['object'] = prefix
signode['fullname'] = fullname
if self.display_prefix:
signode += addnodes.desc_annotation(self.display_prefix,
self.display_prefix)
if nameprefix:
signode += addnodes.desc_addname(nameprefix + '.', nameprefix + '.')
if prefix:
signode += addnodes.desc_addname(prefix + '.', prefix + '.')
elif mod_name:
signode += addnodes.desc_addname(mod_name + '.', mod_name + '.')
signode += addnodes.desc_name(name, name)
if self.has_arguments:
if not arglist:
signode += addnodes.desc_parameterlist()
else:
_pseudo_parse_arglist(signode, arglist)
return fullname, nameprefix
return fullname, prefix
def add_target_and_index(self, name_obj, sig, signode):
# type: (Tuple[unicode, unicode], unicode, addnodes.desc_signature) -> None
objectname = self.options.get(
'object', self.env.ref_context.get('js:object'))
fullname = name_obj[0]
mod_name = self.env.ref_context.get('js:module')
fullname = (mod_name and mod_name + '.' or '') + name_obj[0]
if fullname not in self.state.document.ids:
signode['names'].append(fullname)
signode['ids'].append(fullname.replace('$', '_S_'))
@ -103,7 +117,7 @@ class JSObject(ObjectDescription):
line=self.lineno)
objects[fullname] = self.env.docname, self.objtype
indextext = self.get_index_text(objectname, name_obj)
indextext = self.get_index_text(mod_name, name_obj)
if indextext:
self.indexnode['entries'].append(('single', indextext,
fullname.replace('$', '_S_'),
@ -124,6 +138,63 @@ class JSObject(ObjectDescription):
return _('%s (%s attribute)') % (name, obj)
return ''
def before_content(self):
# type: () -> None
"""Handle object nesting before content
:py:class:`JSObject` represents JavaScript language constructs. For
constructs that are nestable, this method will build up a stack of the
nesting heirarchy so that it can be later de-nested correctly, in
:py:meth:`after_content`.
For constructs that aren't nestable, the stack is bypassed, and instead
only the most recent object is tracked. This object prefix name will be
removed with :py:meth:`after_content`.
The following keys are used in ``self.env.ref_context``:
js:objects
Stores the object prefix history. With each nested element, we
add the object prefix to this list. When we exit that object's
nesting level, :py:meth:`after_content` is triggered and the
prefix is removed from the end of the list.
js:object
Current object prefix. This should generally reflect the last
element in the prefix history
"""
prefix = None
if self.names:
(obj_name, obj_name_prefix) = self.names.pop()
prefix = obj_name_prefix.strip('.') if obj_name_prefix else None
if self.allow_nesting:
prefix = obj_name
if prefix:
self.env.ref_context['js:object'] = prefix
if self.allow_nesting:
objects = self.env.ref_context.setdefault('js:objects', [])
objects.append(prefix)
def after_content(self):
# type: () -> None
"""Handle object de-nesting after content
If this class is a nestable object, removing the last nested class prefix
ends further nesting in the object.
If this class is not a nestable object, the list of classes should not
be altered as we didn't affect the nesting levels in
:py:meth:`before_content`.
"""
objects = self.env.ref_context.setdefault('js:objects', [])
if self.allow_nesting:
try:
objects.pop()
except IndexError:
pass
self.env.ref_context['js:object'] = (objects[-1] if len(objects) > 0
else None)
class JSCallable(JSObject):
"""Description of a JavaScript function, method or constructor."""
@ -146,6 +217,57 @@ class JSCallable(JSObject):
class JSConstructor(JSCallable):
"""Like a callable but with a different prefix."""
display_prefix = 'class '
allow_nesting = True
class JSModule(Directive):
"""
Directive to mark description of a new JavaScript module.
This directive specifies the module name that will be used by objects that
follow this directive.
Options
-------
noindex
If the ``noindex`` option is specified, no linkable elements will be
created, and the module won't be added to the global module index. This
is useful for splitting up the module definition across multiple
sections or files.
:param mod_name: Module name
"""
has_content = False
required_arguments = 1
optional_arguments = 0
final_argument_whitespace = False
option_spec = {
'noindex': directives.flag
}
def run(self):
# type: () -> List[nodes.Node]
env = self.state.document.settings.env
mod_name = self.arguments[0].strip()
env.ref_context['js:module'] = mod_name
noindex = 'noindex' in self.options
ret = []
if not noindex:
env.domaindata['js']['modules'][mod_name] = env.docname
# Make a duplicate entry in 'objects' to facilitate searching for
# the module in JavaScriptDomain.find_obj()
env.domaindata['js']['objects'][mod_name] = (env.docname, 'module')
targetnode = nodes.target('', '', ids=['module-' + mod_name],
ismod=True)
self.state.document.note_explicit_target(targetnode)
ret.append(targetnode)
indextext = _('%s (module)') % mod_name
inode = addnodes.index(entries=[('single', indextext,
'module-' + mod_name, '', None)])
ret.append(inode)
return ret
class JSXRefRole(XRefRole):
@ -153,6 +275,7 @@ class JSXRefRole(XRefRole):
# type: (BuildEnvironment, nodes.Node, bool, unicode, unicode) -> Tuple[unicode, unicode] # NOQA
# basically what sphinx.domains.python.PyXRefRole does
refnode['js:object'] = env.ref_context.get('js:object')
refnode['js:module'] = env.ref_context.get('js:module')
if not has_explicit_title:
title = title.lstrip('.')
target = target.lstrip('~')
@ -174,31 +297,41 @@ class JavaScriptDomain(Domain):
# if you add a new object type make sure to edit JSObject.get_index_string
object_types = {
'function': ObjType(l_('function'), 'func'),
'method': ObjType(l_('method'), 'meth'),
'class': ObjType(l_('class'), 'class'),
'data': ObjType(l_('data'), 'data'),
'attribute': ObjType(l_('attribute'), 'attr'),
'module': ObjType(l_('module'), 'mod'),
}
directives = {
'function': JSCallable,
'method': JSCallable,
'class': JSConstructor,
'data': JSObject,
'attribute': JSObject,
'module': JSModule,
}
roles = {
'func': JSXRefRole(fix_parens=True),
'meth': JSXRefRole(fix_parens=True),
'class': JSXRefRole(fix_parens=True),
'data': JSXRefRole(),
'attr': JSXRefRole(),
'mod': JSXRefRole(),
}
initial_data = {
'objects': {}, # fullname -> docname, objtype
'modules': {}, # mod_name -> docname
} # type: Dict[unicode, Dict[unicode, Tuple[unicode, unicode]]]
def clear_doc(self, docname):
# type: (unicode) -> None
for fullname, (fn, _l) in list(self.data['objects'].items()):
if fn == docname:
for fullname, (pkg_docname, _l) in list(self.data['objects'].items()):
if pkg_docname == docname:
del self.data['objects'][fullname]
for mod_name, pkg_docname in list(self.data['modules'].items()):
if pkg_docname == docname:
del self.data['modules'][mod_name]
def merge_domaindata(self, docnames, otherdata):
# type: (List[unicode], Dict) -> None
@ -206,31 +339,42 @@ class JavaScriptDomain(Domain):
for fullname, (fn, objtype) in otherdata['objects'].items():
if fn in docnames:
self.data['objects'][fullname] = (fn, objtype)
for mod_name, pkg_docname in otherdata['modules'].items():
if pkg_docname in docnames:
self.data['modules'][mod_name] = pkg_docname
def find_obj(self, env, obj, name, typ, searchorder=0):
# type: (BuildEnvironment, unicode, unicode, unicode, int) -> Tuple[unicode, Tuple[unicode, unicode]] # NOQA
def find_obj(self, env, mod_name, prefix, name, typ, searchorder=0):
# type: (BuildEnvironment, unicode, unicode, unicode, unicode, int) -> Tuple[unicode, Tuple[unicode, unicode]] # NOQA
if name[-2:] == '()':
name = name[:-2]
objects = self.data['objects']
searches = []
if mod_name and prefix:
searches.append('.'.join([mod_name, prefix, name]))
if mod_name:
searches.append('.'.join([mod_name, name]))
if prefix:
searches.append('.'.join([prefix, name]))
searches.append(name)
if searchorder == 0:
searches.reverse()
newname = None
if searchorder == 1:
if obj and obj + '.' + name in objects:
newname = obj + '.' + name
else:
newname = name
else:
if name in objects:
newname = name
elif obj and obj + '.' + name in objects:
newname = obj + '.' + name
for search_name in searches:
if search_name in objects:
newname = search_name
return newname, objects.get(newname)
def resolve_xref(self, env, fromdocname, builder, typ, target, node,
contnode):
# type: (BuildEnvironment, unicode, Builder, unicode, unicode, nodes.Node, nodes.Node) -> nodes.Node # NOQA
objectname = node.get('js:object')
mod_name = node.get('js:module')
prefix = node.get('js:object')
searchorder = node.hasattr('refspecific') and 1 or 0
name, obj = self.find_obj(env, objectname, target, typ, searchorder)
name, obj = self.find_obj(env, mod_name, prefix, target, typ, searchorder)
if not obj:
return None
return make_refnode(builder, fromdocname, obj[0],
@ -239,8 +383,9 @@ class JavaScriptDomain(Domain):
def resolve_any_xref(self, env, fromdocname, builder, target, node,
contnode):
# type: (BuildEnvironment, unicode, Builder, unicode, nodes.Node, nodes.Node) -> List[Tuple[unicode, nodes.Node]] # NOQA
objectname = node.get('js:object')
name, obj = self.find_obj(env, objectname, target, None, 1)
mod_name = node.get('js:module')
prefix = node.get('js:object')
name, obj = self.find_obj(env, mod_name, prefix, target, None, 1)
if not obj:
return []
return [('js:' + self.role_for_objtype(obj[1]),

View File

@ -5,7 +5,7 @@
The Python domain.
:copyright: Copyright 2007-2016 by the Sphinx team, see AUTHORS.
:copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
@ -96,11 +96,17 @@ def _pseudo_parse_arglist(signode, arglist):
# This override allows our inline type specifiers to behave like :class: link
# when it comes to handling "." and "~" prefixes.
class PyXrefMixin(object):
def make_xref(self, rolename, domain, target, innernode=nodes.emphasis,
contnode=None):
# type: (unicode, unicode, unicode, nodes.Node, nodes.Node) -> nodes.Node
def make_xref(self,
rolename, # type: unicode
domain, # type: unicode
target, # type: unicode
innernode=nodes.emphasis, # type: nodes.Node
contnode=None, # type: nodes.Node
env=None, # type: BuildEnvironment
):
# type: (...) -> nodes.Node
result = super(PyXrefMixin, self).make_xref(rolename, domain, target, # type: ignore
innernode, contnode)
innernode, contnode, env)
result['refspecific'] = True
if target.startswith(('.', '~')):
prefix, result['reftarget'] = target[0], target[1:]
@ -113,9 +119,15 @@ class PyXrefMixin(object):
break
return result
def make_xrefs(self, rolename, domain, target, innernode=nodes.emphasis,
contnode=None):
# type: (unicode, unicode, unicode, nodes.Node, nodes.Node) -> List[nodes.Node]
def make_xrefs(self,
rolename, # type: unicode
domain, # type: unicode
target, # type: unicode
innernode=nodes.emphasis, # type: nodes.Node
contnode=None, # type: nodes.Node
env=None, # type: BuildEnvironment
):
# type: (...) -> List[nodes.Node]
delims = r'(\s*[\[\]\(\),](?:\s*or\s)?\s*|\s+or\s+)'
delims_re = re.compile(delims)
sub_targets = re.split(delims, target)
@ -131,7 +143,7 @@ class PyXrefMixin(object):
results.append(contnode or innernode(sub_target, sub_target))
else:
results.append(self.make_xref(rolename, domain, sub_target,
innernode, contnode))
innernode, contnode, env))
return results
@ -151,6 +163,9 @@ class PyTypedField(PyXrefMixin, TypedField):
class PyObject(ObjectDescription):
"""
Description of a general Python object.
:cvar allow_nesting: Class is an object that allows for nested namespaces
:vartype allow_nesting: bool
"""
option_spec = {
'noindex': directives.flag,
@ -177,6 +192,8 @@ class PyObject(ObjectDescription):
names=('rtype',), bodyrolename='obj'),
]
allow_nesting = False
def get_signature_prefix(self, sig):
# type: (unicode) -> unicode
"""May return a prefix to put before the object name in the
@ -304,13 +321,53 @@ class PyObject(ObjectDescription):
def before_content(self):
# type: () -> None
# needed for automatic qualification of members (reset in subclasses)
self.clsname_set = False
"""Handle object nesting before content
:py:class:`PyObject` represents Python language constructs. For
constructs that are nestable, such as a Python classes, this method will
build up a stack of the nesting heirarchy so that it can be later
de-nested correctly, in :py:meth:`after_content`.
For constructs that aren't nestable, the stack is bypassed, and instead
only the most recent object is tracked. This object prefix name will be
removed with :py:meth:`after_content`.
"""
prefix = None
if self.names:
# fullname and name_prefix come from the `handle_signature` method.
# fullname represents the full object name that is constructed using
# object nesting and explicit prefixes. `name_prefix` is the
# explicit prefix given in a signature
(fullname, name_prefix) = self.names[-1]
if self.allow_nesting:
prefix = fullname
elif name_prefix:
prefix = name_prefix.strip('.')
if prefix:
self.env.ref_context['py:class'] = prefix
if self.allow_nesting:
classes = self.env.ref_context.setdefault('py:classes', [])
classes.append(prefix)
def after_content(self):
# type: () -> None
if self.clsname_set:
self.env.ref_context.pop('py:class', None)
"""Handle object de-nesting after content
If this class is a nestable object, removing the last nested class prefix
ends further nesting in the object.
If this class is not a nestable object, the list of classes should not
be altered as we didn't affect the nesting levels in
:py:meth:`before_content`.
"""
classes = self.env.ref_context.setdefault('py:classes', [])
if self.allow_nesting:
try:
classes.pop()
except IndexError:
pass
self.env.ref_context['py:class'] = (classes[-1] if len(classes) > 0
else None)
class PyModulelevel(PyObject):
@ -341,6 +398,8 @@ class PyClasslike(PyObject):
Description of a class-like object (classes, interfaces, exceptions).
"""
allow_nesting = True
def get_signature_prefix(self, sig):
# type: (unicode) -> unicode
return self.objtype + ' '
@ -356,13 +415,6 @@ class PyClasslike(PyObject):
else:
return ''
def before_content(self):
# type: () -> None
PyObject.before_content(self)
if self.names:
self.env.ref_context['py:class'] = self.names[0][0]
self.clsname_set = True
class PyClassmember(PyObject):
"""
@ -438,14 +490,6 @@ class PyClassmember(PyObject):
else:
return ''
def before_content(self):
# type: () -> None
PyObject.before_content(self)
lastname = self.names and self.names[-1][1]
if lastname and not self.env.ref_context.get('py:class'):
self.env.ref_context['py:class'] = lastname.strip('.')
self.clsname_set = True
class PyDecoratorMixin(object):
"""

View File

@ -5,7 +5,7 @@
The reStructuredText domain.
:copyright: Copyright 2007-2016 by the Sphinx team, see AUTHORS.
:copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""

View File

@ -5,7 +5,7 @@
The standard domain.
:copyright: Copyright 2007-2016 by the Sphinx team, see AUTHORS.
:copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
@ -571,7 +571,7 @@ class StandardDomain(Domain):
if label in self.data['citations']:
path = env.doc2path(self.data['citations'][label][0])
logger.warning('duplicate citation %s, other instance in %s', label, path,
location=node)
location=node, type='ref', subtype='citation')
self.data['citations'][label] = (docname, node['ids'][0])
def note_labels(self, env, docname, document):

View File

@ -5,7 +5,7 @@
Global creation environment.
:copyright: Copyright 2007-2016 by the Sphinx team, see AUTHORS.
:copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
@ -20,10 +20,9 @@ import warnings
from os import path
from collections import defaultdict
from six import itervalues, class_types, next
from six import StringIO, itervalues, class_types, next
from six.moves import cPickle as pickle
from docutils import nodes
from docutils.io import NullOutput
from docutils.core import Publisher
from docutils.utils import Reporter, get_source_line
@ -35,7 +34,7 @@ from sphinx import addnodes
from sphinx.io import SphinxStandaloneReader, SphinxDummyWriter, SphinxFileInput
from sphinx.util import logging
from sphinx.util import get_matching_docs, FilenameUniqDict, status_iterator
from sphinx.util.nodes import WarningStream, is_translatable, process_only_nodes
from sphinx.util.nodes import WarningStream, is_translatable
from sphinx.util.osutil import SEP, ensuredir
from sphinx.util.i18n import find_catalog_files
from sphinx.util.console import bold # type: ignore
@ -44,14 +43,17 @@ from sphinx.util.matching import compile_matchers
from sphinx.util.parallel import ParallelTasks, parallel_available, make_chunks
from sphinx.util.websupport import is_commentable
from sphinx.errors import SphinxError, ExtensionError
from sphinx.locale import _
from sphinx.transforms import SphinxTransformer
from sphinx.versioning import add_uids, merge_doctrees
from sphinx.deprecation import RemovedInSphinx20Warning
from sphinx.deprecation import RemovedInSphinx17Warning, RemovedInSphinx20Warning
from sphinx.environment.adapters.indexentries import IndexEntries
from sphinx.environment.adapters.toctree import TocTree
if False:
# For type annotation
from typing import Any, Callable, Dict, Iterator, List, Pattern, Set, Tuple, Type, Union # NOQA
from typing import Any, Callable, Dict, IO, Iterator, List, Pattern, Set, Tuple, Type, Union # NOQA
from docutils import nodes # NOQA
from sphinx.application import Sphinx # NOQA
from sphinx.builders import Builder # NOQA
from sphinx.config import Config # NOQA
@ -104,52 +106,78 @@ class BuildEnvironment(object):
# --------- ENVIRONMENT PERSISTENCE ----------------------------------------
@staticmethod
def frompickle(srcdir, config, filename):
# type: (unicode, Config, unicode) -> BuildEnvironment
with open(filename, 'rb') as picklefile:
env = pickle.load(picklefile)
def load(f, app=None):
# type: (IO, Sphinx) -> BuildEnvironment
env = pickle.load(f)
if env.version != ENV_VERSION:
raise IOError('build environment version not current')
if env.srcdir != srcdir:
raise IOError('source directory has changed')
env.config.values = config.values
if app:
env.app = app
env.config.values = app.config.values
if env.srcdir != app.srcdir:
raise IOError('source directory has changed')
return env
def topickle(self, filename):
# type: (unicode) -> None
@classmethod
def loads(cls, string, app=None):
# type: (unicode, Sphinx) -> BuildEnvironment
io = StringIO(string)
return cls.load(io, app)
@classmethod
def frompickle(cls, filename, app):
# type: (unicode, Sphinx) -> BuildEnvironment
with open(filename, 'rb') as f:
return cls.load(f, app)
@staticmethod
def dump(env, f):
# type: (BuildEnvironment, IO) -> None
# remove unpicklable attributes
values = self.config.values
del self.config.values
domains = self.domains
del self.domains
app = env.app
del env.app
values = env.config.values
del env.config.values
domains = env.domains
del env.domains
# remove potentially pickling-problematic values from config
for key, val in list(vars(self.config).items()):
for key, val in list(vars(env.config).items()):
if key.startswith('_') or \
isinstance(val, types.ModuleType) or \
isinstance(val, types.FunctionType) or \
isinstance(val, class_types):
del self.config[key]
with open(filename, 'wb') as picklefile:
pickle.dump(self, picklefile, pickle.HIGHEST_PROTOCOL)
del env.config[key]
pickle.dump(env, f, pickle.HIGHEST_PROTOCOL)
# reset attributes
self.domains = domains
self.config.values = values
env.domains = domains
env.config.values = values
env.app = app
@classmethod
def dumps(cls, env):
# type: (BuildEnvironment) -> unicode
io = StringIO()
cls.dump(env, io)
return io.getvalue()
def topickle(self, filename):
# type: (unicode) -> None
with open(filename, 'wb') as f:
self.dump(self, f)
# --------- ENVIRONMENT INITIALIZATION -------------------------------------
def __init__(self, srcdir, doctreedir, config):
# type: (unicode, unicode, Config) -> None
self.doctreedir = doctreedir
self.srcdir = srcdir # type: unicode
self.config = config # type: Config
def __init__(self, app):
# type: (Sphinx) -> None
self.app = app
self.doctreedir = app.doctreedir
self.srcdir = app.srcdir
self.config = app.config
# the method of doctree versioning; see set_versioning_method
self.versioning_condition = None # type: Union[bool, Callable]
self.versioning_compare = None # type: bool
# the application object; only set while update() runs
self.app = None # type: Sphinx
# all the registered domains, set by the application
self.domains = {}
@ -378,15 +406,15 @@ class BuildEnvironment(object):
enc_rel_fn = rel_fn.encode(sys.getfilesystemencoding())
return rel_fn, path.abspath(path.join(self.srcdir, enc_rel_fn))
def find_files(self, config, buildername):
# type: (Config, unicode) -> None
def find_files(self, config, builder):
# type: (Config, Builder) -> None
"""Find all source files in the source dir and put them in
self.found_docs.
"""
matchers = compile_matchers(
config.exclude_patterns[:] +
config.templates_path +
config.html_extra_path +
builder.get_asset_paths() +
['**/_sources', '.#*', '**/.#*', '*.lproj/**']
)
self.found_docs = set()
@ -403,7 +431,7 @@ class BuildEnvironment(object):
# is set for the doc source and the mo file, it is processed again from
# the reading phase when mo is updated. In the future, we would like to
# move i18n process into the writing phase, and remove these lines.
if buildername != 'gettext':
if builder.use_message_catalog:
# add catalog mo file dependency
for docname in self.found_docs:
catalog_files = find_catalog_files(
@ -466,8 +494,8 @@ class BuildEnvironment(object):
return added, changed, removed
def update(self, config, srcdir, doctreedir, app):
# type: (Config, unicode, unicode, Sphinx) -> List[unicode]
def update(self, config, srcdir, doctreedir):
# type: (Config, unicode, unicode) -> List[unicode]
"""(Re-)read all files new or changed since last update.
Store all environment docnames in the canonical format (ie using SEP as
@ -495,7 +523,7 @@ class BuildEnvironment(object):
# the source and doctree directories may have been relocated
self.srcdir = srcdir
self.doctreedir = doctreedir
self.find_files(config, app.buildername)
self.find_files(config, self.app.builder)
self.config = config
# this cache also needs to be updated every time
@ -506,7 +534,7 @@ class BuildEnvironment(object):
added, changed, removed = self.get_outdated_files(config_changed)
# allow user intervention as well
for docs in app.emit('env-get-outdated', self, added, changed, removed):
for docs in self.app.emit('env-get-outdated', self, added, changed, removed):
changed.update(set(docs) & self.found_docs)
# if files were added or removed, all documents with globbed toctrees
@ -519,49 +547,43 @@ class BuildEnvironment(object):
len(removed))
logger.info(msg)
self.app = app
# clear all files no longer present
for docname in removed:
app.emit('env-purge-doc', self, docname)
self.app.emit('env-purge-doc', self, docname)
self.clear_doc(docname)
# read all new and changed files
docnames = sorted(added | changed)
# allow changing and reordering the list of docs to read
app.emit('env-before-read-docs', self, docnames)
self.app.emit('env-before-read-docs', self, docnames)
# check if we should do parallel or serial read
par_ok = False
if parallel_available and len(docnames) > 5 and app.parallel > 1:
par_ok = True
for extname, md in app._extension_metadata.items():
ext_ok = md.get('parallel_read_safe')
if ext_ok:
continue
if ext_ok is None:
logger.warning('the %s extension does not declare if it '
'is safe for parallel reading, assuming it '
'isn\'t - please ask the extension author to '
'check and make it explicit', extname)
if parallel_available and len(docnames) > 5 and self.app.parallel > 1:
for ext in itervalues(self.app.extensions):
if ext.parallel_read_safe is None:
logger.warning(_('the %s extension does not declare if it is safe '
'for parallel reading, assuming it isn\'t - please '
'ask the extension author to check and make it '
'explicit'), ext.name)
logger.warning('doing serial read')
else:
logger.warning('the %s extension is not safe for parallel '
'reading, doing serial read', extname)
par_ok = False
break
break
elif ext.parallel_read_safe is False:
break
else:
# all extensions support parallel-read
par_ok = True
if par_ok:
self._read_parallel(docnames, app, nproc=app.parallel)
self._read_parallel(docnames, self.app, nproc=self.app.parallel)
else:
self._read_serial(docnames, app)
self._read_serial(docnames, self.app)
if config.master_doc not in self.all_docs:
raise SphinxError('master file %s not found' %
self.doc2path(config.master_doc))
self.app = None
for retval in app.emit('env-updated', self):
for retval in self.app.emit('env-updated', self):
if retval is not None:
docnames.extend(retval)
@ -584,20 +606,17 @@ class BuildEnvironment(object):
self.clear_doc(docname)
def read_process(docs):
# type: (List[unicode]) -> BuildEnvironment
# type: (List[unicode]) -> unicode
self.app = app
for docname in docs:
self.read_doc(docname, app)
# allow pickling self to send it back
del self.app
del self.domains
del self.config.values
del self.config
return self
return BuildEnvironment.dumps(self)
def merge(docs, otherenv):
# type: (List[unicode], BuildEnvironment) -> None
self.merge_info_from(docs, otherenv, app)
# type: (List[unicode], unicode) -> None
env = BuildEnvironment.loads(otherenv)
self.merge_info_from(docs, env, app)
tasks = ParallelTasks(nproc)
chunks = make_chunks(docnames, nproc)
@ -748,18 +767,18 @@ class BuildEnvironment(object):
def currmodule(self):
# type: () -> None
"""Backwards compatible alias. Will be removed."""
logger.warning('env.currmodule is being referenced by an '
'extension; this API will be removed in the future',
location=self.docname)
warnings.warn('env.currmodule is deprecated. '
'Use env.ref_context["py:module"] instead.',
RemovedInSphinx17Warning)
return self.ref_context.get('py:module')
@property
def currclass(self):
# type: () -> None
"""Backwards compatible alias. Will be removed."""
logger.warning('env.currclass is being referenced by an '
'extension; this API will be removed in the future',
location=self.docname)
warnings.warn('env.currclass is deprecated. '
'Use env.ref_context["py:class"] instead.',
RemovedInSphinx17Warning)
return self.ref_context.get('py:class')
def new_serialno(self, category=''):
@ -867,7 +886,7 @@ class BuildEnvironment(object):
doctree = self.get_doctree(docname)
# resolve all pending cross-references
self.resolve_references(doctree, docname, builder)
self.apply_post_transforms(doctree, docname)
# now, resolve all toctree nodes
for toctreenode in doctree.traverse(addnodes.toctree):
@ -901,113 +920,25 @@ class BuildEnvironment(object):
def resolve_references(self, doctree, fromdocname, builder):
# type: (nodes.Node, unicode, Builder) -> None
for node in doctree.traverse(addnodes.pending_xref):
contnode = node[0].deepcopy()
newnode = None
self.apply_post_transforms(doctree, fromdocname)
typ = node['reftype']
target = node['reftarget']
refdoc = node.get('refdoc', fromdocname)
domain = None
def apply_post_transforms(self, doctree, docname):
# type: (nodes.Node, unicode) -> None
"""Apply all post-transforms."""
try:
# set env.docname during applying post-transforms
self.temp_data['docname'] = docname
if hasattr(doctree, 'settings'):
doctree.settings.env = self
try:
if 'refdomain' in node and node['refdomain']:
# let the domain try to resolve the reference
try:
domain = self.domains[node['refdomain']]
except KeyError:
raise NoUri
newnode = domain.resolve_xref(self, refdoc, builder,
typ, target, node, contnode)
# really hardwired reference types
elif typ == 'any':
newnode = self._resolve_any_reference(builder, refdoc, node, contnode)
# no new node found? try the missing-reference event
if newnode is None:
newnode = builder.app.emit_firstresult(
'missing-reference', self, node, contnode)
# still not found? warn if node wishes to be warned about or
# we are in nit-picky mode
if newnode is None:
self._warn_missing_reference(refdoc, typ, target, node, domain)
except NoUri:
newnode = contnode
node.replace_self(newnode or contnode)
# remove only-nodes that do not belong to our builder
process_only_nodes(doctree, builder.tags)
transformer = SphinxTransformer(doctree)
transformer.add_transforms(self.app.post_transforms)
transformer.apply_transforms()
finally:
self.temp_data.clear()
# allow custom references to be resolved
builder.app.emit('doctree-resolved', doctree, fromdocname)
def _warn_missing_reference(self, refdoc, typ, target, node, domain):
# type: (unicode, unicode, unicode, nodes.Node, Domain) -> None
warn = node.get('refwarn')
if self.config.nitpicky:
warn = True
if self._nitpick_ignore:
dtype = domain and '%s:%s' % (domain.name, typ) or typ
if (dtype, target) in self._nitpick_ignore:
warn = False
# for "std" types also try without domain name
if (not domain or domain.name == 'std') and \
(typ, target) in self._nitpick_ignore:
warn = False
if not warn:
return
if domain and typ in domain.dangling_warnings:
msg = domain.dangling_warnings[typ]
elif node.get('refdomain', 'std') not in ('', 'std'):
msg = '%s:%s reference target not found: %%(target)s' % \
(node['refdomain'], typ)
else:
msg = '%r reference target not found: %%(target)s' % typ
logger.warning(msg % {'target': target},
location=node, type='ref', subtype=typ)
def _resolve_any_reference(self, builder, refdoc, node, contnode):
# type: (Builder, unicode, nodes.Node, nodes.Node) -> nodes.Node
"""Resolve reference generated by the "any" role."""
target = node['reftarget']
results = [] # type: List[Tuple[unicode, nodes.Node]]
# first, try resolving as :doc:
doc_ref = self.domains['std'].resolve_xref(self, refdoc, builder, 'doc',
target, node, contnode)
if doc_ref:
results.append(('doc', doc_ref))
# next, do the standard domain (makes this a priority)
results.extend(self.domains['std'].resolve_any_xref(
self, refdoc, builder, target, node, contnode))
for domain in self.domains.values():
if domain.name == 'std':
continue # we did this one already
try:
results.extend(domain.resolve_any_xref(self, refdoc, builder,
target, node, contnode))
except NotImplementedError:
# the domain doesn't yet support the new interface
# we have to manually collect possible references (SLOW)
for role in domain.roles:
res = domain.resolve_xref(self, refdoc, builder, role, target,
node, contnode)
if res and isinstance(res[0], nodes.Element):
results.append(('%s:%s' % (domain.name, role), res))
# now, see how many matches we got...
if not results:
return None
if len(results) > 1:
nice_results = ' or '.join(':%s:' % r[0] for r in results)
logger.warning('more than one target found for \'any\' cross-'
'reference %r: could be %s', target, nice_results,
location=node)
res_role, newnode = results[0]
# Override "any" class with the actual role type to get the styling
# approximately correct.
res_domain = res_role.split(':')[0]
if newnode and newnode[0].get('classes'):
newnode[0]['classes'].append(res_domain)
newnode[0]['classes'].append(res_role.replace(':', '-'))
return newnode
self.app.emit('doctree-resolved', doctree, docname)
def create_index(self, builder, group_entries=True,
_fixre=re.compile(r'(.*) ([(][^()]*[)])')):

View File

@ -5,6 +5,6 @@
Sphinx environment adapters
:copyright: Copyright 2007-2016 by the Sphinx team, see AUTHORS.
:copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""

View File

@ -5,7 +5,7 @@
Index entries adapters for sphinx.environment.
:copyright: Copyright 2007-2016 by the Sphinx team, see AUTHORS.
:copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
import re
@ -13,10 +13,10 @@ import bisect
import unicodedata
from itertools import groupby
from six import text_type
from six import text_type, iteritems
from sphinx.locale import _
from sphinx.util import iteritems, split_into, logging
from sphinx.util import split_into, logging
if False:
# For type annotation

View File

@ -5,7 +5,7 @@
Toctree adapter for sphinx.environment.
:copyright: Copyright 2007-2016 by the Sphinx team, see AUTHORS.
:copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
@ -15,7 +15,7 @@ from docutils import nodes
from sphinx import addnodes
from sphinx.util import url_re, logging
from sphinx.util.nodes import clean_astext, process_only_nodes
from sphinx.util.nodes import clean_astext
if False:
# For type annotation
@ -158,7 +158,7 @@ class TocTree(object):
maxdepth = self.env.metadata[ref].get('tocdepth', 0)
if ref not in toctree_ancestors or (prune and maxdepth > 0):
self._toctree_prune(toc, 2, maxdepth, collapse)
process_only_nodes(toc, builder.tags)
self.process_only_nodes(toc)
if title and toc.children and len(toc.children) == 1:
child = toc.children[0]
for refnode in child.traverse(nodes.reference):
@ -298,7 +298,7 @@ class TocTree(object):
# the document does not exist anymore: return a dummy node that
# renders to nothing
return nodes.paragraph()
process_only_nodes(toc, builder.tags)
self.process_only_nodes(toc)
for node in toc.traverse(nodes.reference):
node['refuri'] = node['anchorname'] or '#'
return toc
@ -323,3 +323,14 @@ class TocTree(object):
for toctree in toctrees[1:]:
result.extend(toctree.children)
return result
def process_only_nodes(self, doctree):
# type: (nodes.Node) -> None
# Lazy loading
from sphinx.transforms import SphinxTransformer
from sphinx.transforms.post_transforms import OnlyNodeTransform
transformer = SphinxTransformer(doctree)
transformer.set_environment(self.env)
transformer.add_transform(OnlyNodeTransform)
transformer.apply_transforms()

View File

@ -5,7 +5,7 @@
The data collector components for sphinx.environment.
:copyright: Copyright 2007-2016 by the Sphinx team, see AUTHORS.
:copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""

View File

@ -5,7 +5,7 @@
The image collector for sphinx.environment.
:copyright: Copyright 2007-2016 by the Sphinx team, see AUTHORS.
:copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
@ -143,6 +143,12 @@ class DownloadFileCollector(EnvironmentCollector):
def setup(app):
# type: (Sphinx) -> None
# type: (Sphinx) -> Dict
app.add_env_collector(ImageCollector)
app.add_env_collector(DownloadFileCollector)
return {
'version': 'builtin',
'parallel_read_safe': True,
'parallel_write_safe': True,
}

View File

@ -5,7 +5,7 @@
The dependencies collector components for sphinx.environment.
:copyright: Copyright 2007-2016 by the Sphinx team, see AUTHORS.
:copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
@ -18,7 +18,7 @@ from sphinx.environment.collectors import EnvironmentCollector
if False:
# For type annotation
from typing import Set # NOQA
from typing import Dict, Set # NOQA
from docutils import nodes # NOQA
from sphinx.sphinx import Sphinx # NOQA
from sphinx.environment import BuildEnvironment # NOQA
@ -56,5 +56,11 @@ class DependenciesCollector(EnvironmentCollector):
def setup(app):
# type: (Sphinx) -> None
# type: (Sphinx) -> Dict
app.add_env_collector(DependenciesCollector)
return {
'version': 'builtin',
'parallel_read_safe': True,
'parallel_write_safe': True,
}

View File

@ -5,7 +5,7 @@
Index entries collector for sphinx.environment.
:copyright: Copyright 2007-2016 by the Sphinx team, see AUTHORS.
:copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
@ -15,7 +15,7 @@ from sphinx.environment.collectors import EnvironmentCollector
if False:
# For type annotation
from typing import Set # NOQA
from typing import Dict, Set # NOQA
from docutils import nodes # NOQA
from sphinx.applicatin import Sphinx # NOQA
from sphinx.environment import BuildEnvironment # NOQA
@ -56,5 +56,11 @@ class IndexEntriesCollector(EnvironmentCollector):
def setup(app):
# type: (Sphinx) -> None
# type: (Sphinx) -> Dict
app.add_env_collector(IndexEntriesCollector)
return {
'version': 'builtin',
'parallel_read_safe': True,
'parallel_write_safe': True,
}

View File

@ -5,7 +5,7 @@
The metadata collector components for sphinx.environment.
:copyright: Copyright 2007-2016 by the Sphinx team, see AUTHORS.
:copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
@ -15,7 +15,7 @@ from sphinx.environment.collectors import EnvironmentCollector
if False:
# For type annotation
from typing import Set # NOQA
from typing import Dict, Set # NOQA
from docutils import nodes # NOQA
from sphinx.sphinx import Sphinx # NOQA
from sphinx.environment import BuildEnvironment # NOQA
@ -69,5 +69,11 @@ class MetadataCollector(EnvironmentCollector):
def setup(app):
# type: (Sphinx) -> None
# type: (Sphinx) -> Dict
app.add_env_collector(MetadataCollector)
return {
'version': 'builtin',
'parallel_read_safe': True,
'parallel_write_safe': True,
}

View File

@ -5,7 +5,7 @@
The title collector components for sphinx.environment.
:copyright: Copyright 2007-2016 by the Sphinx team, see AUTHORS.
:copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
@ -16,7 +16,7 @@ from sphinx.transforms import SphinxContentsFilter
if False:
# For type annotation
from typing import Set # NOQA
from typing import Dict, Set # NOQA
from docutils import nodes # NOQA
from sphinx.sphinx import Sphinx # NOQA
from sphinx.environment import BuildEnvironment # NOQA
@ -62,5 +62,11 @@ class TitleCollector(EnvironmentCollector):
def setup(app):
# type: (Sphinx) -> None
# type: (Sphinx) -> Dict
app.add_env_collector(TitleCollector)
return {
'version': 'builtin',
'parallel_read_safe': True,
'parallel_write_safe': True,
}

View File

@ -5,7 +5,7 @@
Toctree collector for sphinx.environment.
:copyright: Copyright 2007-2016 by the Sphinx team, see AUTHORS.
:copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
@ -284,5 +284,11 @@ class TocTreeCollector(EnvironmentCollector):
def setup(app):
# type: (Sphinx) -> None
# type: (Sphinx) -> Dict
app.add_env_collector(TocTreeCollector)
return {
'version': 'builtin',
'parallel_read_safe': True,
'parallel_write_safe': True,
}

View File

@ -6,7 +6,7 @@
Contains SphinxError and a few subclasses (in an extra module to avoid
circular import problems).
:copyright: Copyright 2007-2016 by the Sphinx team, see AUTHORS.
:copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""

86
sphinx/events.py Normal file
View File

@ -0,0 +1,86 @@
# -*- coding: utf-8 -*-
"""
sphinx.events
~~~~~~~~~~~~~
Sphinx core events.
Gracefully adapted from the TextPress system by Armin.
:copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
from __future__ import print_function
from collections import OrderedDict, defaultdict
from six import itervalues
from sphinx.errors import ExtensionError
from sphinx.locale import _
if False:
# For type annotation
from typing import Any, Callable, Dict, List # NOQA
# List of all known core events. Maps name to arguments description.
core_events = {
'builder-inited': '',
'env-get-outdated': 'env, added, changed, removed',
'env-get-updated': 'env',
'env-purge-doc': 'env, docname',
'env-before-read-docs': 'env, docnames',
'source-read': 'docname, source text',
'doctree-read': 'the doctree before being pickled',
'env-merge-info': 'env, read docnames, other env instance',
'missing-reference': 'env, node, contnode',
'doctree-resolved': 'doctree, docname',
'env-updated': 'env',
'html-collect-pages': 'builder',
'html-page-context': 'pagename, context, doctree or None',
'build-finished': 'exception',
} # type: Dict[unicode, unicode]
class EventManager(object):
def __init__(self):
# type: () -> None
self.events = core_events.copy()
self.listeners = defaultdict(OrderedDict) # type: Dict[unicode, Dict[int, Callable]]
self.next_listener_id = 0
def add(self, name):
# type: (unicode) -> None
if name in self.events:
raise ExtensionError(_('Event %r already present') % name)
self.events[name] = ''
def connect(self, name, callback):
# type: (unicode, Callable) -> int
if name not in self.events:
raise ExtensionError(_('Unknown event name: %s') % name)
listener_id = self.next_listener_id
self.next_listener_id += 1
self.listeners[name][listener_id] = callback
return listener_id
def disconnect(self, listener_id):
# type: (int) -> None
for event in itervalues(self.listeners):
event.pop(listener_id, None)
def emit(self, name, *args):
# type: (unicode, Any) -> List
results = []
for callback in itervalues(self.listeners[name]):
results.append(callback(*args))
return results
def emit_firstresult(self, name, *args):
# type: (unicode, Any) -> Any
for result in self.emit(name, *args):
if result is not None:
return result
return None

View File

@ -5,6 +5,6 @@
Contains Sphinx features not activated by default.
:copyright: Copyright 2007-2016 by the Sphinx team, see AUTHORS.
:copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""

View File

@ -7,7 +7,7 @@
the doctree, thus avoiding duplication between docstrings and documentation
for those who like elaborate docstrings.
:copyright: Copyright 2007-2016 by the Sphinx team, see AUTHORS.
:copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""

View File

@ -5,7 +5,7 @@
Allow reference sections by :ref: role using its title.
:copyright: Copyright 2007-2016 by the Sphinx team, see AUTHORS.
:copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""

View File

@ -49,7 +49,7 @@
resolved to a Python object, and otherwise it becomes simple emphasis.
This can be used as the default role to make links 'smart'.
:copyright: Copyright 2007-2016 by the Sphinx team, see AUTHORS.
:copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""

View File

@ -14,7 +14,7 @@
generate:
sphinx-autogen -o source/generated source/*.rst
:copyright: Copyright 2007-2016 by the Sphinx team, see AUTHORS.
:copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
from __future__ import print_function
@ -34,6 +34,7 @@ from sphinx.ext.autosummary import import_by_name, get_documenter
from sphinx.jinja2glue import BuiltinTemplateLoader
from sphinx.util.osutil import ensuredir
from sphinx.util.inspect import safe_getattr
from sphinx.util.rst import escape as rst_escape
# Add documenters to AutoDirective registry
from sphinx.ext.autodoc import add_documenter, \
@ -95,6 +96,12 @@ def _simple_warn(msg):
print('WARNING: ' + msg, file=sys.stderr)
def _underline(title, line='='):
if '\n' in title:
raise ValueError('Can only underline single lines')
return title + '\n' + line * len(title)
# -- Generating output ---------------------------------------------------------
def generate_autosummary_docs(sources, output_dir=None, suffix='.rst',
@ -130,6 +137,11 @@ def generate_autosummary_docs(sources, output_dir=None, suffix='.rst',
template_dirs.insert(0, template_dir)
template_loader = FileSystemLoader(template_dirs) # type: ignore
template_env = SandboxedEnvironment(loader=template_loader)
template_env.filters['underline'] = _underline
# replace the builtin html filters
template_env.filters['escape'] = rst_escape
template_env.filters['e'] = rst_escape
# read
items = find_autosummary_in_files(sources)

View File

@ -1,5 +1,4 @@
{{ fullname }}
{{ underline }}
{{ fullname | escape | underline}}
.. currentmodule:: {{ module }}

View File

@ -1,5 +1,4 @@
{{ fullname }}
{{ underline }}
{{ fullname | escape | underline}}
.. currentmodule:: {{ module }}

View File

@ -1,5 +1,4 @@
{{ fullname }}
{{ underline }}
{{ fullname | escape | underline}}
.. automodule:: {{ fullname }}

View File

@ -6,7 +6,7 @@
Check Python modules and C API for coverage. Mostly written by Josip
Dzolonga for the Google Highly Open Participation contest.
:copyright: Copyright 2007-2016 by the Sphinx team, see AUTHORS.
:copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""

View File

@ -6,7 +6,7 @@
Mimic doctest by automatically executing code snippets and checking
their results.
:copyright: Copyright 2007-2016 by the Sphinx team, see AUTHORS.
:copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
from __future__ import absolute_import

View File

@ -20,7 +20,7 @@
You can also give an explicit caption, e.g. :exmpl:`Foo <foo>`.
:copyright: Copyright 2007-2016 by the Sphinx team, see AUTHORS.
:copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""

View File

@ -5,7 +5,7 @@
To publish HTML docs at GitHub Pages, create .nojekyll file.
:copyright: Copyright 2007-2016 by the Sphinx team, see AUTHORS.
:copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""

View File

@ -6,7 +6,7 @@
Allow graphviz-formatted graphs to be included in Sphinx-generated
documents inline.
:copyright: Copyright 2007-2016 by the Sphinx team, see AUTHORS.
:copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""

View File

@ -16,7 +16,7 @@
namespace of the project configuration (that is, all variables from
``conf.py`` are available.)
:copyright: Copyright 2007-2016 by the Sphinx team, see AUTHORS.
:copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""

View File

@ -5,7 +5,7 @@
Render math in HTML via dvipng or dvisvgm.
:copyright: Copyright 2007-2016 by the Sphinx team, see AUTHORS.
:copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""

View File

@ -32,7 +32,7 @@ r"""
The graph is inserted as a PNG+image map into HTML and a PDF in
LaTeX.
:copyright: Copyright 2007-2016 by the Sphinx team, see AUTHORS.
:copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""

View File

@ -20,12 +20,13 @@
also be specified individually, e.g. if the docs should be buildable
without Internet access.
:copyright: Copyright 2007-2016 by the Sphinx team, see AUTHORS.
:copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
from __future__ import print_function
import sys
import time
import functools
import posixpath
@ -340,17 +341,27 @@ def setup(app):
return {'version': sphinx.__display_version__, 'parallel_read_safe': True}
if __name__ == '__main__':
# debug functionality to print out an inventory
import sys
def debug(argv):
# type: (List[unicode]) -> None
"""Debug functionality to print out an inventory"""
if len(argv) < 2:
print("Print out an inventory file.\n"
"Error: must specify local path or URL to an inventory file.",
file=sys.stderr)
sys.exit(1)
class MockConfig(object):
intersphinx_timeout = None # type: int
tls_verify = False
class MockApp(object):
srcdir = ''
config = MockConfig()
def warn(self, msg):
print(msg, file=sys.stderr)
filename = sys.argv[1]
filename = argv[1]
invdata = fetch_inventory(MockApp(), '', filename) # type: ignore
for key in sorted(invdata or {}):
print(key)
@ -358,3 +369,10 @@ if __name__ == '__main__':
print('\t%-40s %s%s' % (entry,
einfo[3] != '-' and '%-40s: ' % einfo[3] or '',
einfo[2]))
if __name__ == '__main__':
import logging # type: ignore
logging.basicConfig()
debug(argv=sys.argv) # type: ignore

View File

@ -6,7 +6,7 @@
Set up everything for use of JSMath to display math in HTML
via JavaScript.
:copyright: Copyright 2007-2016 by the Sphinx team, see AUTHORS.
:copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""

View File

@ -5,7 +5,7 @@
Add external links to module code in Python object descriptions.
:copyright: Copyright 2007-2016 by the Sphinx team, see AUTHORS.
:copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""

View File

@ -5,7 +5,7 @@
Set up math support in source files and LaTeX/text output.
:copyright: Copyright 2007-2016 by the Sphinx team, see AUTHORS.
:copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""

View File

@ -7,7 +7,7 @@
Sphinx's HTML writer -- requires the MathJax JavaScript library on your
webserver/computer.
:copyright: Copyright 2007-2016 by the Sphinx team, see AUTHORS.
:copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
@ -74,7 +74,7 @@ def setup(app):
# more information for mathjax secure url is here:
# http://docs.mathjax.org/en/latest/start.html#secure-access-to-the-cdn
app.add_config_value('mathjax_path',
'https://cdn.mathjax.org/mathjax/latest/MathJax.js?'
'https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.0/MathJax.js?'
'config=TeX-AMS-MML_HTMLorMML', False)
app.add_config_value('mathjax_inline', [r'\(', r'\)'], 'html')
app.add_config_value('mathjax_display', [r'\[', r'\]'], 'html')

View File

@ -5,7 +5,7 @@
Support for NumPy and Google style docstrings.
:copyright: Copyright 2007-2016 by the Sphinx team, see AUTHORS.
:copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""

View File

@ -7,7 +7,7 @@
Classes for docstring parsing and formatting.
:copyright: Copyright 2007-2016 by the Sphinx team, see AUTHORS.
:copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""

View File

@ -7,7 +7,7 @@
A collection of helpful iterators.
:copyright: Copyright 2007-2016 by the Sphinx team, see AUTHORS.
:copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""

View File

@ -6,7 +6,7 @@
Render math in HTML via dvipng. This extension has been deprecated; please
use sphinx.ext.imgmath instead.
:copyright: Copyright 2007-2016 by the Sphinx team, see AUTHORS.
:copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""

View File

@ -8,7 +8,7 @@
all todos of your project and lists them along with a backlink to the
original location.
:copyright: Copyright 2007-2016 by the Sphinx team, see AUTHORS.
:copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""

View File

@ -5,7 +5,7 @@
Add links to module code in Python object descriptions.
:copyright: Copyright 2007-2016 by the Sphinx team, see AUTHORS.
:copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""

120
sphinx/extension.py Normal file
View File

@ -0,0 +1,120 @@
# -*- coding: utf-8 -*-
"""
sphinx.extension
~~~~~~~~~~~~~~~~
Utilities for Sphinx extensions.
:copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
import traceback
from six import iteritems
from sphinx.errors import ExtensionError, VersionRequirementError
from sphinx.locale import _
from sphinx.util import logging
if False:
# For type annotation
from typing import Any, Dict # NOQA
from sphinx.application import Sphinx # NOQA
logger = logging.getLogger(__name__)
# list of deprecated extensions. Keys are extension name.
# Values are Sphinx version that merge the extension.
EXTENSION_BLACKLIST = {
"sphinxjp.themecore": "1.2"
} # type: Dict[unicode, unicode]
class Extension(object):
def __init__(self, name, module, **kwargs):
self.name = name
self.module = module
self.metadata = kwargs
self.version = kwargs.pop('version', 'unknown version')
# The extension supports parallel read or not. The default value
# is ``None``. It means the extension does not tell the status.
# It will be warned on parallel reading.
self.parallel_read_safe = kwargs.pop('parallel_read_safe', None)
# The extension supports parallel write or not. The default value
# is ``True``. Sphinx writes parallelly documents even if
# the extension does not tell its status.
self.parallel_write_safe = kwargs.pop('parallel_read_safe', True)
def load_extension(app, extname):
# type: (Sphinx, unicode) -> None
"""Load a Sphinx extension."""
if extname in app.extensions: # alread loaded
return
if extname in EXTENSION_BLACKLIST:
logger.warning(_('the extension %r was already merged with Sphinx since '
'version %s; this extension is ignored.'),
extname, EXTENSION_BLACKLIST[extname])
return
# update loading context
app._setting_up_extension.append(extname)
try:
mod = __import__(extname, None, None, ['setup'])
except ImportError as err:
logger.verbose(_('Original exception:\n') + traceback.format_exc())
raise ExtensionError(_('Could not import extension %s') % extname, err)
if not hasattr(mod, 'setup'):
logger.warning(_('extension %r has no setup() function; is it really '
'a Sphinx extension module?'), extname)
metadata = {} # type: Dict[unicode, Any]
else:
try:
metadata = mod.setup(app)
except VersionRequirementError as err:
# add the extension name to the version required
raise VersionRequirementError(
_('The %s extension used by this project needs at least '
'Sphinx v%s; it therefore cannot be built with this '
'version.') % (extname, err)
)
if metadata is None:
metadata = {}
if extname == 'rst2pdf.pdfbuilder':
metadata['parallel_read_safe'] = True
elif not isinstance(metadata, dict):
logger.warning(_('extension %r returned an unsupported object from '
'its setup() function; it should return None or a '
'metadata dictionary'), extname)
app.extensions[extname] = Extension(extname, mod, **metadata)
app._setting_up_extension.pop()
def verify_required_extensions(app, requirements):
# type: (Sphinx, Dict[unicode, unicode]) -> None
"""Verify the required Sphinx extensions are loaded."""
if requirements is None:
return
for extname, reqversion in iteritems(requirements):
extension = app.extensions.get(extname)
if extension is None:
logger.warning(_('needs_extensions config value specifies a '
'version requirement for extension %s, but it is '
'not loaded'), extname)
continue
if extension.version == 'unknown version' or reqversion > extension.version:
raise VersionRequirementError(_('This project needs the extension %s at least in '
'version %s and therefore cannot be built with '
'the loaded version (%s).') %
(extname, reqversion, extension.version))

View File

@ -5,7 +5,7 @@
Highlight code blocks using Pygments.
:copyright: Copyright 2007-2016 by the Sphinx team, see AUTHORS.
:copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""

View File

@ -5,7 +5,7 @@
Input/Output files
:copyright: Copyright 2007-2016 by the Sphinx team, see AUTHORS.
:copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
from docutils.io import FileInput

View File

@ -5,7 +5,7 @@
Glue code for the jinja2 templating engine.
:copyright: Copyright 2007-2016 by the Sphinx team, see AUTHORS.
:copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""

Some files were not shown because too many files have changed in this diff Show More