Merge branch 'master' into drop_websupport

This commit is contained in:
Takeshi KOMIYA 2017-04-23 19:18:01 +09:00
commit c50321d3ca
51 changed files with 1187 additions and 309 deletions

View File

@ -43,6 +43,7 @@ addons:
- texlive-xetex - texlive-xetex
- lmodern - lmodern
- latex-xcolor - latex-xcolor
- imagemagick
install: install:
- pip install -U pip setuptools - pip install -U pip setuptools
- pip install docutils==$DOCUTILS - pip install docutils==$DOCUTILS
@ -51,4 +52,4 @@ install:
script: script:
- flake8 - flake8
- if [[ $TRAVIS_PYTHON_VERSION == '3.6' ]]; then make style-check type-check test-async; fi - if [[ $TRAVIS_PYTHON_VERSION == '3.6' ]]; then make style-check type-check test-async; fi
- if [[ $TRAVIS_PYTHON_VERSION != '3.6' ]]; then make test; fi - if [[ $TRAVIS_PYTHON_VERSION != '3.6' ]]; then SKIP_LATEX_BUILD=1 make test; fi

View File

@ -12,6 +12,7 @@ Other co-maintainers:
* Robert Lehmann <@lehmannro> * Robert Lehmann <@lehmannro>
* Roland Meister <@rolmei> * Roland Meister <@rolmei>
* Takeshi Komiya <@tk0miya> * Takeshi Komiya <@tk0miya>
* Jean-François Burnol <@jfbu>
* Yoshiki Shibukawa <@shibu_jp> * Yoshiki Shibukawa <@shibu_jp>
Other contributors, listed alphabetically, are: Other contributors, listed alphabetically, are:
@ -21,7 +22,6 @@ Other contributors, listed alphabetically, are:
* Jakob Lykke Andersen -- Rewritten C++ domain * Jakob Lykke Andersen -- Rewritten C++ domain
* Henrique Bastos -- SVG support for graphviz extension * Henrique Bastos -- SVG support for graphviz extension
* Daniel Bültmann -- todo extension * Daniel Bültmann -- todo extension
* Jean-François Burnol -- LaTeX improvements
* Marco Buttu -- doctest extension (pyversion option) * Marco Buttu -- doctest extension (pyversion option)
* Etienne Desautels -- apidoc module * Etienne Desautels -- apidoc module
* Michael Droettboom -- inheritance_diagram extension * Michael Droettboom -- inheritance_diagram extension

View File

@ -45,6 +45,7 @@ Incompatible changes
* ``Builder.env`` is not filled at instantiation * ``Builder.env`` is not filled at instantiation
* #3594: LaTeX: single raw directive has been considered as block level element * #3594: LaTeX: single raw directive has been considered as block level element
* #3639: If ``html_experimental_html5_writer`` is available, epub builder use it by default. * #3639: If ``html_experimental_html5_writer`` is available, epub builder use it by default.
* ``Sphinx.add_source_parser()`` raises an error if duplicated
Features removed Features removed
---------------- ----------------
@ -117,6 +118,8 @@ Features added
* #3641: Epub theme supports HTML structures that are generated by HTML5 writer. * #3641: Epub theme supports HTML structures that are generated by HTML5 writer.
* #3644 autodoc uses inspect instead of checking types. Thanks to * #3644 autodoc uses inspect instead of checking types. Thanks to
Jeroen Demeyer. Jeroen Demeyer.
* Add a new extension; ``sphinx.ext.imgconverter``. It converts images in the
document to appropriate format for builders
Bugs fixed Bugs fixed
---------- ----------
@ -131,6 +134,8 @@ Bugs fixed
* C++, properly look up ``any`` references. * C++, properly look up ``any`` references.
* #3624: sphinx.ext.intersphinx couldn't load inventories compressed with gzip * #3624: sphinx.ext.intersphinx couldn't load inventories compressed with gzip
* #3551: PDF information dictionary is lacking author and title data * #3551: PDF information dictionary is lacking author and title data
* #3351: intersphinx does not refers context like ``py:module``, ``py:class``
and so on.
* Fail to load template file if the parent template is archived * Fail to load template file if the parent template is archived
Deprecated Deprecated
@ -146,6 +151,8 @@ Deprecated
instead (as Sphinx does since 1.5.) instead (as Sphinx does since 1.5.)
* ``Sphinx.status_iterator()`` and ``Sphinx.old_status_iterator()`` is now * ``Sphinx.status_iterator()`` and ``Sphinx.old_status_iterator()`` is now
deprecated. Please use ``sphinx.util:status_iterator()`` intead. deprecated. Please use ``sphinx.util:status_iterator()`` intead.
* ``Sphinx._directive_helper()`` is deprecated. Please use
``sphinx.util.docutils.directive_helper()`` instead.
* ``BuildEnvironment.set_warnfunc()`` is now deprecated * ``BuildEnvironment.set_warnfunc()`` is now deprecated
* Following methods of ``BuildEnvironment`` is now deprecated. * Following methods of ``BuildEnvironment`` is now deprecated.
@ -165,6 +172,8 @@ Deprecated
* #3254: ``sphinx.websupport`` is now separated into independent package; * #3254: ``sphinx.websupport`` is now separated into independent package;
``sphinxcontrib-websupport``. ``sphinx.websupport`` will be removed in ``sphinxcontrib-websupport``. ``sphinx.websupport`` will be removed in
Sphinx-2.0. Sphinx-2.0.
* #3628: ``sphinx_themes`` entry_point is deprecated. Please use
``sphinx.html_themes`` instead.
Release 1.5.6 (in development) Release 1.5.6 (in development)
============================== ==============================

View File

@ -15,6 +15,7 @@ These extensions are built in and can be activated by respective entries in the
githubpages githubpages
graphviz graphviz
ifconfig ifconfig
imgconverter
inheritance inheritance
intersphinx intersphinx
linkcode linkcode

30
doc/ext/imgconverter.rst Normal file
View File

@ -0,0 +1,30 @@
.. highlight:: rest
:mod:`sphinx.ext.imgconverter` -- Convert images to appropriate format for builders
===================================================================================
.. module:: sphinx.ext.imgconverter
:synopsis: Convert images to appropriate format for builders
.. versionadded:: 1.6
This extension converts images in your document to appropriate format for builders.
For example, it allows you to use SVG images with LaTeX builder.
As a result, you don't mind what image format the builder supports.
Internally, this extension uses Imagemagick_ to convert images.
.. _Imagemagick: https://www.imagemagick.org/script/index.php
Configuration
-------------
.. confval:: image_converter
A path to :command:`convert` command. By default, the imgconverter uses
the command from search paths.
.. confval:: image_converter_args
Additional command-line arguments to give to :command:`convert`, as a list.
The default is an empty list ``[]``.

View File

@ -370,6 +370,13 @@ package.
.. versionadded:: 1.4 .. versionadded:: 1.4
.. method:: Sphinx.add_html_theme(name, theme_path)
Register a HTML Theme. The *name* is a name of theme, and *path* is a
full path to the theme (refs: :ref:`distribute-your-theme`).
.. versionadded:: 1.6
.. method:: Sphinx.add_env_collector(collector) .. method:: Sphinx.add_env_collector(collector)
Register an environment collector class (refs: :ref:`collector-api`) Register an environment collector class (refs: :ref:`collector-api`)

View File

@ -46,34 +46,14 @@ file :file:`blue.zip`, you can put it right in the directory containing
html_theme = "blue" html_theme = "blue"
html_theme_path = ["."] html_theme_path = ["."]
The third form provides your theme path dynamically to Sphinx if the The third form is a python package. If a theme you want to use is distributed
``setuptools`` package is installed. You can provide an entry point section as a python package, you can use it after installing::
called ``sphinx_themes`` in your setup.py file and write a ``get_path`` function
that has to return the directory with themes in it::
# 'setup.py' # installing theme package
$ pip install sphinxjp.themes.dotted
setup( # use it in your conf.py
... html_theme = "dotted"
entry_points = {
'sphinx_themes': [
'path = your_package:get_path',
]
},
...
)
# 'your_package.py'
from os import path
package_dir = path.abspath(path.dirname(__file__))
template_path = path.join(package_dir, 'themes')
def get_path():
return template_path
.. versionadded:: 1.2
'sphinx_themes' entry_points feature.
.. _builtin-themes: .. _builtin-themes:
@ -310,6 +290,48 @@ Python :mod:`ConfigParser` module) and has the following structure:
and are accessible from all templates as ``theme_<name>``. and are accessible from all templates as ``theme_<name>``.
.. _distribute-your-theme:
Distribute your theme as a python package
-----------------------------------------
As a way to distribute your theme, you can use python package. Python package
brings to users easy setting up ways.
To distribute your theme as a python package, please define an entry point
called ``sphinx.html_themes`` in your setup.py file, and write a ``setup()``
function to register your themes using ``add_html_theme()`` API in it::
# 'setup.py'
setup(
...
entry_points = {
'sphinx.html_themes': [
'name_of_theme = your_package',
]
},
...
)
# 'your_package.py'
from os import path
def setup(app):
app.add_html_theme('name_of_theme', path.abspath(path.dirname(__file__)))
If your theme package contains two or more themes, please call ``add_html_theme()``
twice or more.
.. versionadded:: 1.2
'sphinx_themes' entry_points feature.
.. deprecated:: 1.6
``sphinx_themes`` entry_points has been deprecated.
.. versionadded:: 1.6
``sphinx.html_themes`` entry_points feature.
Templating Templating
~~~~~~~~~~ ~~~~~~~~~~

View File

@ -14,7 +14,6 @@ from __future__ import print_function
import os import os
import sys import sys
import types
import warnings import warnings
import posixpath import posixpath
from os import path from os import path
@ -24,24 +23,19 @@ from six import iteritems
from six.moves import cStringIO from six.moves import cStringIO
from docutils import nodes from docutils import nodes
from docutils.parsers.rst import convert_directive_function, \ from docutils.parsers.rst import directives, roles
directives, roles
from pkg_resources import iter_entry_points
import sphinx import sphinx
from sphinx import package_dir, locale from sphinx import package_dir, locale
from sphinx.config import Config from sphinx.config import Config
from sphinx.errors import SphinxError, ExtensionError, VersionRequirementError, \ from sphinx.errors import ConfigError, ExtensionError, VersionRequirementError
ConfigError
from sphinx.domains import ObjType
from sphinx.domains.std import GenericObject, Target, StandardDomain
from sphinx.deprecation import RemovedInSphinx17Warning, RemovedInSphinx20Warning from sphinx.deprecation import RemovedInSphinx17Warning, RemovedInSphinx20Warning
from sphinx.environment import BuildEnvironment from sphinx.environment import BuildEnvironment
from sphinx.events import EventManager from sphinx.events import EventManager
from sphinx.extension import load_extension, verify_required_extensions from sphinx.extension import verify_required_extensions
from sphinx.io import SphinxStandaloneReader from sphinx.io import SphinxStandaloneReader
from sphinx.locale import _ from sphinx.locale import _
from sphinx.roles import XRefRole from sphinx.registry import SphinxComponentRegistry
from sphinx.util import pycompat # noqa: F401 from sphinx.util import pycompat # noqa: F401
from sphinx.util import import_object from sphinx.util import import_object
from sphinx.util import logging from sphinx.util import logging
@ -49,7 +43,7 @@ from sphinx.util import status_iterator, old_status_iterator, display_chunk
from sphinx.util.tags import Tags from sphinx.util.tags import Tags
from sphinx.util.osutil import ENOENT from sphinx.util.osutil import ENOENT
from sphinx.util.console import bold, darkgreen # type: ignore from sphinx.util.console import bold, darkgreen # type: ignore
from sphinx.util.docutils import is_html5_writer_available from sphinx.util.docutils import is_html5_writer_available, directive_helper
from sphinx.util.i18n import find_catalog_source_files from sphinx.util.i18n import find_catalog_source_files
if False: if False:
@ -121,12 +115,10 @@ class Sphinx(object):
# type: (unicode, unicode, unicode, unicode, unicode, Dict, IO, IO, bool, bool, List[unicode], int, int) -> None # NOQA # type: (unicode, unicode, unicode, unicode, unicode, Dict, IO, IO, bool, bool, List[unicode], int, int) -> None # NOQA
self.verbosity = verbosity self.verbosity = verbosity
self.extensions = {} # type: Dict[unicode, Extension] self.extensions = {} # type: Dict[unicode, Extension]
self._additional_source_parsers = {} # type: Dict[unicode, Parser]
self._setting_up_extension = ['?'] # type: List[unicode] self._setting_up_extension = ['?'] # type: List[unicode]
self.domains = {} # type: Dict[unicode, Type[Domain]]
self.builderclasses = {} # type: Dict[unicode, Type[Builder]]
self.builder = None # type: Builder self.builder = None # type: Builder
self.env = None # type: BuildEnvironment self.env = None # type: BuildEnvironment
self.registry = SphinxComponentRegistry()
self.enumerable_nodes = {} # type: Dict[nodes.Node, Tuple[unicode, Callable]] # NOQA self.enumerable_nodes = {} # type: Dict[nodes.Node, Tuple[unicode, Callable]] # NOQA
self.post_transforms = [] # type: List[Transform] self.post_transforms = [] # type: List[Transform]
self.html_themes = {} # type: Dict[unicode, unicode] self.html_themes = {} # type: Dict[unicode, unicode]
@ -154,7 +146,6 @@ class Sphinx(object):
logging.setup(self, self._status, self._warning) logging.setup(self, self._status, self._warning)
self.events = EventManager() self.events = EventManager()
self._translators = {} # type: Dict[unicode, nodes.GenericNodeVisitor]
# keep last few messages for traceback # keep last few messages for traceback
# This will be filled by sphinx.util.logging.LastMessagesWriter # This will be filled by sphinx.util.logging.LastMessagesWriter
@ -226,9 +217,9 @@ class Sphinx(object):
verify_required_extensions(self, self.config.needs_extensions) verify_required_extensions(self, self.config.needs_extensions)
# check primary_domain if requested # check primary_domain if requested
if self.config.primary_domain and self.config.primary_domain not in self.domains: primary_domain = self.config.primary_domain
logger.warning(_('primary_domain %r not found, ignored.'), if primary_domain and not self.registry.has_domain(primary_domain):
self.config.primary_domain) logger.warning(_('primary_domain %r not found, ignored.'), primary_domain)
# create the builder # create the builder
self.builder = self.create_builder(buildername) self.builder = self.create_builder(buildername)
@ -271,28 +262,28 @@ class Sphinx(object):
def _init_source_parsers(self): def _init_source_parsers(self):
# type: () -> None # type: () -> None
for suffix, parser in iteritems(self._additional_source_parsers): for suffix, parser in iteritems(self.config.source_parsers):
self.add_source_parser(suffix, parser)
for suffix, parser in iteritems(self.registry.get_source_parsers()):
if suffix not in self.config.source_suffix: if suffix not in self.config.source_suffix:
self.config.source_suffix.append(suffix) self.config.source_suffix.append(suffix)
if suffix not in self.config.source_parsers:
self.config.source_parsers[suffix] = parser
def _init_env(self, freshenv): def _init_env(self, freshenv):
# type: (bool) -> None # type: (bool) -> None
if freshenv: if freshenv:
self.env = BuildEnvironment(self) self.env = BuildEnvironment(self)
self.env.find_files(self.config, self.builder) self.env.find_files(self.config, self.builder)
for domain in self.domains.keys(): for domain in self.registry.create_domains(self.env):
self.env.domains[domain] = self.domains[domain](self.env) self.env.domains[domain.name] = domain
else: else:
try: try:
logger.info(bold(_('loading pickled environment... ')), nonl=True) logger.info(bold(_('loading pickled environment... ')), nonl=True)
filename = path.join(self.doctreedir, ENV_PICKLE_FILENAME) filename = path.join(self.doctreedir, ENV_PICKLE_FILENAME)
self.env = BuildEnvironment.frompickle(filename, self) self.env = BuildEnvironment.frompickle(filename, self)
self.env.domains = {} self.env.domains = {}
for domain in self.domains.keys(): for domain in self.registry.create_domains(self.env):
# this can raise if the data version doesn't fit # this can raise if the data version doesn't fit
self.env.domains[domain] = self.domains[domain](self.env) self.env.domains[domain.name] = domain
logger.info(_('done')) logger.info(_('done'))
except Exception as err: except Exception as err:
if isinstance(err, IOError) and err.errno == ENOENT: if isinstance(err, IOError) and err.errno == ENOENT:
@ -301,28 +292,17 @@ class Sphinx(object):
logger.info(_('failed: %s'), err) logger.info(_('failed: %s'), err)
self._init_env(freshenv=True) self._init_env(freshenv=True)
def preload_builder(self, buildername): def preload_builder(self, name):
# type: (unicode) -> None # type: (unicode) -> None
if buildername is None: self.registry.preload_builder(self, name)
return
if buildername not in self.builderclasses: def create_builder(self, name):
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)
def create_builder(self, buildername):
# type: (unicode) -> Builder # type: (unicode) -> Builder
if buildername is None: if name is None:
buildername = 'html' logger.info(_('No builder selected, using default: html'))
if buildername not in self.builderclasses: name = 'html'
raise SphinxError(_('Builder name %s not registered') % buildername)
return self.builderclasses[buildername](self) return self.registry.create_builder(self, name)
def _init_builder(self): def _init_builder(self):
# type: () -> None # type: () -> None
@ -472,7 +452,7 @@ class Sphinx(object):
# type: (unicode) -> None # type: (unicode) -> None
"""Import and setup a Sphinx extension module. No-op if called twice.""" """Import and setup a Sphinx extension module. No-op if called twice."""
logger.debug('[app] setting up extension: %r', extname) logger.debug('[app] setting up extension: %r', extname)
load_extension(self, extname) self.registry.load_extension(self, extname)
def require_sphinx(self, version): def require_sphinx(self, version):
# type: (unicode) -> None # type: (unicode) -> None
@ -516,13 +496,7 @@ class Sphinx(object):
def add_builder(self, builder): def add_builder(self, builder):
# type: (Type[Builder]) -> None # type: (Type[Builder]) -> None
logger.debug('[app] adding builder: %r', builder) logger.debug('[app] adding builder: %r', builder)
if not hasattr(builder, 'name'): self.registry.add_builder(builder)
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__))
self.builderclasses[builder.name] = builder
def add_config_value(self, name, default, rebuild, types=()): def add_config_value(self, name, default, rebuild, types=()):
# type: (unicode, Any, Union[bool, unicode], Any) -> None # type: (unicode, Any, Union[bool, unicode], Any) -> None
@ -540,9 +514,9 @@ class Sphinx(object):
self.events.add(name) self.events.add(name)
def set_translator(self, name, translator_class): def set_translator(self, name, translator_class):
# type: (unicode, Any) -> None # type: (unicode, Type[nodes.NodeVisitor]) -> 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 self.registry.add_translator(name, translator_class)
def add_node(self, node, **kwds): def add_node(self, node, **kwds):
# type: (nodes.Node, Any) -> None # type: (nodes.Node, Any) -> None
@ -560,7 +534,7 @@ class Sphinx(object):
except ValueError: except ValueError:
raise ExtensionError(_('Value for key %r must be a ' raise ExtensionError(_('Value for key %r must be a '
'(visit, depart) function tuple') % key) '(visit, depart) function tuple') % key)
translator = self._translators.get(key) translator = self.registry.translators.get(key)
translators = [] translators = []
if translator is not None: if translator is not None:
translators.append(translator) translators.append(translator)
@ -593,21 +567,15 @@ class Sphinx(object):
self.enumerable_nodes[node] = (figtype, title_getter) self.enumerable_nodes[node] = (figtype, title_getter)
self.add_node(node, **kwds) self.add_node(node, **kwds)
def _directive_helper(self, obj, content=None, arguments=None, **options): def _directive_helper(self, obj, has_content=None, argument_spec=None, **option_spec):
# type: (Any, unicode, Any, Any) -> Any # type: (Any, bool, Tuple[int, int, bool], Any) -> Any
if isinstance(obj, (types.FunctionType, types.MethodType)): warnings.warn('_directive_helper() is now deprecated. '
obj.content = content # type: ignore 'Please use sphinx.util.docutils.directive_helper() instead.',
obj.arguments = arguments or (0, 0, False) # type: ignore RemovedInSphinx17Warning)
obj.options = options # type: ignore return directive_helper(obj, has_content, argument_spec, **option_spec)
return convert_directive_function(obj)
else:
if content or arguments or options:
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): def add_directive(self, name, obj, content=None, arguments=None, **options):
# type: (unicode, Any, unicode, Any, Any) -> None # type: (unicode, Any, bool, Tuple[int, int, bool], Any) -> None
logger.debug('[app] adding directive: %r', logger.debug('[app] adding directive: %r',
(name, obj, content, arguments, options)) (name, obj, content, arguments, options))
if name in directives._directives: if name in directives._directives:
@ -615,8 +583,8 @@ class Sphinx(object):
'already registered, it will be overridden'), 'already registered, it will be overridden'),
self._setting_up_extension[-1], name, self._setting_up_extension[-1], name,
type='app', subtype='add_directive') type='app', subtype='add_directive')
directives.register_directive( directive = directive_helper(obj, content, arguments, **options)
name, self._directive_helper(obj, content, arguments, **options)) directives.register_directive(name, directive)
def add_role(self, name, role): def add_role(self, name, role):
# type: (unicode, Any) -> None # type: (unicode, Any) -> None
@ -644,43 +612,30 @@ class Sphinx(object):
def add_domain(self, domain): def add_domain(self, domain):
# type: (Type[Domain]) -> None # type: (Type[Domain]) -> None
logger.debug('[app] adding domain: %r', domain) logger.debug('[app] adding domain: %r', domain)
if domain.name in self.domains: self.registry.add_domain(domain)
raise ExtensionError(_('domain %s already registered') % domain.name)
self.domains[domain.name] = domain
def override_domain(self, domain): def override_domain(self, domain):
# type: (Type[Domain]) -> None # type: (Type[Domain]) -> None
logger.debug('[app] overriding domain: %r', domain) logger.debug('[app] overriding domain: %r', domain)
if domain.name not in self.domains: self.registry.override_domain(domain)
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)
self.domains[domain.name] = domain
def add_directive_to_domain(self, domain, name, obj, def add_directive_to_domain(self, domain, name, obj,
content=None, arguments=None, **options): has_content=None, argument_spec=None, **option_spec):
# type: (unicode, unicode, Any, unicode, Any, Any) -> None # type: (unicode, unicode, Any, bool, Any, Any) -> None
logger.debug('[app] adding directive to domain: %r', logger.debug('[app] adding directive to domain: %r',
(domain, name, obj, content, arguments, options)) (domain, name, obj, has_content, argument_spec, option_spec))
if domain not in self.domains: self.registry.add_directive_to_domain(domain, name, obj,
raise ExtensionError(_('domain %s not yet registered') % domain) has_content, argument_spec, **option_spec)
self.domains[domain].directives[name] = \
self._directive_helper(obj, content, arguments, **options)
def add_role_to_domain(self, domain, name, role): def add_role_to_domain(self, domain, name, role):
# type: (unicode, unicode, Any) -> None # type: (unicode, unicode, Any) -> None
logger.debug('[app] adding role to domain: %r', (domain, name, role)) logger.debug('[app] adding role to domain: %r', (domain, name, role))
if domain not in self.domains: self.registry.add_role_to_domain(domain, name, role)
raise ExtensionError(_('domain %s not yet registered') % domain)
self.domains[domain].roles[name] = role
def add_index_to_domain(self, domain, index): def add_index_to_domain(self, domain, index):
# type: (unicode, Type[Index]) -> None # type: (unicode, Type[Index]) -> None
logger.debug('[app] adding index to domain: %r', (domain, index)) logger.debug('[app] adding index to domain: %r', (domain, index))
if domain not in self.domains: self.registry.add_index_to_domain(domain, index)
raise ExtensionError(_('domain %s not yet registered') % domain)
self.domains[domain].indices.append(index)
def add_object_type(self, directivename, rolename, indextemplate='', def add_object_type(self, directivename, rolename, indextemplate='',
parse_node=None, ref_nodeclass=None, objname='', parse_node=None, ref_nodeclass=None, objname='',
@ -689,19 +644,18 @@ class Sphinx(object):
logger.debug('[app] adding object type: %r', logger.debug('[app] adding object type: %r',
(directivename, rolename, indextemplate, parse_node, (directivename, rolename, indextemplate, parse_node,
ref_nodeclass, objname, doc_field_types)) ref_nodeclass, objname, doc_field_types))
StandardDomain.object_types[directivename] = \ self.registry.add_object_type(directivename, rolename, indextemplate, parse_node,
ObjType(objname or directivename, rolename) ref_nodeclass, objname, doc_field_types)
# create a subclass of GenericObject as the new directive
new_directive = type(directivename, (GenericObject, object), # type: ignore
{'indextemplate': indextemplate,
'parse_node': staticmethod(parse_node), # type: ignore
'doc_field_types': doc_field_types})
StandardDomain.directives[directivename] = new_directive
# XXX support more options?
StandardDomain.roles[rolename] = XRefRole(innernodeclass=ref_nodeclass)
# backwards compatible alias def add_description_unit(self, directivename, rolename, indextemplate='',
add_description_unit = add_object_type parse_node=None, ref_nodeclass=None, objname='',
doc_field_types=[]):
# type: (unicode, unicode, unicode, Callable, nodes.Node, unicode, List) -> None
warnings.warn('app.add_description_unit() is now deprecated. '
'Use app.add_object_type() instead.',
RemovedInSphinx20Warning)
self.add_object_type(directivename, rolename, indextemplate, parse_node,
ref_nodeclass, objname, doc_field_types)
def add_crossref_type(self, directivename, rolename, indextemplate='', def add_crossref_type(self, directivename, rolename, indextemplate='',
ref_nodeclass=None, objname=''): ref_nodeclass=None, objname=''):
@ -709,14 +663,8 @@ class Sphinx(object):
logger.debug('[app] adding crossref type: %r', logger.debug('[app] adding crossref type: %r',
(directivename, rolename, indextemplate, ref_nodeclass, (directivename, rolename, indextemplate, ref_nodeclass,
objname)) objname))
StandardDomain.object_types[directivename] = \ self.registry.add_crossref_type(directivename, rolename,
ObjType(objname or directivename, rolename) indextemplate, ref_nodeclass, objname)
# create a subclass of Target as the new directive
new_directive = type(directivename, (Target, object), # type: ignore
{'indextemplate': indextemplate})
StandardDomain.directives[directivename] = new_directive
# XXX support more options?
StandardDomain.roles[rolename] = XRefRole(innernodeclass=ref_nodeclass)
def add_transform(self, transform): def add_transform(self, transform):
# type: (Type[Transform]) -> None # type: (Type[Transform]) -> None
@ -788,18 +736,18 @@ class Sphinx(object):
def add_source_parser(self, suffix, parser): def add_source_parser(self, suffix, parser):
# type: (unicode, Parser) -> None # type: (unicode, Parser) -> None
logger.debug('[app] adding search source_parser: %r, %r', suffix, parser) logger.debug('[app] adding search source_parser: %r, %r', suffix, parser)
if suffix in self._additional_source_parsers: self.registry.add_source_parser(suffix, parser)
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
def add_env_collector(self, collector): def add_env_collector(self, collector):
# type: (Type[EnvironmentCollector]) -> None # type: (Type[EnvironmentCollector]) -> None
logger.debug('[app] adding environment collector: %r', collector) logger.debug('[app] adding environment collector: %r', collector)
collector().enable(self) collector().enable(self)
def add_html_theme(self, name, theme_path):
# type: (unicode, unicode) -> None
logger.debug('[app] adding HTML theme: %r, %r', name, theme_path)
self.html_themes[name] = theme_path
class TemplateBridge(object): class TemplateBridge(object):
""" """

View File

@ -11,6 +11,7 @@
import os import os
from os import path from os import path
import warnings
try: try:
import multiprocessing import multiprocessing
@ -20,6 +21,7 @@ except ImportError:
from six import itervalues from six import itervalues
from docutils import nodes from docutils import nodes
from sphinx.deprecation import RemovedInSphinx20Warning
from sphinx.util import i18n, path_stabilize, logging, status_iterator from sphinx.util import i18n, path_stabilize, logging, status_iterator
from sphinx.util.osutil import SEP, relative_uri from sphinx.util.osutil import SEP, relative_uri
from sphinx.util.i18n import find_catalog from sphinx.util.i18n import find_catalog
@ -53,6 +55,9 @@ class Builder(object):
name = '' # type: unicode name = '' # type: unicode
#: The 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 format = '' # type: unicode
# default translator class for the builder. This will be overrided by
# ``app.set_translator()``.
default_translator_class = None # type: nodes.NodeVisitor
# doctree versioning method # doctree versioning method
versioning_method = 'none' # type: unicode versioning_method = 'none' # type: unicode
versioning_compare = False versioning_compare = False
@ -101,9 +106,6 @@ class Builder(object):
self.parallel_ok = False self.parallel_ok = False
self.finish_tasks = None # type: Any self.finish_tasks = None # type: Any
# load default translator class
self.translator_class = app._translators.get(self.name)
def set_environment(self, env): def set_environment(self, env):
# type: (BuildEnvironment) -> None # type: (BuildEnvironment) -> None
"""Store BuildEnvironment object.""" """Store BuildEnvironment object."""
@ -111,6 +113,38 @@ class Builder(object):
self.env.set_versioning_method(self.versioning_method, self.env.set_versioning_method(self.versioning_method,
self.versioning_compare) self.versioning_compare)
def get_translator_class(self, *args):
# type: (Any) -> nodes.NodeVisitor
"""Return a class of translator."""
return self.app.registry.get_translator_class(self)
def create_translator(self, *args):
# type: (Any) -> nodes.NodeVisitor
"""Return an instance of translator.
This method returns an instance of ``default_translator_class`` by default.
Users can replace the translator class with ``app.set_translator()`` API.
"""
translator_class = self.app.registry.get_translator_class(self)
assert translator_class, "translator not found for %s" % self.__class__.__name__
return translator_class(*args)
@property
def translator_class(self):
# type: () -> Callable[[Any], nodes.NodeVisitor]
"""Return a class of translator.
.. deprecated:: 1.6
"""
translator_class = self.app.registry.get_translator_class(self)
if translator_class is None and self.default_translator_class is None:
warnings.warn('builder.translator_class() is now deprecated. '
'Please use builder.create_translator() and '
'builder.default_translator_class instead.',
RemovedInSphinx20Warning)
return None
return self.create_translator
# helper methods # helper methods
def init(self): def init(self):
# type: () -> None # type: () -> None

View File

@ -158,7 +158,6 @@ class StandaloneHTMLBuilder(Builder):
self.init_templates() self.init_templates()
self.init_highlighter() self.init_highlighter()
self.init_translator_class()
if self.config.html_file_suffix is not None: if self.config.html_file_suffix is not None:
self.out_suffix = self.config.html_file_suffix self.out_suffix = self.config.html_file_suffix
@ -218,23 +217,18 @@ class StandaloneHTMLBuilder(Builder):
self.highlighter = PygmentsBridge('html', style, self.highlighter = PygmentsBridge('html', style,
self.config.trim_doctest_flags) self.config.trim_doctest_flags)
def init_translator_class(self): @property
# type: () -> None def default_translator_class(self):
if self.translator_class is None: if self.config.html_experimental_html5_writer and html5_ready:
use_html5_writer = self.config.html_experimental_html5_writer if self.config.html_use_smartypants:
if use_html5_writer is None: return SmartyPantsHTML5Translator
use_html5_writer = self.default_html5_translator and html5_ready
if use_html5_writer and html5_ready:
if self.config.html_use_smartypants:
self.translator_class = SmartyPantsHTML5Translator
else:
self.translator_class = HTML5Translator
else: else:
if self.config.html_use_smartypants: return HTML5Translator
self.translator_class = SmartyPantsHTMLTranslator else:
else: if self.config.html_use_smartypants:
self.translator_class = HTMLTranslator return SmartyPantsHTMLTranslator
else:
return HTMLTranslator
def get_outdated_docs(self): def get_outdated_docs(self):
# type: () -> Iterator[unicode] # type: () -> Iterator[unicode]
@ -1200,7 +1194,6 @@ class SerializingHTMLBuilder(StandaloneHTMLBuilder):
self.current_docname = None self.current_docname = None
self.theme = None # no theme necessary self.theme = None # no theme necessary
self.templates = None # no template bridge necessary self.templates = None # no template bridge necessary
self.init_translator_class()
self.init_templates() self.init_templates()
self.init_highlighter() self.init_highlighter()
self.use_index = self.get_builder_config('use_index', 'html') self.use_index = self.get_builder_config('use_index', 'html')

View File

@ -31,7 +31,7 @@ from sphinx.util.nodes import inline_all_toctrees
from sphinx.util.fileutil import copy_asset_file from sphinx.util.fileutil import copy_asset_file
from sphinx.util.osutil import SEP, make_filename from sphinx.util.osutil import SEP, make_filename
from sphinx.util.console import bold, darkgreen # type: ignore from sphinx.util.console import bold, darkgreen # type: ignore
from sphinx.writers.latex import LaTeXWriter from sphinx.writers.latex import LaTeXWriter, LaTeXTranslator
if False: if False:
# For type annotation # For type annotation
@ -51,6 +51,7 @@ class LaTeXBuilder(Builder):
format = 'latex' format = 'latex'
supported_image_types = ['application/pdf', 'image/png', 'image/jpeg'] supported_image_types = ['application/pdf', 'image/png', 'image/jpeg']
supported_remote_images = False supported_remote_images = False
default_translator_class = LaTeXTranslator
def init(self): def init(self):
# type: () -> None # type: () -> None

View File

@ -23,7 +23,7 @@ from sphinx.util import logging
from sphinx.util.nodes import inline_all_toctrees from sphinx.util.nodes import inline_all_toctrees
from sphinx.util.osutil import make_filename from sphinx.util.osutil import make_filename
from sphinx.util.console import bold, darkgreen # type: ignore from sphinx.util.console import bold, darkgreen # type: ignore
from sphinx.writers.manpage import ManualPageWriter from sphinx.writers.manpage import ManualPageWriter, ManualPageTranslator
if False: if False:
# For type annotation # For type annotation
@ -40,6 +40,7 @@ class ManualPageBuilder(Builder):
""" """
name = 'man' name = 'man'
format = 'man' format = 'man'
default_translator_class = ManualPageTranslator
supported_image_types = [] # type: List[unicode] supported_image_types = [] # type: List[unicode]
def init(self): def init(self):

View File

@ -27,7 +27,7 @@ from sphinx.util.fileutil import copy_asset_file
from sphinx.util.nodes import inline_all_toctrees from sphinx.util.nodes import inline_all_toctrees
from sphinx.util.osutil import SEP, make_filename from sphinx.util.osutil import SEP, make_filename
from sphinx.util.console import bold, darkgreen # type: ignore from sphinx.util.console import bold, darkgreen # type: ignore
from sphinx.writers.texinfo import TexinfoWriter from sphinx.writers.texinfo import TexinfoWriter, TexinfoTranslator
if False: if False:
# For type annotation # For type annotation
@ -99,6 +99,7 @@ class TexinfoBuilder(Builder):
format = 'texinfo' format = 'texinfo'
supported_image_types = ['image/png', 'image/jpeg', supported_image_types = ['image/png', 'image/jpeg',
'image/gif'] 'image/gif']
default_translator_class = TexinfoTranslator
def init(self): def init(self):
# type: () -> None # type: () -> None

View File

@ -17,7 +17,7 @@ from docutils.io import StringOutput
from sphinx.builders import Builder from sphinx.builders import Builder
from sphinx.util import logging from sphinx.util import logging
from sphinx.util.osutil import ensuredir, os_path from sphinx.util.osutil import ensuredir, os_path
from sphinx.writers.text import TextWriter from sphinx.writers.text import TextWriter, TextTranslator
if False: if False:
# For type annotation # For type annotation
@ -33,6 +33,7 @@ class TextBuilder(Builder):
format = 'text' format = 'text'
out_suffix = '.txt' out_suffix = '.txt'
allow_parallel = True allow_parallel = True
default_translator_class = TextTranslator
current_docname = None # type: unicode current_docname = None # type: unicode

View File

@ -14,6 +14,7 @@ from os import path
from docutils import nodes from docutils import nodes
from docutils.io import StringOutput from docutils.io import StringOutput
from docutils.writers.docutils_xml import XMLTranslator
from sphinx.builders import Builder from sphinx.builders import Builder
from sphinx.util import logging from sphinx.util import logging
@ -38,6 +39,7 @@ class XMLBuilder(Builder):
allow_parallel = True allow_parallel = True
_writer_class = XMLWriter _writer_class = XMLWriter
default_translator_class = XMLTranslator
def init(self): def init(self):
# type: () -> None # type: () -> None

View File

@ -308,3 +308,8 @@ class Domain(object):
if primary: if primary:
return type.lname return type.lname
return _('%s %s') % (self.label, type.lname) return _('%s %s') % (self.label, type.lname)
def get_full_qualified_name(self, node):
# type: (nodes.Node) -> unicode
"""Return full qualified name for given node."""
return None

View File

@ -5032,6 +5032,20 @@ class CPPDomain(Domain):
newestId = symbol.declaration.get_newest_id() newestId = symbol.declaration.get_newest_id()
yield (name, name, objectType, docname, newestId, 1) yield (name, name, objectType, docname, newestId, 1)
def get_full_qualified_name(self, node):
# type: (nodes.Node) -> unicode
target = node.get('reftarget', None)
if target is None:
return None
parentKey = node.get("cpp:parent_key", None)
if parentKey is None:
return None
rootSymbol = self.data['root_symbol']
parentSymbol = rootSymbol.direct_lookup(parentKey)
parentName = parentSymbol.get_full_nested_name()
return '::'.join([text_type(parentName), target])
def setup(app): def setup(app):
# type: (Sphinx) -> Dict[unicode, Any] # type: (Sphinx) -> Dict[unicode, Any]

View File

@ -398,6 +398,16 @@ class JavaScriptDomain(Domain):
yield refname, refname, type, docname, \ yield refname, refname, type, docname, \
refname.replace('$', '_S_'), 1 refname.replace('$', '_S_'), 1
def get_full_qualified_name(self, node):
# type: (nodes.Node) -> unicode
modname = node.get('js:module')
prefix = node.get('js:object')
target = node.get('reftarget')
if target is None:
return None
else:
return '.'.join(filter(None, [modname, prefix, target]))
def setup(app): def setup(app):
# type: (Sphinx) -> Dict[unicode, Any] # type: (Sphinx) -> Dict[unicode, Any]

View File

@ -885,6 +885,16 @@ class PythonDomain(Domain):
if type != 'module': # modules are already handled if type != 'module': # modules are already handled
yield (refname, refname, type, docname, refname, 1) yield (refname, refname, type, docname, refname, 1)
def get_full_qualified_name(self, node):
# type: (nodes.Node) -> unicode
modname = node.get('py:module')
clsname = node.get('py:class')
target = node.get('reftarget')
if target is None:
return None
else:
return '.'.join(filter(None, [modname, clsname, target]))
def setup(app): def setup(app):
# type: (Sphinx) -> Dict[unicode, Any] # type: (Sphinx) -> Dict[unicode, Any]

View File

@ -849,7 +849,11 @@ class StandardDomain(Domain):
for doc in self.env.all_docs: for doc in self.env.all_docs:
yield (doc, clean_astext(self.env.titles[doc]), 'doc', doc, '', -1) yield (doc, clean_astext(self.env.titles[doc]), 'doc', doc, '', -1)
for (prog, option), info in iteritems(self.data['progoptions']): for (prog, option), info in iteritems(self.data['progoptions']):
yield (option, option, 'cmdoption', info[0], info[1], 1) if prog:
fullname = ".".join([prog, option])
yield (fullname, fullname, 'cmdoption', info[0], info[1], 1)
else:
yield (option, option, 'cmdoption', info[0], info[1], 1)
for (type, name), info in iteritems(self.data['objects']): for (type, name), info in iteritems(self.data['objects']):
yield (name, name, type, info[0], info[1], yield (name, name, type, info[0], info[1],
self.object_types[type].attrs['searchprio']) self.object_types[type].attrs['searchprio'])
@ -925,6 +929,15 @@ class StandardDomain(Domain):
# Maybe it is defined in orphaned document. # Maybe it is defined in orphaned document.
raise ValueError raise ValueError
def get_full_qualified_name(self, node):
# type: (nodes.Node) -> unicode
progname = node.get('std:program')
target = node.get('reftarget')
if progname is None or target is None:
return None
else:
return '.'.join([progname, target])
def setup(app): def setup(app):
# type: (Sphinx) -> Dict[unicode, Any] # type: (Sphinx) -> Dict[unicode, Any]

View File

@ -691,7 +691,8 @@ class BuildEnvironment(object):
codecs.register_error('sphinx', self.warn_and_replace) # type: ignore codecs.register_error('sphinx', self.warn_and_replace) # type: ignore
# publish manually # publish manually
reader = SphinxStandaloneReader(self.app, parsers=self.config.source_parsers) reader = SphinxStandaloneReader(self.app,
parsers=self.app.registry.get_source_parsers())
pub = Publisher(reader=reader, pub = Publisher(reader=reader,
writer=SphinxDummyWriter(), writer=SphinxDummyWriter(),
destination_class=NullOutput) destination_class=NullOutput)

View File

@ -573,7 +573,7 @@ def get_rst_suffix(app):
# type: (Sphinx) -> unicode # type: (Sphinx) -> unicode
def get_supported_format(suffix): def get_supported_format(suffix):
# type: (unicode) -> Tuple[unicode] # type: (unicode) -> Tuple[unicode]
parser_class = app.config.source_parsers.get(suffix) parser_class = app.registry.get_source_parsers().get(suffix)
if parser_class is None: if parser_class is None:
return ('restructuredtext',) return ('restructuredtext',)
if isinstance(parser_class, string_types): if isinstance(parser_class, string_types):

View File

@ -0,0 +1,93 @@
# -*- coding: utf-8 -*-
"""
sphinx.ext.imgconverter
~~~~~~~~~~~~~~~~~~~~~~~
Image converter extension for Sphinx
:copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
import subprocess
from sphinx.errors import ExtensionError
from sphinx.locale import _
from sphinx.transforms.post_transforms.images import ImageConverter
from sphinx.util import logging
from sphinx.util.osutil import ENOENT, EPIPE, EINVAL
if False:
# For type annotation
from typing import Any, Dict # NOQA
from sphinx.application import Sphinx # NOQA
logger = logging.getLogger(__name__)
class ImagemagickConverter(ImageConverter):
conversion_rules = [
('image/svg+xml', 'image/png'),
('application/pdf', 'image/png'),
]
def is_available(self):
# type: () -> bool
"""Confirms the converter is available or not."""
try:
args = [self.config.image_converter, '-version']
logger.debug('Invoking %r ...', args)
ret = subprocess.call(args, stdin=subprocess.PIPE, stdout=subprocess.PIPE)
if ret == 0:
return True
else:
return False
except (OSError, IOError):
logger.warning(_('convert command %r cannot be run.'
'check the image_converter setting'),
self.config.image_converter)
return False
def convert(self, _from, _to):
# type: (unicode, unicode) -> bool
"""Converts the image to expected one."""
try:
args = ([self.config.image_converter] +
self.config.image_converter_args +
[_from, _to])
logger.debug('Invoking %r ...', args)
p = subprocess.Popen(args, stdin=subprocess.PIPE, stdout=subprocess.PIPE)
except OSError as err:
if err.errno != ENOENT: # No such file or directory
raise
logger.warning(_('convert command %r cannot be run.'
'check the image_converter setting'),
self.config.image_converter)
return False
try:
stdout, stderr = p.communicate()
except (OSError, IOError) as err:
if err.errno not in (EPIPE, EINVAL):
raise
stdout, stderr = p.stdout.read(), p.stderr.read()
p.wait()
if p.returncode != 0:
raise ExtensionError(_('convert exited with error:\n'
'[stderr]\n%s\n[stdout]\n%s') %
(stderr, stdout))
return True
def setup(app):
# type: (Sphinx) -> Dict[unicode, Any]
app.add_post_transform(ImagemagickConverter)
app.add_config_value('image_converter', 'convert', 'env')
app.add_config_value('image_converter_args', [], 'env')
return {
'version': 'builtin',
'parallel_read_safe': True,
'parallel_write_safe': True,
}

View File

@ -292,6 +292,10 @@ def missing_reference(app, env, node, contnode):
# until Sphinx-1.6, cmdoptions are stored as std:option # until Sphinx-1.6, cmdoptions are stored as std:option
objtypes.append('std:option') objtypes.append('std:option')
to_try = [(inventories.main_inventory, target)] to_try = [(inventories.main_inventory, target)]
if domain:
full_qualified_name = env.get_domain(domain).get_full_qualified_name(node)
if full_qualified_name:
to_try.append((inventories.main_inventory, full_qualified_name))
in_set = None in_set = None
if ':' in target: if ':' in target:
# first part may be the foreign doc set name # first part may be the foreign doc set name
@ -299,6 +303,10 @@ def missing_reference(app, env, node, contnode):
if setname in inventories.named_inventory: if setname in inventories.named_inventory:
in_set = setname in_set = setname
to_try.append((inventories.named_inventory[setname], newtarget)) to_try.append((inventories.named_inventory[setname], newtarget))
if domain:
full_qualified_name = env.get_domain(domain).get_full_qualified_name(node)
if full_qualified_name:
to_try.append((inventories.named_inventory[setname], full_qualified_name))
for inventory, target in to_try: for inventory, target in to_try:
for objtype in objtypes: for objtype in objtypes:
if objtype not in inventory or target not in inventory[objtype]: if objtype not in inventory or target not in inventory[objtype]:

View File

@ -9,30 +9,20 @@
:license: BSD, see LICENSE for details. :license: BSD, see LICENSE for details.
""" """
import traceback
from six import iteritems from six import iteritems
from sphinx.errors import ExtensionError, VersionRequirementError from sphinx.errors import VersionRequirementError
from sphinx.locale import _ from sphinx.locale import _
from sphinx.util import logging from sphinx.util import logging
if False: if False:
# For type annotation # For type annotation
from typing import Any, Dict # NOQA from typing import Dict # NOQA
from sphinx.application import Sphinx # NOQA from sphinx.application import Sphinx # NOQA
logger = logging.getLogger(__name__) 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): class Extension(object):
def __init__(self, name, module, **kwargs): def __init__(self, name, module, **kwargs):
self.name = name self.name = name
@ -51,54 +41,6 @@ class Extension(object):
self.parallel_write_safe = kwargs.pop('parallel_read_safe', True) 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): def verify_required_extensions(app, requirements):
# type: (Sphinx, Dict[unicode, unicode]) -> None # type: (Sphinx, Dict[unicode, unicode]) -> None
"""Verify the required Sphinx extensions are loaded.""" """Verify the required Sphinx extensions are loaded."""

View File

@ -11,7 +11,7 @@
from docutils.io import FileInput from docutils.io import FileInput
from docutils.readers import standalone from docutils.readers import standalone
from docutils.writers import UnfilteredWriter from docutils.writers import UnfilteredWriter
from six import string_types, text_type from six import string_types, text_type, iteritems
from typing import Any, Union # NOQA from typing import Any, Union # NOQA
from sphinx.transforms import ( from sphinx.transforms import (
@ -158,9 +158,8 @@ class SphinxFileInput(FileInput):
# type: () -> unicode # type: () -> unicode
def get_parser_type(source_path): def get_parser_type(source_path):
# type: (unicode) -> Tuple[unicode] # type: (unicode) -> Tuple[unicode]
for suffix in self.env.config.source_parsers: for suffix, parser_class in iteritems(self.app.registry.get_source_parsers()):
if source_path.endswith(suffix): if source_path.endswith(suffix):
parser_class = self.env.config.source_parsers[suffix]
if isinstance(parser_class, string_types): if isinstance(parser_class, string_types):
parser_class = import_object(parser_class, 'source parser') # type: ignore # NOQA parser_class = import_object(parser_class, 'source parser') # type: ignore # NOQA
return parser_class.supported return parser_class.supported

226
sphinx/registry.py Normal file
View File

@ -0,0 +1,226 @@
# -*- coding: utf-8 -*-
"""
sphinx.registry
~~~~~~~~~~~~~~~
Sphinx component registry.
:copyright: Copyright 2007-2016 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
from __future__ import print_function
import traceback
from pkg_resources import iter_entry_points
from six import itervalues
from sphinx.errors import ExtensionError, SphinxError, VersionRequirementError
from sphinx.extension import Extension
from sphinx.domains import ObjType
from sphinx.domains.std import GenericObject, Target
from sphinx.locale import _
from sphinx.roles import XRefRole
from sphinx.util import logging
from sphinx.util.docutils import directive_helper
if False:
# For type annotation
from typing import Any, Callable, Dict, Iterator, List, Type # NOQA
from docutils import nodes # NOQA
from docutils.parsers import Parser # NOQA
from sphinx.application import Sphinx # NOQA
from sphinx.builders import Builder # NOQA
from sphinx.domains import Domain, Index # NOQA
from sphinx.environment import BuildEnvironment # 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 SphinxComponentRegistry(object):
def __init__(self):
self.builders = {} # type: Dict[unicode, Type[Builder]]
self.domains = {} # type: Dict[unicode, Type[Domain]]
self.source_parsers = {} # type: Dict[unicode, Parser]
self.translators = {} # type: Dict[unicode, nodes.NodeVisitor]
def add_builder(self, builder):
# type: (Type[Builder]) -> None
if not hasattr(builder, 'name'):
raise ExtensionError(_('Builder class %s has no "name" attribute') % builder)
if builder.name in self.builders:
raise ExtensionError(_('Builder %r already exists (in module %s)') %
(builder.name, self.builders[builder.name].__module__))
self.builders[builder.name] = builder
def preload_builder(self, app, name):
# type: (Sphinx, unicode) -> None
if name is None:
return
if name not in self.builders:
entry_points = iter_entry_points('sphinx.builders', name)
try:
entry_point = next(entry_points)
except StopIteration:
raise SphinxError(_('Builder name %s not registered or available'
' through entry point') % name)
self.load_extension(app, entry_point.module_name)
def create_builder(self, app, name):
# type: (Sphinx, unicode) -> Builder
if name not in self.builders:
raise SphinxError(_('Builder name %s not registered') % name)
return self.builders[name](app)
def add_domain(self, domain):
# type: (Type[Domain]) -> None
if domain.name in self.domains:
raise ExtensionError(_('domain %s already registered') % domain.name)
self.domains[domain.name] = domain
def has_domain(self, domain):
# type: (unicode) -> bool
return domain in self.domains
def create_domains(self, env):
# type: (BuildEnvironment) -> Iterator[Domain]
for DomainClass in itervalues(self.domains):
yield DomainClass(env)
def override_domain(self, domain):
# type: (Type[Domain]) -> None
if domain.name not in self.domains:
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)
self.domains[domain.name] = domain
def add_directive_to_domain(self, domain, name, obj,
has_content=None, argument_spec=None, **option_spec):
# type: (unicode, unicode, Any, bool, Any, Any) -> None
if domain not in self.domains:
raise ExtensionError(_('domain %s not yet registered') % domain)
directive = directive_helper(obj, has_content, argument_spec, **option_spec)
self.domains[domain].directives[name] = directive
def add_role_to_domain(self, domain, name, role):
# type: (unicode, unicode, Any) -> None
if domain not in self.domains:
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
if domain not in self.domains:
raise ExtensionError(_('domain %s not yet registered') % domain)
self.domains[domain].indices.append(index)
def add_object_type(self, directivename, rolename, indextemplate='',
parse_node=None, ref_nodeclass=None, objname='',
doc_field_types=[]):
# type: (unicode, unicode, unicode, Callable, nodes.Node, unicode, List) -> None
# create a subclass of GenericObject as the new directive
directive = type(directivename, # type: ignore
(GenericObject, object),
{'indextemplate': indextemplate,
'parse_node': staticmethod(parse_node), # type: ignore
'doc_field_types': doc_field_types})
stddomain = self.domains['std']
stddomain.directives[directivename] = directive
stddomain.roles[rolename] = XRefRole(innernodeclass=ref_nodeclass)
stddomain.object_types[directivename] = ObjType(objname or directivename, rolename)
def add_crossref_type(self, directivename, rolename, indextemplate='',
ref_nodeclass=None, objname=''):
# type: (unicode, unicode, unicode, nodes.Node, unicode) -> None
# create a subclass of Target as the new directive
directive = type(directivename, # type: ignore
(Target, object),
{'indextemplate': indextemplate})
stddomain = self.domains['std']
stddomain.directives[directivename] = directive
stddomain.roles[rolename] = XRefRole(innernodeclass=ref_nodeclass)
stddomain.object_types[directivename] = ObjType(objname or directivename, rolename)
def add_source_parser(self, suffix, parser):
# type: (unicode, Parser) -> None
if suffix in self.source_parsers:
raise ExtensionError(_('source_parser for %r is already registered') % suffix)
self.source_parsers[suffix] = parser
def get_source_parsers(self):
# type: () -> Dict[unicode, Parser]
return self.source_parsers
def add_translator(self, name, translator):
# type: (unicode, Type[nodes.NodeVisitor]) -> None
self.translators[name] = translator
def get_translator_class(self, builder):
# type: (Builder) -> Type[nodes.NodeVisitor]
return self.translators.get(builder.name,
builder.default_translator_class)
def create_translator(self, builder, document):
# type: (Builder, nodes.Node) -> nodes.NodeVisitor
translator_class = self.get_translator_class(builder)
return translator_class(builder, document)
def load_extension(self, 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()

View File

@ -12,6 +12,7 @@
import os import os
import shutil import shutil
import tempfile import tempfile
import warnings
from os import path from os import path
from zipfile import ZipFile from zipfile import ZipFile
@ -20,6 +21,7 @@ from six import string_types, iteritems
from six.moves import configparser from six.moves import configparser
from sphinx import package_dir from sphinx import package_dir
from sphinx.deprecation import RemovedInSphinx20Warning
from sphinx.errors import ThemeError from sphinx.errors import ThemeError
from sphinx.locale import _ from sphinx.locale import _
from sphinx.util import logging from sphinx.util import logging
@ -77,6 +79,8 @@ class Theme(object):
try: try:
inherit = self.config.get('theme', 'inherit') inherit = self.config.get('theme', 'inherit')
except configparser.NoSectionError:
raise ThemeError(_('theme %r doesn\'t have "theme" setting') % name)
except configparser.NoOptionError: except configparser.NoOptionError:
raise ThemeError(_('theme %r doesn\'t have "inherit" setting') % name) raise ThemeError(_('theme %r doesn\'t have "inherit" setting') % name)
@ -161,7 +165,7 @@ class HTMLThemeFactory(object):
def __init__(self, app): def __init__(self, app):
# type: (Sphinx) -> None # type: (Sphinx) -> None
self.confdir = app.confdir self.app = app
self.themes = app.html_themes self.themes = app.html_themes
self.load_builtin_themes() self.load_builtin_themes()
if getattr(app.config, 'html_theme_path', None): if getattr(app.config, 'html_theme_path', None):
@ -178,7 +182,7 @@ class HTMLThemeFactory(object):
# type: (unicode) -> None # type: (unicode) -> None
"""Load additional themes placed at specified directories.""" """Load additional themes placed at specified directories."""
for theme_path in theme_paths: for theme_path in theme_paths:
abs_theme_path = path.abspath(path.join(self.confdir, theme_path)) abs_theme_path = path.abspath(path.join(self.app.confdir, theme_path))
themes = self.find_themes(abs_theme_path) themes = self.find_themes(abs_theme_path)
for name, theme in iteritems(themes): for name, theme in iteritems(themes):
self.themes[name] = theme self.themes[name] = theme
@ -215,6 +219,16 @@ class HTMLThemeFactory(object):
Sphinx refers to ``sphinx_themes`` entry_points. Sphinx refers to ``sphinx_themes`` entry_points.
""" """
# look up for new styled entry_points at first
entry_points = pkg_resources.iter_entry_points('sphinx.html_themes', name)
try:
entry_point = next(entry_points)
self.app.registry.load_extension(self.app, entry_point.module_name)
return
except StopIteration:
pass
# look up for old styled entry_points
for entry_point in pkg_resources.iter_entry_points('sphinx_themes'): for entry_point in pkg_resources.iter_entry_points('sphinx_themes'):
target = entry_point.load() target = entry_point.load()
if callable(target): if callable(target):
@ -228,6 +242,9 @@ class HTMLThemeFactory(object):
themes = self.find_themes(themedir) themes = self.find_themes(themedir)
for entry, theme in iteritems(themes): for entry, theme in iteritems(themes):
if name == entry: if name == entry:
warnings.warn('``sphinx_themes`` entry point is now deprecated. '
'Please use ``sphinx.html_themes`` instead.',
RemovedInSphinx20Warning)
self.themes[name] = theme self.themes[name] = theme
def find_themes(self, theme_path): def find_themes(self, theme_path):

View File

@ -52,7 +52,7 @@ def publish_msgstr(app, source, source_path, source_line, config, settings):
from sphinx.io import SphinxI18nReader from sphinx.io import SphinxI18nReader
reader = SphinxI18nReader( reader = SphinxI18nReader(
app=app, app=app,
parsers=config.source_parsers, parsers=app.registry.get_source_parsers(),
parser_name='restructuredtext', # default parser parser_name='restructuredtext', # default parser
) )
reader.set_lineno_for_reporter(source_line) reader.set_lineno_for_reporter(source_line)

View File

@ -24,7 +24,7 @@ from sphinx.util.osutil import ensuredir
if False: if False:
# For type annotation # For type annotation
from typing import Any, Dict # NOQA from typing import Any, Dict, List, Tuple # NOQA
from sphinx.application import Sphinx # NOQA from sphinx.application import Sphinx # NOQA
@ -136,6 +136,107 @@ class DataURIExtractor(BaseImageConverter):
self.app.env.images.add_file(self.env.docname, path) self.app.env.images.add_file(self.env.docname, path)
def get_filename_for(filename, mimetype):
# type: (unicode, unicode) -> unicode
basename = os.path.basename(filename)
return os.path.splitext(basename)[0] + get_image_extension(mimetype)
class ImageConverter(BaseImageConverter):
"""A base class images converter.
The concrete image converters should derive this class and
overrides the following methods and attributes:
* default_priority (if needed)
* conversion_rules
* is_available()
* convert()
"""
default_priority = 200
#: A conversion rules between two mimetypes which this converters supports
conversion_rules = [] # type: List[Tuple[unicode, unicode]]
def __init__(self, *args, **kwargs):
# type: (Any, Any) -> None
self.available = None # type: bool
# the converter is available or not.
# Will be checked at first conversion
BaseImageConverter.__init__(self, *args, **kwargs) # type: ignore
def match(self, node):
# type: (nodes.Node) -> bool
if self.available is None:
self.available = self.is_available()
if not self.available:
return False
elif set(node['candidates']) & set(self.app.builder.supported_image_types):
# builder supports the image; no need to convert
return False
else:
rule = self.get_conversion_rule(node)
if rule:
return True
else:
return False
def get_conversion_rule(self, node):
# type: (nodes.Node) -> Tuple[unicode, unicode]
for candidate in self.guess_mimetypes(node):
for supported in self.app.builder.supported_image_types:
rule = (candidate, supported)
if rule in self.conversion_rules:
return rule
return None
def is_available(self):
# type: () -> bool
"""Confirms the converter is available or not."""
raise NotImplemented
def guess_mimetypes(self, node):
# type: (nodes.Node) -> List[unicode]
if '?' in node['candidates']:
return []
elif '*' in node['candidates']:
from sphinx.util.images import guess_mimetype
return [guess_mimetype(node['uri'])]
else:
return node['candidates'].keys()
def handle(self, node):
# type: (nodes.Node) -> None
_from, _to = self.get_conversion_rule(node)
if _from in node['candidates']:
srcpath = node['candidates'][_from]
else:
srcpath = node['candidates']['*']
filename = get_filename_for(srcpath, _to)
ensuredir(self.imagedir)
destpath = os.path.join(self.imagedir, filename)
abs_srcpath = os.path.join(self.app.srcdir, srcpath)
if self.convert(abs_srcpath, destpath):
if '*' in node['candidates']:
node['candidates']['*'] = destpath
else:
node['candidates'][_to] = destpath
node['uri'] = destpath
self.env.original_image_uri[destpath] = srcpath
self.env.images.add_file(self.env.docname, destpath)
def convert(self, _from, _to):
# type: (unicode, unicode) -> bool
"""Converts the image to expected one."""
raise NotImplemented
def setup(app): def setup(app):
# type: (Sphinx) -> Dict[unicode, Any] # type: (Sphinx) -> Dict[unicode, Any]
app.add_post_transform(ImageDownloader) app.add_post_transform(ImageDownloader)

View File

@ -11,13 +11,16 @@
from __future__ import absolute_import from __future__ import absolute_import
import re import re
import types
from copy import copy from copy import copy
from contextlib import contextmanager from contextlib import contextmanager
import docutils import docutils
from docutils.utils import Reporter from docutils.utils import Reporter
from docutils.parsers.rst import directives, roles from docutils.parsers.rst import directives, roles, convert_directive_function
from sphinx.errors import ExtensionError
from sphinx.locale import _
from sphinx.util import logging from sphinx.util import logging
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@ -154,3 +157,17 @@ class LoggingReporter(Reporter):
def is_html5_writer_available(): def is_html5_writer_available():
# type: () -> bool # type: () -> bool
return __version_info__ > (0, 13, 0) return __version_info__ > (0, 13, 0)
def directive_helper(obj, has_content=None, argument_spec=None, **option_spec):
# type: (Any, bool, Tuple[int, int, bool], Any) -> Any
if isinstance(obj, (types.FunctionType, types.MethodType)):
obj.content = has_content # type: ignore
obj.arguments = argument_spec or (0, 0, False) # type: ignore
obj.options = option_spec # type: ignore
return convert_directive_function(obj)
else:
if has_content or argument_spec or option_spec:
raise ExtensionError(_('when adding directive classes, no '
'additional arguments may be given'))
return obj

View File

@ -52,8 +52,8 @@ class HTMLWriter(Writer):
def translate(self): def translate(self):
# type: () -> None # type: () -> None
# sadly, this is mostly copied from parent class # sadly, this is mostly copied from parent class
self.visitor = visitor = self.builder.translator_class(self.builder, self.visitor = visitor = self.builder.create_translator(self.builder,
self.document) self.document)
self.document.walkabout(visitor) self.document.walkabout(visitor)
self.output = visitor.astext() self.output = visitor.astext()
for attr in ('head_prefix', 'stylesheet', 'head', 'body_prefix', for attr in ('head_prefix', 'stylesheet', 'head', 'body_prefix',

View File

@ -159,14 +159,12 @@ class LaTeXWriter(writers.Writer):
# type: (Builder) -> None # type: (Builder) -> None
writers.Writer.__init__(self) writers.Writer.__init__(self)
self.builder = builder self.builder = builder
self.translator_class = (
self.builder.translator_class or LaTeXTranslator)
def translate(self): def translate(self):
# type: () -> None # type: () -> None
transform = ShowUrlsTransform(self.document) transform = ShowUrlsTransform(self.document)
transform.apply() transform.apply()
visitor = self.translator_class(self.document, self.builder) visitor = self.builder.create_translator(self.document, self.builder)
self.document.walkabout(visitor) self.document.walkabout(visitor)
self.output = visitor.astext() self.output = visitor.astext()

View File

@ -35,14 +35,12 @@ class ManualPageWriter(Writer):
# type: (Builder) -> None # type: (Builder) -> None
Writer.__init__(self) Writer.__init__(self)
self.builder = builder self.builder = builder
self.translator_class = (
self.builder.translator_class or ManualPageTranslator)
def translate(self): def translate(self):
# type: () -> None # type: () -> None
transform = NestedInlineTransform(self.document) transform = NestedInlineTransform(self.document)
transform.apply() transform.apply()
visitor = self.translator_class(self.builder, self.document) visitor = self.builder.create_translator(self.builder, self.document)
self.visitor = visitor self.visitor = visitor
self.document.walkabout(visitor) self.document.walkabout(visitor)
self.output = visitor.astext() self.output = visitor.astext()

View File

@ -133,13 +133,10 @@ class TexinfoWriter(writers.Writer):
# type: (TexinfoBuilder) -> None # type: (TexinfoBuilder) -> None
writers.Writer.__init__(self) writers.Writer.__init__(self)
self.builder = builder self.builder = builder
self.translator_class = (
self.builder.translator_class or TexinfoTranslator)
def translate(self): def translate(self):
# type: () -> None # type: () -> None
self.visitor = visitor = self.translator_class( self.visitor = visitor = self.builder.create_translator(self.document, self.builder)
self.document, self.builder)
self.document.walkabout(visitor) self.document.walkabout(visitor)
visitor.finish() visitor.finish()
for attr in self.visitor_attributes: for attr in self.visitor_attributes:

View File

@ -159,11 +159,10 @@ class TextWriter(writers.Writer):
# type: (TextBuilder) -> None # type: (TextBuilder) -> None
writers.Writer.__init__(self) writers.Writer.__init__(self)
self.builder = builder self.builder = builder
self.translator_class = self.builder.translator_class or TextTranslator
def translate(self): def translate(self):
# type: () -> None # type: () -> None
visitor = self.translator_class(self.document, self.builder) visitor = self.builder.create_translator(self.document, self.builder)
self.document.walkabout(visitor) self.document.walkabout(visitor)
self.output = visitor.body self.output = visitor.body

View File

@ -24,8 +24,7 @@ class XMLWriter(BaseXMLWriter):
# type: (Builder) -> None # type: (Builder) -> None
BaseXMLWriter.__init__(self) BaseXMLWriter.__init__(self)
self.builder = builder self.builder = builder
if self.builder.translator_class: self.translator_class = self.builder.get_translator_class()
self.translator_class = self.builder.translator_class
def translate(self, *args, **kwargs): def translate(self, *args, **kwargs):
# type: (Any, Any) -> None # type: (Any, Any) -> None

View File

@ -0,0 +1,4 @@
# -*- coding: utf-8 -*-
master_doc = 'index'
extensions = ['sphinx.ext.imgconverter']

View File

@ -0,0 +1,4 @@
test-ext-imgconverter
=====================
.. image:: svgimg.svg

View File

@ -0,0 +1,158 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://web.resource.org/cc/"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:sodipodi="http://inkscape.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
height="60"
width="60"
_SVGFile__filename="oldscale/apps/warning.svg"
version="1.0"
y="0"
x="0"
id="svg1"
sodipodi:version="0.32"
inkscape:version="0.41"
sodipodi:docname="exclamation.svg"
sodipodi:docbase="/home/danny/work/icons/primary/scalable/actions">
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0000000"
inkscape:pageshadow="2"
inkscape:zoom="7.5136000"
inkscape:cx="42.825186"
inkscape:cy="24.316071"
inkscape:window-width="1020"
inkscape:window-height="691"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:current-layer="svg1" />
<defs
id="defs3">
<linearGradient
id="linearGradient1160">
<stop
style="stop-color: #000000;stop-opacity: 1.0;"
id="stop1161"
offset="0" />
<stop
style="stop-color:#ffffff;stop-opacity:1;"
id="stop1162"
offset="1" />
</linearGradient>
<linearGradient
xlink:href="#linearGradient1160"
id="linearGradient1163" />
</defs>
<metadata
id="metadata12">
<RDF
id="RDF13">
<Work
about=""
id="Work14">
<title
id="title15">Part of the Flat Icon Collection (Thu Aug 26 14:31:40 2004)</title>
<description
id="description17" />
<subject
id="subject18">
<Bag
id="Bag19">
<li
id="li20" />
</Bag>
</subject>
<publisher
id="publisher21">
<Agent
about=""
id="Agent22">
<title
id="title23" />
</Agent>
</publisher>
<creator
id="creator24">
<Agent
about=""
id="Agent25">
<title
id="title26">Danny Allen</title>
</Agent>
</creator>
<rights
id="rights28">
<Agent
about=""
id="Agent29">
<title
id="title30">Danny Allen</title>
</Agent>
</rights>
<date
id="date32" />
<format
id="format33">image/svg+xml</format>
<type
id="type35"
resource="http://purl.org/dc/dcmitype/StillImage" />
<license
id="license36"
resource="http://creativecommons.org/licenses/LGPL/2.1/">
<date
id="date37" />
</license>
<language
id="language38">en</language>
</Work>
</RDF>
<rdf:RDF
id="RDF40">
<cc:Work
rdf:about=""
id="Work41">
<dc:format
id="format42">image/svg+xml</dc:format>
<dc:type
id="type44"
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
</cc:Work>
</rdf:RDF>
</metadata>
<g
id="g2099">
<path
style="color:#000000;fill:none;fill-opacity:1.0000000;fill-rule:evenodd;stroke:#ffffff;stroke-width:8.1250000;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4.0000000;stroke-dashoffset:0.0000000;stroke-opacity:1.0000000;marker:none;marker-start:none;marker-mid:none;marker-end:none"
d="M 55.311891,51.920745 L 4.6880989,51.920744 L 29.999995,8.0792542 L 55.311891,51.920745 z "
id="path1724" />
<path
style="color:#000000;fill:#ffe940;fill-opacity:1.0000000;fill-rule:evenodd;stroke:#000000;stroke-width:3.1250010;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4.0000000;stroke-dashoffset:0.0000000;stroke-opacity:1.0000000;marker:none;marker-start:none;marker-mid:none;marker-end:none"
d="M 55.311891,51.920745 L 4.6880989,51.920744 L 29.999995,8.0792542 L 55.311891,51.920745 z "
id="path1722" />
<path
style="font-size:12.000000;font-weight:900;fill:none;fill-opacity:1.0000000;stroke:#ffffff;stroke-width:8.1250000;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4.0000000;stroke-opacity:1.0000000"
d="M 34.944960,10.779626 L 34.944960,33.186510 C 34.944960,34.752415 34.501979,36.081368 33.616007,37.173380 C 32.750636,38.265402 31.545298,38.811408 29.999995,38.811408 C 28.475302,38.811408 27.269965,38.265402 26.383993,37.173380 C 25.498020,36.060767 25.055030,34.731804 25.055030,33.186510 L 25.055030,10.779626 C 25.055030,9.1931155 25.498020,7.8641562 26.383993,6.7927462 C 27.269965,5.7007332 28.475302,5.1547262 29.999995,5.1547262 C 31.009593,5.1547262 31.885265,5.4019740 32.627010,5.8964706 C 33.389356,6.3909681 33.966274,7.0709005 34.357752,7.9362696 C 34.749221,8.7810349 34.944960,9.7288200 34.944960,10.779626 z "
id="path1099" />
<path
style="font-size:12.000000;font-weight:900;fill:#e71c02;fill-opacity:1.0000000;stroke:none;stroke-width:3.1249981;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1.0000000"
d="M 29.999995,3.5986440 C 28.102272,3.5986440 26.318514,4.3848272 25.156245,5.8173940 C 24.028906,7.1806889 23.499995,8.9087770 23.499995,10.786144 L 23.499995,33.192394 C 23.499995,35.036302 24.050685,36.772771 25.156245,38.161144 C 26.318514,39.593721 28.102273,40.379893 29.999995,40.379894 C 31.913354,40.379894 33.697195,39.576736 34.843745,38.129894 C 35.959941,36.754118 36.499995,35.052976 36.499995,33.192394 L 36.499995,10.786144 C 36.499995,9.5413010 36.276626,8.3551469 35.781245,7.2861440 C 35.278844,6.1755772 34.477762,5.2531440 33.468745,4.5986440 C 32.454761,3.9226545 31.264694,3.5986439 29.999995,3.5986440 z "
id="path835"
sodipodi:nodetypes="cccccccccccc" />
<path
style="color:#000000;fill:none;fill-opacity:1.0000000;fill-rule:evenodd;stroke:#ffffff;stroke-width:5.0000000;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4.0000000;stroke-dashoffset:0.0000000;stroke-opacity:1.0000000;marker:none;marker-start:none;marker-mid:none;marker-end:none"
d="M 36.506243,49.901522 C 36.506243,53.492972 33.591442,56.407773 29.999991,56.407773 C 26.408541,56.407773 23.493739,53.492972 23.493739,49.901522 C 23.493739,46.310071 26.408541,43.395270 29.999991,43.395270 C 33.591442,43.395270 36.506243,46.310071 36.506243,49.901522 z "
id="path1727" />
<path
style="color:#000000;fill:#e71c02;fill-opacity:1.0000000;fill-rule:evenodd;stroke:none;stroke-width:3.1250000;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4.0000000;stroke-dashoffset:0.0000000;stroke-opacity:1.0000000;marker:none;marker-start:none;marker-mid:none;marker-end:none"
d="M 36.506243,49.901522 C 36.506243,53.492972 33.591442,56.407773 29.999991,56.407773 C 26.408541,56.407773 23.493739,53.492972 23.493739,49.901522 C 23.493739,46.310071 26.408541,43.395270 29.999991,43.395270 C 33.591442,43.395270 36.506243,46.310071 36.506243,49.901522 z "
id="path1725" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 7.2 KiB

View File

@ -0,0 +1,4 @@
# -*- coding: utf-8 -*-
extensions = ['sphinx.ext.intersphinx']
master_doc = 'index'

View File

@ -0,0 +1,6 @@
test-ext-intersphinx-cppdomain
==============================
.. cpp:namespace:: foo
:cpp:class:`Bar`

View File

@ -26,7 +26,7 @@ def teardown_module():
@pytest.mark.sphinx('html') @pytest.mark.sphinx('html')
def test_html_translator(app, status, warning): def test_html_translator(app, status, warning):
# no set_translator() # no set_translator()
translator_class = app.builder.translator_class translator_class = app.builder.get_translator_class()
assert translator_class assert translator_class
assert translator_class.__name__ == 'SmartyPantsHTMLTranslator' assert translator_class.__name__ == 'SmartyPantsHTMLTranslator'
@ -35,7 +35,7 @@ def test_html_translator(app, status, warning):
'html_use_smartypants': False}) 'html_use_smartypants': False})
def test_html_with_smartypants(app, status, warning): def test_html_with_smartypants(app, status, warning):
# no set_translator(), html_use_smartypants=False # no set_translator(), html_use_smartypants=False
translator_class = app.builder.translator_class translator_class = app.builder.get_translator_class()
assert translator_class assert translator_class
assert translator_class.__name__ == 'HTMLTranslator' assert translator_class.__name__ == 'HTMLTranslator'
@ -43,69 +43,69 @@ def test_html_with_smartypants(app, status, warning):
@pytest.mark.sphinx('html', testroot='api-set-translator') @pytest.mark.sphinx('html', testroot='api-set-translator')
def test_html_with_set_translator_for_html_(app, status, warning): def test_html_with_set_translator_for_html_(app, status, warning):
# use set_translator() # use set_translator()
translator_class = app.builder.translator_class translator_class = app.builder.get_translator_class()
assert translator_class assert translator_class
assert translator_class.__name__ == 'ConfHTMLTranslator' assert translator_class.__name__ == 'ConfHTMLTranslator'
@pytest.mark.sphinx('singlehtml', testroot='api-set-translator') @pytest.mark.sphinx('singlehtml', testroot='api-set-translator')
def test_singlehtml_set_translator_for_singlehtml(app, status, warning): def test_singlehtml_set_translator_for_singlehtml(app, status, warning):
translator_class = app.builder.translator_class translator_class = app.builder.get_translator_class()
assert translator_class assert translator_class
assert translator_class.__name__ == 'ConfSingleHTMLTranslator' assert translator_class.__name__ == 'ConfSingleHTMLTranslator'
@pytest.mark.sphinx('pickle', testroot='api-set-translator') @pytest.mark.sphinx('pickle', testroot='api-set-translator')
def test_pickle_set_translator_for_pickle(app, status, warning): def test_pickle_set_translator_for_pickle(app, status, warning):
translator_class = app.builder.translator_class translator_class = app.builder.get_translator_class()
assert translator_class assert translator_class
assert translator_class.__name__ == 'ConfPickleTranslator' assert translator_class.__name__ == 'ConfPickleTranslator'
@pytest.mark.sphinx('json', testroot='api-set-translator') @pytest.mark.sphinx('json', testroot='api-set-translator')
def test_json_set_translator_for_json(app, status, warning): def test_json_set_translator_for_json(app, status, warning):
translator_class = app.builder.translator_class translator_class = app.builder.get_translator_class()
assert translator_class assert translator_class
assert translator_class.__name__ == 'ConfJsonTranslator' assert translator_class.__name__ == 'ConfJsonTranslator'
@pytest.mark.sphinx('latex', testroot='api-set-translator') @pytest.mark.sphinx('latex', testroot='api-set-translator')
def test_html_with_set_translator_for_latex(app, status, warning): def test_html_with_set_translator_for_latex(app, status, warning):
translator_class = app.builder.translator_class translator_class = app.builder.get_translator_class()
assert translator_class assert translator_class
assert translator_class.__name__ == 'ConfLaTeXTranslator' assert translator_class.__name__ == 'ConfLaTeXTranslator'
@pytest.mark.sphinx('man', testroot='api-set-translator') @pytest.mark.sphinx('man', testroot='api-set-translator')
def test_html_with_set_translator_for_man(app, status, warning): def test_html_with_set_translator_for_man(app, status, warning):
translator_class = app.builder.translator_class translator_class = app.builder.get_translator_class()
assert translator_class assert translator_class
assert translator_class.__name__ == 'ConfManualPageTranslator' assert translator_class.__name__ == 'ConfManualPageTranslator'
@pytest.mark.sphinx('texinfo', testroot='api-set-translator') @pytest.mark.sphinx('texinfo', testroot='api-set-translator')
def test_html_with_set_translator_for_texinfo(app, status, warning): def test_html_with_set_translator_for_texinfo(app, status, warning):
translator_class = app.builder.translator_class translator_class = app.builder.get_translator_class()
assert translator_class assert translator_class
assert translator_class.__name__ == 'ConfTexinfoTranslator' assert translator_class.__name__ == 'ConfTexinfoTranslator'
@pytest.mark.sphinx('text', testroot='api-set-translator') @pytest.mark.sphinx('text', testroot='api-set-translator')
def test_html_with_set_translator_for_text(app, status, warning): def test_html_with_set_translator_for_text(app, status, warning):
translator_class = app.builder.translator_class translator_class = app.builder.get_translator_class()
assert translator_class assert translator_class
assert translator_class.__name__ == 'ConfTextTranslator' assert translator_class.__name__ == 'ConfTextTranslator'
@pytest.mark.sphinx('xml', testroot='api-set-translator') @pytest.mark.sphinx('xml', testroot='api-set-translator')
def test_html_with_set_translator_for_xml(app, status, warning): def test_html_with_set_translator_for_xml(app, status, warning):
translator_class = app.builder.translator_class translator_class = app.builder.get_translator_class()
assert translator_class assert translator_class
assert translator_class.__name__ == 'ConfXMLTranslator' assert translator_class.__name__ == 'ConfXMLTranslator'
@pytest.mark.sphinx('pseudoxml', testroot='api-set-translator') @pytest.mark.sphinx('pseudoxml', testroot='api-set-translator')
def test_html_with_set_translator_for_pseudoxml(app, status, warning): def test_html_with_set_translator_for_pseudoxml(app, status, warning):
translator_class = app.builder.translator_class translator_class = app.builder.get_translator_class()
assert translator_class assert translator_class
assert translator_class.__name__ == 'ConfPseudoXMLTranslator' assert translator_class.__name__ == 'ConfPseudoXMLTranslator'

View File

@ -85,13 +85,6 @@ def test_domain_override(app, status, warning):
@pytest.mark.sphinx(testroot='add_source_parser') @pytest.mark.sphinx(testroot='add_source_parser')
def test_add_source_parser(app, status, warning): def test_add_source_parser(app, status, warning):
assert set(app.config.source_suffix) == set(['.rst', '.md', '.test']) assert set(app.config.source_suffix) == set(['.rst', '.md', '.test'])
assert set(app.config.source_parsers.keys()) == set(['.md', '.test']) assert set(app.registry.get_source_parsers().keys()) == set(['.md', '.test'])
assert app.config.source_parsers['.md'].__name__ == 'DummyMarkdownParser' assert app.registry.get_source_parsers()['.md'].__name__ == 'DummyMarkdownParser'
assert app.config.source_parsers['.test'].__name__ == 'TestSourceParser' assert app.registry.get_source_parsers()['.test'].__name__ == 'TestSourceParser'
@pytest.mark.sphinx(testroot='add_source_parser-conflicts-with-users-setting')
def test_add_source_parser_conflicts_with_users_setting(app, status, warning):
assert set(app.config.source_suffix) == set(['.rst', '.test'])
assert set(app.config.source_parsers.keys()) == set(['.test'])
assert app.config.source_parsers['.test'].__name__ == 'DummyTestParser'

View File

@ -87,6 +87,14 @@ def compile_latex_document(app):
app.config.latex_engine, p.returncode) app.config.latex_engine, p.returncode)
def skip_if_requested(testfunc):
if 'SKIP_LATEX_BUILD' in os.environ:
msg = 'Skip LaTeX builds because SKIP_LATEX_BUILD is set'
return skip_if(True, msg)(testfunc)
else:
return testfunc
def skip_if_stylefiles_notfound(testfunc): def skip_if_stylefiles_notfound(testfunc):
if kpsetest(*STYLEFILES) is False: if kpsetest(*STYLEFILES) is False:
msg = 'not running latex, the required styles do not seem to be installed' msg = 'not running latex, the required styles do not seem to be installed'
@ -95,6 +103,7 @@ def skip_if_stylefiles_notfound(testfunc):
return testfunc return testfunc
@skip_if_requested
@skip_if_stylefiles_notfound @skip_if_stylefiles_notfound
@pytest.mark.parametrize( @pytest.mark.parametrize(
"engine,docclass", "engine,docclass",

View File

@ -10,7 +10,11 @@
""" """
import pytest import pytest
from mock import Mock
from docutils import nodes
from sphinx import addnodes from sphinx import addnodes
from sphinx.domains.javascript import JavaScriptDomain
from util import assert_node from util import assert_node
@ -133,3 +137,31 @@ def test_domain_js_find_obj(app, status, warning):
( u'module_a.submodule.ModTopLevel.mod_child_2', (u'module', u'method'))) ( u'module_a.submodule.ModTopLevel.mod_child_2', (u'module', u'method')))
assert (find_obj(u'module_b.submodule', u'ModTopLevel', u'module_a.submodule', u'mod') == assert (find_obj(u'module_b.submodule', u'ModTopLevel', u'module_a.submodule', u'mod') ==
( u'module_a.submodule', (u'module', u'module'))) ( u'module_a.submodule', (u'module', u'module')))
def test_get_full_qualified_name():
env = Mock(domaindata={})
domain = JavaScriptDomain(env)
# non-js references
node = nodes.reference()
assert domain.get_full_qualified_name(node) is None
# simple reference
node = nodes.reference(reftarget='func')
assert domain.get_full_qualified_name(node) == 'func'
# with js:module context
kwargs = {'js:module': 'module1'}
node = nodes.reference(reftarget='func', **kwargs)
assert domain.get_full_qualified_name(node) == 'module1.func'
# with js:object context
kwargs = {'js:object': 'Class'}
node = nodes.reference(reftarget='func', **kwargs)
assert domain.get_full_qualified_name(node) == 'Class.func'
# with both js:module and js:object context
kwargs = {'js:module': 'module1', 'js:object': 'Class'}
node = nodes.reference(reftarget='func', **kwargs)
assert domain.get_full_qualified_name(node) == 'module1.Class.func'

View File

@ -10,9 +10,12 @@
""" """
import pytest import pytest
from mock import Mock
from six import text_type from six import text_type
from docutils import nodes
from sphinx import addnodes from sphinx import addnodes
from sphinx.domains.python import py_sig_re, _pseudo_parse_arglist from sphinx.domains.python import py_sig_re, _pseudo_parse_arglist, PythonDomain
from util import assert_node from util import assert_node
@ -28,7 +31,6 @@ def parse(sig):
def test_function_signatures(): def test_function_signatures():
rv = parse('func(a=1) -> int object') rv = parse('func(a=1) -> int object')
assert text_type(rv) == u'a=1' assert text_type(rv) == u'a=1'
@ -165,3 +167,31 @@ def test_domain_py_find_obj(app, status, warning):
[(u'NestedParentA.NestedChildA.subchild_1', (u'roles', u'method'))]) [(u'NestedParentA.NestedChildA.subchild_1', (u'roles', u'method'))])
assert (find_obj(None, u'NestedParentA.NestedChildA', u'subchild_1', u'meth') == assert (find_obj(None, u'NestedParentA.NestedChildA', u'subchild_1', u'meth') ==
[(u'NestedParentA.NestedChildA.subchild_1', (u'roles', u'method'))]) [(u'NestedParentA.NestedChildA.subchild_1', (u'roles', u'method'))])
def test_get_full_qualified_name():
env = Mock(domaindata={})
domain = PythonDomain(env)
# non-python references
node = nodes.reference()
assert domain.get_full_qualified_name(node) is None
# simple reference
node = nodes.reference(reftarget='func')
assert domain.get_full_qualified_name(node) == 'func'
# with py:module context
kwargs = {'py:module': 'module1'}
node = nodes.reference(reftarget='func', **kwargs)
assert domain.get_full_qualified_name(node) == 'module1.func'
# with py:class context
kwargs = {'py:class': 'Class'}
node = nodes.reference(reftarget='func', **kwargs)
assert domain.get_full_qualified_name(node) == 'Class.func'
# with both py:module and py:class context
kwargs = {'py:module': 'module1', 'py:class': 'Class'}
node = nodes.reference(reftarget='func', **kwargs)
assert domain.get_full_qualified_name(node) == 'module1.Class.func'

View File

@ -57,3 +57,21 @@ def test_process_doc_handle_table_title():
assert 'testname' in domain.data['labels'] assert 'testname' in domain.data['labels']
assert domain.data['labels']['testname'] == ( assert domain.data['labels']['testname'] == (
'testdoc', 'testid', 'title text') 'testdoc', 'testid', 'title text')
def test_get_full_qualified_name():
env = mock.Mock(domaindata={})
domain = StandardDomain(env)
# normal references
node = nodes.reference()
assert domain.get_full_qualified_name(node) is None
# simple reference to options
node = nodes.reference(reftype='option', reftarget='-l')
assert domain.get_full_qualified_name(node) is None
# options with std:program context
kwargs = {'std:program': 'ls'}
node = nodes.reference(reftype='option', reftarget='-l', **kwargs)
assert domain.get_full_qualified_name(node) == 'ls.-l'

View File

@ -0,0 +1,22 @@
# -*- coding: utf-8 -*-
"""
test_ext_imgconverter
~~~~~~~~~~~~~~~~~~~~~
Test sphinx.ext.imgconverter extension.
:copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
import pytest
@pytest.mark.sphinx('latex', testroot='ext-imgconverter')
def test_ext_imgconverter(app, status, warning):
app.builder.build_all()
content = (app.outdir / 'Python.tex').text()
assert '\\sphinxincludegraphics{{svgimg}.png}' in content
assert not (app.outdir / 'svgimg.svg').exists()
assert (app.outdir / 'svgimg.png').exists()

View File

@ -26,6 +26,22 @@ from sphinx.ext.intersphinx import (
from test_util_inventory import inventory_v2 from test_util_inventory import inventory_v2
def fake_node(domain, type, target, content, **attrs):
contnode = nodes.emphasis(content, content)
node = addnodes.pending_xref('')
node['reftarget'] = target
node['reftype'] = type
node['refdomain'] = domain
node.attributes.update(attrs)
node += contnode
return node, contnode
def reference_check(app, *args, **kwds):
node, contnode = fake_node(*args, **kwds)
return missing_reference(app, app.env, node, contnode)
@mock.patch('sphinx.ext.intersphinx.InventoryFile') @mock.patch('sphinx.ext.intersphinx.InventoryFile')
@mock.patch('sphinx.ext.intersphinx._read_from_url') @mock.patch('sphinx.ext.intersphinx._read_from_url')
def test_fetch_inventory_redirection(_read_from_url, InventoryFile, app, status, warning): def test_fetch_inventory_redirection(_read_from_url, InventoryFile, app, status, warning):
@ -88,46 +104,30 @@ def test_missing_reference(tempdir, app, status, warning):
assert inv['py:module']['module2'] == \ assert inv['py:module']['module2'] == \
('foo', '2.0', 'https://docs.python.org/foo.html#module-module2', '-') ('foo', '2.0', 'https://docs.python.org/foo.html#module-module2', '-')
# create fake nodes and check referencing
def fake_node(domain, type, target, content, **attrs):
contnode = nodes.emphasis(content, content)
node = addnodes.pending_xref('')
node['reftarget'] = target
node['reftype'] = type
node['refdomain'] = domain
node.attributes.update(attrs)
node += contnode
return node, contnode
def reference_check(*args, **kwds):
node, contnode = fake_node(*args, **kwds)
return missing_reference(app, app.env, node, contnode)
# check resolution when a target is found # check resolution when a target is found
rn = reference_check('py', 'func', 'module1.func', 'foo') rn = reference_check(app, 'py', 'func', 'module1.func', 'foo')
assert isinstance(rn, nodes.reference) assert isinstance(rn, nodes.reference)
assert rn['refuri'] == 'https://docs.python.org/sub/foo.html#module1.func' assert rn['refuri'] == 'https://docs.python.org/sub/foo.html#module1.func'
assert rn['reftitle'] == '(in foo v2.0)' assert rn['reftitle'] == '(in foo v2.0)'
assert rn[0].astext() == 'foo' assert rn[0].astext() == 'foo'
# create unresolvable nodes and check None return value # create unresolvable nodes and check None return value
assert reference_check('py', 'foo', 'module1.func', 'foo') is None assert reference_check(app, 'py', 'foo', 'module1.func', 'foo') is None
assert reference_check('py', 'func', 'foo', 'foo') is None assert reference_check(app, 'py', 'func', 'foo', 'foo') is None
assert reference_check('py', 'func', 'foo', 'foo') is None assert reference_check(app, 'py', 'func', 'foo', 'foo') is None
# check handling of prefixes # check handling of prefixes
# prefix given, target found: prefix is stripped # prefix given, target found: prefix is stripped
rn = reference_check('py', 'mod', 'py3k:module2', 'py3k:module2') rn = reference_check(app, 'py', 'mod', 'py3k:module2', 'py3k:module2')
assert rn[0].astext() == 'module2' assert rn[0].astext() == 'module2'
# prefix given, but not in title: nothing stripped # prefix given, but not in title: nothing stripped
rn = reference_check('py', 'mod', 'py3k:module2', 'module2') rn = reference_check(app, 'py', 'mod', 'py3k:module2', 'module2')
assert rn[0].astext() == 'module2' assert rn[0].astext() == 'module2'
# prefix given, but explicit: nothing stripped # prefix given, but explicit: nothing stripped
rn = reference_check('py', 'mod', 'py3k:module2', 'py3k:module2', rn = reference_check(app, 'py', 'mod', 'py3k:module2', 'py3k:module2',
refexplicit=True) refexplicit=True)
assert rn[0].astext() == 'py3k:module2' assert rn[0].astext() == 'py3k:module2'
@ -146,23 +146,116 @@ def test_missing_reference(tempdir, app, status, warning):
assert contnode[0].astext() == 'py3k:unknown' assert contnode[0].astext() == 'py3k:unknown'
# check relative paths # check relative paths
rn = reference_check('py', 'mod', 'py3krel:module1', 'foo') rn = reference_check(app, 'py', 'mod', 'py3krel:module1', 'foo')
assert rn['refuri'] == 'py3k/foo.html#module-module1' assert rn['refuri'] == 'py3k/foo.html#module-module1'
rn = reference_check('py', 'mod', 'py3krelparent:module1', 'foo') rn = reference_check(app, 'py', 'mod', 'py3krelparent:module1', 'foo')
assert rn['refuri'] == '../../py3k/foo.html#module-module1' assert rn['refuri'] == '../../py3k/foo.html#module-module1'
rn = reference_check('py', 'mod', 'py3krel:module1', 'foo', refdoc='sub/dir/test') rn = reference_check(app, 'py', 'mod', 'py3krel:module1', 'foo', refdoc='sub/dir/test')
assert rn['refuri'] == '../../py3k/foo.html#module-module1' assert rn['refuri'] == '../../py3k/foo.html#module-module1'
rn = reference_check('py', 'mod', 'py3krelparent:module1', 'foo', refdoc='sub/dir/test') rn = reference_check(app, 'py', 'mod', 'py3krelparent:module1', 'foo',
refdoc='sub/dir/test')
assert rn['refuri'] == '../../../../py3k/foo.html#module-module1' assert rn['refuri'] == '../../../../py3k/foo.html#module-module1'
# check refs of standard domain # check refs of standard domain
rn = reference_check('std', 'doc', 'docname', 'docname') rn = reference_check(app, 'std', 'doc', 'docname', 'docname')
assert rn['refuri'] == 'https://docs.python.org/docname.html' assert rn['refuri'] == 'https://docs.python.org/docname.html'
def test_missing_reference_pydomain(tempdir, app, status, warning):
inv_file = tempdir / 'inventory'
inv_file.write_bytes(inventory_v2)
app.config.intersphinx_mapping = {
'https://docs.python.org/': inv_file,
}
app.config.intersphinx_cache_limit = 0
# load the inventory and check if it's done correctly
load_mappings(app)
# no context data
kwargs = {}
node, contnode = fake_node('py', 'func', 'func', 'func()', **kwargs)
rn = missing_reference(app, app.env, node, contnode)
assert rn is None
# py:module context helps to search objects
kwargs = {'py:module': 'module1'}
node, contnode = fake_node('py', 'func', 'func', 'func()', **kwargs)
rn = missing_reference(app, app.env, node, contnode)
assert rn.astext() == 'func()'
def test_missing_reference_stddomain(tempdir, app, status, warning):
inv_file = tempdir / 'inventory'
inv_file.write_bytes(inventory_v2)
app.config.intersphinx_mapping = {
'https://docs.python.org/': inv_file,
}
app.config.intersphinx_cache_limit = 0
# load the inventory and check if it's done correctly
load_mappings(app)
# no context data
kwargs = {}
node, contnode = fake_node('std', 'option', '-l', '-l', **kwargs)
rn = missing_reference(app, app.env, node, contnode)
assert rn is None
# std:program context helps to search objects
kwargs = {'std:program': 'ls'}
node, contnode = fake_node('std', 'option', '-l', 'ls -l', **kwargs)
rn = missing_reference(app, app.env, node, contnode)
assert rn.astext() == 'ls -l'
@pytest.mark.sphinx('html', testroot='ext-intersphinx-cppdomain')
def test_missing_reference_cppdomain(tempdir, app, status, warning):
inv_file = tempdir / 'inventory'
inv_file.write_bytes(inventory_v2)
app.config.intersphinx_mapping = {
'https://docs.python.org/': inv_file,
}
app.config.intersphinx_cache_limit = 0
# load the inventory and check if it's done correctly
load_mappings(app)
app.build()
html = (app.outdir / 'index.html').text()
assert ('<a class="reference external"'
' href="https://docs.python.org/index.html#cpp_foo_bar"'
' title="(in foo v2.0)"><code class="xref cpp cpp-class docutils literal">'
'<span class="pre">Bar</span></code></a>' in html)
def test_missing_reference_jsdomain(tempdir, app, status, warning):
inv_file = tempdir / 'inventory'
inv_file.write_bytes(inventory_v2)
app.config.intersphinx_mapping = {
'https://docs.python.org/': inv_file,
}
app.config.intersphinx_cache_limit = 0
# load the inventory and check if it's done correctly
load_mappings(app)
# no context data
kwargs = {}
node, contnode = fake_node('js', 'meth', 'baz', 'baz()', **kwargs)
rn = missing_reference(app, app.env, node, contnode)
assert rn is None
# js:module and js:object context helps to search objects
kwargs = {'js:module': 'foo', 'js:object': 'bar'}
node, contnode = fake_node('js', 'meth', 'baz', 'baz()', **kwargs)
rn = missing_reference(app, app.env, node, contnode)
assert rn.astext() == 'baz()'
def test_load_mappings_warnings(tempdir, app, status, warning): def test_load_mappings_warnings(tempdir, app, status, warning):
""" """
load_mappings issues a warning if new-style mapping load_mappings issues a warning if new-style mapping

View File

@ -34,8 +34,15 @@ module1 py:module 0 foo.html#module-module1 Long Module desc
module2 py:module 0 foo.html#module-$ - module2 py:module 0 foo.html#module-$ -
module1.func py:function 1 sub/foo.html#$ - module1.func py:function 1 sub/foo.html#$ -
CFunc c:function 2 cfunc.html#CFunc - CFunc c:function 2 cfunc.html#CFunc -
foo::Bar cpp:class 1 index.html#cpp_foo_bar -
foo::Bar::baz cpp:function 1 index.html#cpp_foo_bar_baz -
a term std:term -1 glossary.html#term-a-term - a term std:term -1 glossary.html#term-a-term -
ls.-l std:cmdoption 1 index.html#cmdoption-ls-l -
docname std:doc -1 docname.html - docname std:doc -1 docname.html -
foo js:module 1 index.html#foo -
foo.bar js:class 1 index.html#foo.bar -
foo.bar.baz js:method 1 index.html#foo.bar.baz -
foo.bar.qux js:data 1 index.html#foo.bar.qux -
a term including:colon std:term -1 glossary.html#term-a-term-including-colon - a term including:colon std:term -1 glossary.html#term-a-term-including-colon -
'''.encode('utf-8')) '''.encode('utf-8'))