mirror of
https://github.com/sphinx-doc/sphinx.git
synced 2025-02-25 18:55:22 -06:00
Merge branch '2.0' into refactor_py_domain2
This commit is contained in:
commit
a285220778
9
CHANGES
9
CHANGES
@ -44,6 +44,9 @@ Deprecated
|
|||||||
* ``sphinx.ext.autodoc.importer.MockLoader``
|
* ``sphinx.ext.autodoc.importer.MockLoader``
|
||||||
* ``sphinx.ext.autodoc.importer.mock()``
|
* ``sphinx.ext.autodoc.importer.mock()``
|
||||||
* ``sphinx.ext.autosummary.autolink_role()``
|
* ``sphinx.ext.autosummary.autolink_role()``
|
||||||
|
* ``sphinx.ext.imgmath.DOC_BODY``
|
||||||
|
* ``sphinx.ext.imgmath.DOC_BODY_PREVIEW``
|
||||||
|
* ``sphinx.ext.imgmath.DOC_HEAD``
|
||||||
* ``sphinx.transforms.CitationReferences``
|
* ``sphinx.transforms.CitationReferences``
|
||||||
* ``sphinx.transforms.SmartQuotesSkipper``
|
* ``sphinx.transforms.SmartQuotesSkipper``
|
||||||
* ``sphinx.util.docfields.DocFieldTransformer.preprocess_fieldtypes()``
|
* ``sphinx.util.docfields.DocFieldTransformer.preprocess_fieldtypes()``
|
||||||
@ -68,8 +71,13 @@ Features added
|
|||||||
* ``math`` directive now supports ``:class:`` option
|
* ``math`` directive now supports ``:class:`` option
|
||||||
* todo: ``todo`` directive now supports ``:name:`` option
|
* todo: ``todo`` directive now supports ``:name:`` option
|
||||||
* #6232: Enable CLI override of Makefile variables
|
* #6232: Enable CLI override of Makefile variables
|
||||||
|
* #6287: autodoc: Unable to document bound instance methods exported as module
|
||||||
|
functions
|
||||||
|
* #6289: autodoc: :confval:`autodoc_default_options` now supports
|
||||||
|
``imported-members`` option
|
||||||
* #6212 autosummary: Add :confval:`autosummary_imported_members` to display
|
* #6212 autosummary: Add :confval:`autosummary_imported_members` to display
|
||||||
imported members on autosummary
|
imported members on autosummary
|
||||||
|
* #6271: ``make clean`` is catastrophically broken if building into '.'
|
||||||
* Add ``:classmethod:`` and ``:staticmethod:`` options to :rst:dir:`py:method`
|
* Add ``:classmethod:`` and ``:staticmethod:`` options to :rst:dir:`py:method`
|
||||||
directive
|
directive
|
||||||
|
|
||||||
@ -80,6 +88,7 @@ Bugs fixed
|
|||||||
is consisted by non-ASCII characters
|
is consisted by non-ASCII characters
|
||||||
* #6213: ifconfig: contents after headings are not shown
|
* #6213: ifconfig: contents after headings are not shown
|
||||||
* commented term in glossary directive is wrongly recognized
|
* commented term in glossary directive is wrongly recognized
|
||||||
|
* #6299: rst domain: rst:directive directive generates waste space
|
||||||
|
|
||||||
Testing
|
Testing
|
||||||
--------
|
--------
|
||||||
|
@ -147,7 +147,7 @@ Sphinx core events
|
|||||||
------------------
|
------------------
|
||||||
|
|
||||||
These events are known to the core. The arguments shown are given to the
|
These events are known to the core. The arguments shown are given to the
|
||||||
registered event handlers. Use :meth:`.connect` in an extension's ``setup``
|
registered event handlers. Use :meth:`.Sphinx.connect` in an extension's ``setup``
|
||||||
function (note that ``conf.py`` can also have a ``setup`` function) to connect
|
function (note that ``conf.py`` can also have a ``setup`` function) to connect
|
||||||
handlers to the events. Example:
|
handlers to the events. Example:
|
||||||
|
|
||||||
|
@ -38,3 +38,8 @@ Builder API
|
|||||||
.. automethod:: write_doc
|
.. automethod:: write_doc
|
||||||
.. automethod:: finish
|
.. automethod:: finish
|
||||||
|
|
||||||
|
**Attributes**
|
||||||
|
|
||||||
|
.. attribute:: events
|
||||||
|
|
||||||
|
An :class:`.EventManager` object.
|
||||||
|
@ -177,6 +177,21 @@ The following is a list of deprecated interfaces.
|
|||||||
- 4.0
|
- 4.0
|
||||||
- ``sphinx.ext.autosummary.AutoLink``
|
- ``sphinx.ext.autosummary.AutoLink``
|
||||||
|
|
||||||
|
* - ``sphinx.ext.imgmath.DOC_BODY``
|
||||||
|
- 2.1
|
||||||
|
- 4.0
|
||||||
|
- N/A
|
||||||
|
|
||||||
|
* - ``sphinx.ext.imgmath.DOC_BODY_PREVIEW``
|
||||||
|
- 2.1
|
||||||
|
- 4.0
|
||||||
|
- N/A
|
||||||
|
|
||||||
|
* - ``sphinx.ext.imgmath.DOC_HEAD``
|
||||||
|
- 2.1
|
||||||
|
- 4.0
|
||||||
|
- N/A
|
||||||
|
|
||||||
* - ``sphinx.transforms.CitationReferences``
|
* - ``sphinx.transforms.CitationReferences``
|
||||||
- 2.1
|
- 2.1
|
||||||
- 4.0
|
- 4.0
|
||||||
|
@ -27,6 +27,10 @@ Build environment API
|
|||||||
|
|
||||||
Directory for storing pickled doctrees.
|
Directory for storing pickled doctrees.
|
||||||
|
|
||||||
|
.. attribute:: events
|
||||||
|
|
||||||
|
An :class:`.EventManager` object.
|
||||||
|
|
||||||
.. attribute:: found_docs
|
.. attribute:: found_docs
|
||||||
|
|
||||||
A set of all existing docnames.
|
A set of all existing docnames.
|
||||||
|
@ -29,3 +29,9 @@ components (e.g. :class:`.Config`, :class:`.BuildEnvironment` and so on) easily.
|
|||||||
|
|
||||||
.. autoclass:: sphinx.transforms.post_transforms.images.ImageConverter
|
.. autoclass:: sphinx.transforms.post_transforms.images.ImageConverter
|
||||||
:members:
|
:members:
|
||||||
|
|
||||||
|
Utility components
|
||||||
|
------------------
|
||||||
|
|
||||||
|
.. autoclass:: sphinx.events.EventManager
|
||||||
|
:members:
|
||||||
|
@ -387,14 +387,17 @@ There are also config values that you can set:
|
|||||||
|
|
||||||
The supported options are ``'members'``, ``'member-order'``,
|
The supported options are ``'members'``, ``'member-order'``,
|
||||||
``'undoc-members'``, ``'private-members'``, ``'special-members'``,
|
``'undoc-members'``, ``'private-members'``, ``'special-members'``,
|
||||||
``'inherited-members'``, ``'show-inheritance'``, ``'ignore-module-all'`` and
|
``'inherited-members'``, ``'show-inheritance'``, ``'ignore-module-all'``,
|
||||||
``'exclude-members'``.
|
``'imported-members'`` and ``'exclude-members'``.
|
||||||
|
|
||||||
.. versionadded:: 1.8
|
.. versionadded:: 1.8
|
||||||
|
|
||||||
.. versionchanged:: 2.0
|
.. versionchanged:: 2.0
|
||||||
Accepts ``True`` as a value.
|
Accepts ``True`` as a value.
|
||||||
|
|
||||||
|
.. versionchanged:: 2.1
|
||||||
|
Added ``'imported-members'``.
|
||||||
|
|
||||||
.. confval:: autodoc_docstring_signature
|
.. confval:: autodoc_docstring_signature
|
||||||
|
|
||||||
Functions imported from C modules cannot be introspected, and therefore the
|
Functions imported from C modules cannot be introspected, and therefore the
|
||||||
|
@ -187,7 +187,7 @@ class Sphinx:
|
|||||||
self.warningiserror = warningiserror
|
self.warningiserror = warningiserror
|
||||||
logging.setup(self, self._status, self._warning)
|
logging.setup(self, self._status, self._warning)
|
||||||
|
|
||||||
self.events = EventManager()
|
self.events = EventManager(self)
|
||||||
|
|
||||||
# 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
|
||||||
@ -254,7 +254,7 @@ class Sphinx:
|
|||||||
|
|
||||||
# now that we know all config values, collect them from conf.py
|
# now that we know all config values, collect them from conf.py
|
||||||
self.config.init_values()
|
self.config.init_values()
|
||||||
self.emit('config-inited', self.config)
|
self.events.emit('config-inited', self.config)
|
||||||
|
|
||||||
# create the project
|
# create the project
|
||||||
self.project = Project(self.srcdir, self.config.source_suffix)
|
self.project = Project(self.srcdir, self.config.source_suffix)
|
||||||
@ -324,7 +324,7 @@ class Sphinx:
|
|||||||
# type: () -> None
|
# type: () -> None
|
||||||
self.builder.set_environment(self.env)
|
self.builder.set_environment(self.env)
|
||||||
self.builder.init()
|
self.builder.init()
|
||||||
self.emit('builder-inited')
|
self.events.emit('builder-inited')
|
||||||
|
|
||||||
# ---- main "build" method -------------------------------------------------
|
# ---- main "build" method -------------------------------------------------
|
||||||
|
|
||||||
@ -365,10 +365,10 @@ class Sphinx:
|
|||||||
envfile = path.join(self.doctreedir, ENV_PICKLE_FILENAME)
|
envfile = path.join(self.doctreedir, ENV_PICKLE_FILENAME)
|
||||||
if path.isfile(envfile):
|
if path.isfile(envfile):
|
||||||
os.unlink(envfile)
|
os.unlink(envfile)
|
||||||
self.emit('build-finished', err)
|
self.events.emit('build-finished', err)
|
||||||
raise
|
raise
|
||||||
else:
|
else:
|
||||||
self.emit('build-finished', None)
|
self.events.emit('build-finished', None)
|
||||||
self.builder.cleanup()
|
self.builder.cleanup()
|
||||||
|
|
||||||
# ---- general extensibility interface -------------------------------------
|
# ---- general extensibility interface -------------------------------------
|
||||||
@ -437,13 +437,7 @@ class Sphinx:
|
|||||||
Return the return values of all callbacks as a list. Do not emit core
|
Return the return values of all callbacks as a list. Do not emit core
|
||||||
Sphinx events in extensions!
|
Sphinx events in extensions!
|
||||||
"""
|
"""
|
||||||
try:
|
return self.events.emit(event, *args)
|
||||||
logger.debug('[app] emitting event: %r%s', event, repr(args)[:100])
|
|
||||||
except Exception:
|
|
||||||
# not every object likes to be repr()'d (think
|
|
||||||
# random stuff coming via autodoc)
|
|
||||||
pass
|
|
||||||
return self.events.emit(event, self, *args)
|
|
||||||
|
|
||||||
def emit_firstresult(self, event, *args):
|
def emit_firstresult(self, event, *args):
|
||||||
# type: (str, Any) -> Any
|
# type: (str, Any) -> Any
|
||||||
@ -453,7 +447,7 @@ class Sphinx:
|
|||||||
|
|
||||||
.. versionadded:: 0.5
|
.. versionadded:: 0.5
|
||||||
"""
|
"""
|
||||||
return self.events.emit_firstresult(event, self, *args)
|
return self.events.emit_firstresult(event, *args)
|
||||||
|
|
||||||
# registering addon parts
|
# registering addon parts
|
||||||
|
|
||||||
|
@ -43,6 +43,7 @@ if False:
|
|||||||
from sphinx.application import Sphinx # NOQA
|
from sphinx.application import Sphinx # NOQA
|
||||||
from sphinx.config import Config # NOQA
|
from sphinx.config import Config # NOQA
|
||||||
from sphinx.environment import BuildEnvironment # NOQA
|
from sphinx.environment import BuildEnvironment # NOQA
|
||||||
|
from sphinx.events import EventManager # NOQA
|
||||||
from sphinx.util.i18n import CatalogInfo # NOQA
|
from sphinx.util.i18n import CatalogInfo # NOQA
|
||||||
from sphinx.util.tags import Tags # NOQA
|
from sphinx.util.tags import Tags # NOQA
|
||||||
|
|
||||||
@ -93,6 +94,7 @@ class Builder:
|
|||||||
|
|
||||||
self.app = app # type: Sphinx
|
self.app = app # type: Sphinx
|
||||||
self.env = None # type: BuildEnvironment
|
self.env = None # type: BuildEnvironment
|
||||||
|
self.events = app.events # type: EventManager
|
||||||
self.config = app.config # type: Config
|
self.config = app.config # type: Config
|
||||||
self.tags = app.tags # type: Tags
|
self.tags = app.tags # type: Tags
|
||||||
self.tags.add(self.format)
|
self.tags.add(self.format)
|
||||||
@ -399,7 +401,7 @@ class Builder:
|
|||||||
added, changed, removed = self.env.get_outdated_files(updated)
|
added, changed, removed = self.env.get_outdated_files(updated)
|
||||||
|
|
||||||
# allow user intervention as well
|
# allow user intervention as well
|
||||||
for docs in self.app.emit('env-get-outdated', self, added, changed, removed):
|
for docs in self.events.emit('env-get-outdated', self, added, changed, removed):
|
||||||
changed.update(set(docs) & self.env.found_docs)
|
changed.update(set(docs) & self.env.found_docs)
|
||||||
|
|
||||||
# if files were added or removed, all documents with globbed toctrees
|
# if files were added or removed, all documents with globbed toctrees
|
||||||
@ -416,13 +418,13 @@ class Builder:
|
|||||||
|
|
||||||
# clear all files no longer present
|
# clear all files no longer present
|
||||||
for docname in removed:
|
for docname in removed:
|
||||||
self.app.emit('env-purge-doc', self.env, docname)
|
self.events.emit('env-purge-doc', self.env, docname)
|
||||||
self.env.clear_doc(docname)
|
self.env.clear_doc(docname)
|
||||||
|
|
||||||
# read all new and changed files
|
# read all new and changed files
|
||||||
docnames = sorted(added | changed)
|
docnames = sorted(added | changed)
|
||||||
# allow changing and reordering the list of docs to read
|
# allow changing and reordering the list of docs to read
|
||||||
self.app.emit('env-before-read-docs', self.env, docnames)
|
self.events.emit('env-before-read-docs', self.env, docnames)
|
||||||
|
|
||||||
# check if we should do parallel or serial read
|
# check if we should do parallel or serial read
|
||||||
if parallel_available and len(docnames) > 5 and self.app.parallel > 1:
|
if parallel_available and len(docnames) > 5 and self.app.parallel > 1:
|
||||||
@ -439,7 +441,7 @@ class Builder:
|
|||||||
raise SphinxError('master file %s not found' %
|
raise SphinxError('master file %s not found' %
|
||||||
self.env.doc2path(self.config.master_doc))
|
self.env.doc2path(self.config.master_doc))
|
||||||
|
|
||||||
for retval in self.app.emit('env-updated', self.env):
|
for retval in self.events.emit('env-updated', self.env):
|
||||||
if retval is not None:
|
if retval is not None:
|
||||||
docnames.extend(retval)
|
docnames.extend(retval)
|
||||||
|
|
||||||
@ -453,7 +455,7 @@ class Builder:
|
|||||||
for docname in status_iterator(docnames, __('reading sources... '), "purple",
|
for docname in status_iterator(docnames, __('reading sources... '), "purple",
|
||||||
len(docnames), self.app.verbosity):
|
len(docnames), self.app.verbosity):
|
||||||
# remove all inventory entries for that file
|
# remove all inventory entries for that file
|
||||||
self.app.emit('env-purge-doc', self.env, docname)
|
self.events.emit('env-purge-doc', self.env, docname)
|
||||||
self.env.clear_doc(docname)
|
self.env.clear_doc(docname)
|
||||||
self.read_doc(docname)
|
self.read_doc(docname)
|
||||||
|
|
||||||
@ -461,7 +463,7 @@ class Builder:
|
|||||||
# type: (List[str], int) -> None
|
# type: (List[str], int) -> None
|
||||||
# clear all outdated docs at once
|
# clear all outdated docs at once
|
||||||
for docname in docnames:
|
for docname in docnames:
|
||||||
self.app.emit('env-purge-doc', self.env, docname)
|
self.events.emit('env-purge-doc', self.env, docname)
|
||||||
self.env.clear_doc(docname)
|
self.env.clear_doc(docname)
|
||||||
|
|
||||||
def read_process(docs):
|
def read_process(docs):
|
||||||
|
@ -686,7 +686,7 @@ class StandaloneHTMLBuilder(Builder):
|
|||||||
def gen_additional_pages(self):
|
def gen_additional_pages(self):
|
||||||
# type: () -> None
|
# type: () -> None
|
||||||
# pages from extensions
|
# pages from extensions
|
||||||
for pagelist in self.app.emit('html-collect-pages'):
|
for pagelist in self.events.emit('html-collect-pages'):
|
||||||
for pagename, context, template in pagelist:
|
for pagename, context, template in pagelist:
|
||||||
self.handle_page(pagename, context, template)
|
self.handle_page(pagename, context, template)
|
||||||
|
|
||||||
|
@ -72,11 +72,19 @@ class Make:
|
|||||||
|
|
||||||
def build_clean(self):
|
def build_clean(self):
|
||||||
# type: () -> int
|
# type: () -> int
|
||||||
|
srcdir = path.abspath(self.srcdir)
|
||||||
|
builddir = path.abspath(self.builddir)
|
||||||
if not path.exists(self.builddir):
|
if not path.exists(self.builddir):
|
||||||
return 0
|
return 0
|
||||||
elif not path.isdir(self.builddir):
|
elif not path.isdir(self.builddir):
|
||||||
print("Error: %r is not a directory!" % self.builddir)
|
print("Error: %r is not a directory!" % self.builddir)
|
||||||
return 1
|
return 1
|
||||||
|
elif srcdir == builddir:
|
||||||
|
print("Error: %r is same as source directory!" % self.builddir)
|
||||||
|
return 1
|
||||||
|
elif path.commonpath([srcdir, builddir]) == builddir:
|
||||||
|
print("Error: %r directory contains source directory!" % self.builddir)
|
||||||
|
return 1
|
||||||
print("Removing everything under %r..." % self.builddir)
|
print("Removing everything under %r..." % self.builddir)
|
||||||
for item in os.listdir(self.builddir):
|
for item in os.listdir(self.builddir):
|
||||||
rmtree(self.builddir_join(item))
|
rmtree(self.builddir_join(item))
|
||||||
|
@ -9,12 +9,14 @@
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
import re
|
import re
|
||||||
|
from typing import cast
|
||||||
|
|
||||||
from sphinx import addnodes
|
from sphinx import addnodes
|
||||||
from sphinx.directives import ObjectDescription
|
from sphinx.directives import ObjectDescription
|
||||||
from sphinx.domains import Domain, ObjType
|
from sphinx.domains import Domain, ObjType
|
||||||
from sphinx.locale import _
|
from sphinx.locale import _, __
|
||||||
from sphinx.roles import XRefRole
|
from sphinx.roles import XRefRole
|
||||||
|
from sphinx.util import logging
|
||||||
from sphinx.util.nodes import make_refnode
|
from sphinx.util.nodes import make_refnode
|
||||||
|
|
||||||
if False:
|
if False:
|
||||||
@ -26,6 +28,8 @@ if False:
|
|||||||
from sphinx.environment import BuildEnvironment # NOQA
|
from sphinx.environment import BuildEnvironment # NOQA
|
||||||
|
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
dir_sig_re = re.compile(r'\.\. (.+?)::(.*)$')
|
dir_sig_re = re.compile(r'\.\. (.+?)::(.*)$')
|
||||||
|
|
||||||
|
|
||||||
@ -43,14 +47,9 @@ class ReSTMarkup(ObjectDescription):
|
|||||||
signode['first'] = (not self.names)
|
signode['first'] = (not self.names)
|
||||||
self.state.document.note_explicit_target(signode)
|
self.state.document.note_explicit_target(signode)
|
||||||
|
|
||||||
objects = self.env.domaindata['rst']['objects']
|
domain = cast(ReSTDomain, self.env.get_domain('rst'))
|
||||||
key = (self.objtype, name)
|
domain.note_object(self.objtype, name, location=(self.env.docname, self.lineno))
|
||||||
if key in objects:
|
|
||||||
self.state_machine.reporter.warning(
|
|
||||||
'duplicate description of %s %s, ' % (self.objtype, name) +
|
|
||||||
'other instance in ' + self.env.doc2path(objects[key]),
|
|
||||||
line=self.lineno)
|
|
||||||
objects[key] = self.env.docname
|
|
||||||
indextext = self.get_index_text(self.objtype, name)
|
indextext = self.get_index_text(self.objtype, name)
|
||||||
if indextext:
|
if indextext:
|
||||||
self.indexnode['entries'].append(('single', indextext,
|
self.indexnode['entries'].append(('single', indextext,
|
||||||
@ -58,10 +57,6 @@ class ReSTMarkup(ObjectDescription):
|
|||||||
|
|
||||||
def get_index_text(self, objectname, name):
|
def get_index_text(self, objectname, name):
|
||||||
# type: (str, str) -> str
|
# type: (str, str) -> str
|
||||||
if self.objtype == 'directive':
|
|
||||||
return _('%s (directive)') % name
|
|
||||||
elif self.objtype == 'role':
|
|
||||||
return _('%s (role)') % name
|
|
||||||
return ''
|
return ''
|
||||||
|
|
||||||
|
|
||||||
@ -80,7 +75,10 @@ def parse_directive(d):
|
|||||||
if not m:
|
if not m:
|
||||||
return (dir, '')
|
return (dir, '')
|
||||||
parsed_dir, parsed_args = m.groups()
|
parsed_dir, parsed_args = m.groups()
|
||||||
|
if parsed_args.strip():
|
||||||
return (parsed_dir.strip(), ' ' + parsed_args.strip())
|
return (parsed_dir.strip(), ' ' + parsed_args.strip())
|
||||||
|
else:
|
||||||
|
return (parsed_dir.strip(), '')
|
||||||
|
|
||||||
|
|
||||||
class ReSTDirective(ReSTMarkup):
|
class ReSTDirective(ReSTMarkup):
|
||||||
@ -96,6 +94,10 @@ class ReSTDirective(ReSTMarkup):
|
|||||||
signode += addnodes.desc_addname(args, args)
|
signode += addnodes.desc_addname(args, args)
|
||||||
return name
|
return name
|
||||||
|
|
||||||
|
def get_index_text(self, objectname, name):
|
||||||
|
# type: (str, str) -> str
|
||||||
|
return _('%s (directive)') % name
|
||||||
|
|
||||||
|
|
||||||
class ReSTRole(ReSTMarkup):
|
class ReSTRole(ReSTMarkup):
|
||||||
"""
|
"""
|
||||||
@ -106,6 +108,10 @@ class ReSTRole(ReSTMarkup):
|
|||||||
signode += addnodes.desc_name(':%s:' % sig, ':%s:' % sig)
|
signode += addnodes.desc_name(':%s:' % sig, ':%s:' % sig)
|
||||||
return sig
|
return sig
|
||||||
|
|
||||||
|
def get_index_text(self, objectname, name):
|
||||||
|
# type: (str, str) -> str
|
||||||
|
return _('%s (role)') % name
|
||||||
|
|
||||||
|
|
||||||
class ReSTDomain(Domain):
|
class ReSTDomain(Domain):
|
||||||
"""ReStructuredText domain."""
|
"""ReStructuredText domain."""
|
||||||
@ -126,42 +132,54 @@ class ReSTDomain(Domain):
|
|||||||
}
|
}
|
||||||
initial_data = {
|
initial_data = {
|
||||||
'objects': {}, # fullname -> docname, objtype
|
'objects': {}, # fullname -> docname, objtype
|
||||||
} # type: Dict[str, Dict[str, Tuple[str, ObjType]]]
|
} # type: Dict[str, Dict[Tuple[str, str], str]]
|
||||||
|
|
||||||
|
@property
|
||||||
|
def objects(self):
|
||||||
|
# type: () -> Dict[Tuple[str, str], str]
|
||||||
|
return self.data.setdefault('objects', {}) # (objtype, fullname) -> docname
|
||||||
|
|
||||||
|
def note_object(self, objtype, name, location=None):
|
||||||
|
# type: (str, str, Any) -> None
|
||||||
|
if (objtype, name) in self.objects:
|
||||||
|
docname = self.objects[objtype, name]
|
||||||
|
logger.warning(__('duplicate description of %s %s, other instance in %s') %
|
||||||
|
(objtype, name, docname), location=location)
|
||||||
|
|
||||||
|
self.objects[objtype, name] = self.env.docname
|
||||||
|
|
||||||
def clear_doc(self, docname):
|
def clear_doc(self, docname):
|
||||||
# type: (str) -> None
|
# type: (str) -> None
|
||||||
for (typ, name), doc in list(self.data['objects'].items()):
|
for (typ, name), doc in list(self.objects.items()):
|
||||||
if doc == docname:
|
if doc == docname:
|
||||||
del self.data['objects'][typ, name]
|
del self.objects[typ, name]
|
||||||
|
|
||||||
def merge_domaindata(self, docnames, otherdata):
|
def merge_domaindata(self, docnames, otherdata):
|
||||||
# type: (List[str], Dict) -> None
|
# type: (List[str], Dict) -> None
|
||||||
# XXX check duplicates
|
# XXX check duplicates
|
||||||
for (typ, name), doc in otherdata['objects'].items():
|
for (typ, name), doc in otherdata['objects'].items():
|
||||||
if doc in docnames:
|
if doc in docnames:
|
||||||
self.data['objects'][typ, name] = doc
|
self.objects[typ, name] = doc
|
||||||
|
|
||||||
def resolve_xref(self, env, fromdocname, builder, typ, target, node, contnode):
|
def resolve_xref(self, env, fromdocname, builder, typ, target, node, contnode):
|
||||||
# type: (BuildEnvironment, str, Builder, str, str, addnodes.pending_xref, nodes.Element) -> nodes.Element # NOQA
|
# type: (BuildEnvironment, str, Builder, str, str, addnodes.pending_xref, nodes.Element) -> nodes.Element # NOQA
|
||||||
objects = self.data['objects']
|
|
||||||
objtypes = self.objtypes_for_role(typ)
|
objtypes = self.objtypes_for_role(typ)
|
||||||
for objtype in objtypes:
|
for objtype in objtypes:
|
||||||
if (objtype, target) in objects:
|
todocname = self.objects.get((objtype, target))
|
||||||
return make_refnode(builder, fromdocname,
|
if todocname:
|
||||||
objects[objtype, target],
|
return make_refnode(builder, fromdocname, todocname,
|
||||||
objtype + '-' + target,
|
objtype + '-' + target,
|
||||||
contnode, target + ' ' + objtype)
|
contnode, target + ' ' + objtype)
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def resolve_any_xref(self, env, fromdocname, builder, target, node, contnode):
|
def resolve_any_xref(self, env, fromdocname, builder, target, node, contnode):
|
||||||
# type: (BuildEnvironment, str, Builder, str, addnodes.pending_xref, nodes.Element) -> List[Tuple[str, nodes.Element]] # NOQA
|
# type: (BuildEnvironment, str, Builder, str, addnodes.pending_xref, nodes.Element) -> List[Tuple[str, nodes.Element]] # NOQA
|
||||||
objects = self.data['objects']
|
|
||||||
results = [] # type: List[Tuple[str, nodes.Element]]
|
results = [] # type: List[Tuple[str, nodes.Element]]
|
||||||
for objtype in self.object_types:
|
for objtype in self.object_types:
|
||||||
if (objtype, target) in self.data['objects']:
|
todocname = self.objects.get((objtype, target))
|
||||||
|
if todocname:
|
||||||
results.append(('rst:' + self.role_for_objtype(objtype),
|
results.append(('rst:' + self.role_for_objtype(objtype),
|
||||||
make_refnode(builder, fromdocname,
|
make_refnode(builder, fromdocname, todocname,
|
||||||
objects[objtype, target],
|
|
||||||
objtype + '-' + target,
|
objtype + '-' + target,
|
||||||
contnode, target + ' ' + objtype)))
|
contnode, target + ' ' + objtype)))
|
||||||
return results
|
return results
|
||||||
|
@ -37,6 +37,7 @@ if False:
|
|||||||
from sphinx.application import Sphinx # NOQA
|
from sphinx.application import Sphinx # NOQA
|
||||||
from sphinx.builders import Builder # NOQA
|
from sphinx.builders import Builder # NOQA
|
||||||
from sphinx.config import Config # NOQA
|
from sphinx.config import Config # NOQA
|
||||||
|
from sphinx.event import EventManager # NOQA
|
||||||
from sphinx.domains import Domain # NOQA
|
from sphinx.domains import Domain # NOQA
|
||||||
from sphinx.project import Project # NOQA
|
from sphinx.project import Project # NOQA
|
||||||
|
|
||||||
@ -98,6 +99,7 @@ class BuildEnvironment:
|
|||||||
self.srcdir = None # type: str
|
self.srcdir = None # type: str
|
||||||
self.config = None # type: Config
|
self.config = None # type: Config
|
||||||
self.config_status = None # type: int
|
self.config_status = None # type: int
|
||||||
|
self.events = None # type: EventManager
|
||||||
self.project = None # type: Project
|
self.project = None # type: Project
|
||||||
self.version = None # type: Dict[str, str]
|
self.version = None # type: Dict[str, str]
|
||||||
|
|
||||||
@ -193,7 +195,7 @@ class BuildEnvironment:
|
|||||||
# type: () -> Dict
|
# type: () -> Dict
|
||||||
"""Obtains serializable data for pickling."""
|
"""Obtains serializable data for pickling."""
|
||||||
__dict__ = self.__dict__.copy()
|
__dict__ = self.__dict__.copy()
|
||||||
__dict__.update(app=None, domains={}) # clear unpickable attributes
|
__dict__.update(app=None, domains={}, events=None) # clear unpickable attributes
|
||||||
return __dict__
|
return __dict__
|
||||||
|
|
||||||
def __setstate__(self, state):
|
def __setstate__(self, state):
|
||||||
@ -213,6 +215,7 @@ class BuildEnvironment:
|
|||||||
|
|
||||||
self.app = app
|
self.app = app
|
||||||
self.doctreedir = app.doctreedir
|
self.doctreedir = app.doctreedir
|
||||||
|
self.events = app.events
|
||||||
self.srcdir = app.srcdir
|
self.srcdir = app.srcdir
|
||||||
self.project = app.project
|
self.project = app.project
|
||||||
self.version = app.registry.get_envversion(app)
|
self.version = app.registry.get_envversion(app)
|
||||||
@ -310,7 +313,7 @@ class BuildEnvironment:
|
|||||||
|
|
||||||
for domainname, domain in self.domains.items():
|
for domainname, domain in self.domains.items():
|
||||||
domain.merge_domaindata(docnames, other.domaindata[domainname])
|
domain.merge_domaindata(docnames, other.domaindata[domainname])
|
||||||
app.emit('env-merge-info', self, docnames, other)
|
self.events.emit('env-merge-info', self, docnames, other)
|
||||||
|
|
||||||
def path2doc(self, filename):
|
def path2doc(self, filename):
|
||||||
# type: (str) -> Optional[str]
|
# type: (str) -> Optional[str]
|
||||||
@ -452,7 +455,7 @@ class BuildEnvironment:
|
|||||||
def check_dependents(self, app, already):
|
def check_dependents(self, app, already):
|
||||||
# type: (Sphinx, Set[str]) -> Iterator[str]
|
# type: (Sphinx, Set[str]) -> Iterator[str]
|
||||||
to_rewrite = [] # type: List[str]
|
to_rewrite = [] # type: List[str]
|
||||||
for docnames in app.emit('env-get-updated', self):
|
for docnames in self.events.emit('env-get-updated', self):
|
||||||
to_rewrite.extend(docnames)
|
to_rewrite.extend(docnames)
|
||||||
for docname in set(to_rewrite):
|
for docname in set(to_rewrite):
|
||||||
if docname not in already:
|
if docname not in already:
|
||||||
@ -600,7 +603,7 @@ class BuildEnvironment:
|
|||||||
self.temp_data = backup
|
self.temp_data = backup
|
||||||
|
|
||||||
# allow custom references to be resolved
|
# allow custom references to be resolved
|
||||||
self.app.emit('doctree-resolved', doctree, docname)
|
self.events.emit('doctree-resolved', doctree, docname)
|
||||||
|
|
||||||
def collect_relations(self):
|
def collect_relations(self):
|
||||||
# type: () -> Dict[str, List[str]]
|
# type: () -> Dict[str, List[str]]
|
||||||
@ -656,7 +659,7 @@ class BuildEnvironment:
|
|||||||
# call check-consistency for all extensions
|
# call check-consistency for all extensions
|
||||||
for domain in self.domains.values():
|
for domain in self.domains.values():
|
||||||
domain.check_consistency()
|
domain.check_consistency()
|
||||||
self.app.emit('env-check-consistency', self)
|
self.events.emit('env-check-consistency', self)
|
||||||
|
|
||||||
# --------- METHODS FOR COMPATIBILITY --------------------------------------
|
# --------- METHODS FOR COMPATIBILITY --------------------------------------
|
||||||
|
|
||||||
|
@ -10,14 +10,20 @@
|
|||||||
:license: BSD, see LICENSE for details.
|
:license: BSD, see LICENSE for details.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
import warnings
|
||||||
from collections import OrderedDict, defaultdict
|
from collections import OrderedDict, defaultdict
|
||||||
|
|
||||||
|
from sphinx.deprecation import RemovedInSphinx40Warning
|
||||||
from sphinx.errors import ExtensionError
|
from sphinx.errors import ExtensionError
|
||||||
from sphinx.locale import __
|
from sphinx.locale import __
|
||||||
|
from sphinx.util import logging
|
||||||
|
|
||||||
if False:
|
if False:
|
||||||
# For type annotation
|
# For type annotation
|
||||||
from typing import Any, Callable, Dict, List # NOQA
|
from typing import Any, Callable, Dict, List # NOQA
|
||||||
|
from sphinx.application import Sphinx # NOQA
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
# List of all known core events. Maps name to arguments description.
|
# List of all known core events. Maps name to arguments description.
|
||||||
@ -42,20 +48,28 @@ core_events = {
|
|||||||
|
|
||||||
|
|
||||||
class EventManager:
|
class EventManager:
|
||||||
def __init__(self):
|
"""Event manager for Sphinx."""
|
||||||
# type: () -> None
|
|
||||||
|
def __init__(self, app=None):
|
||||||
|
# type: (Sphinx) -> None
|
||||||
|
if app is None:
|
||||||
|
warnings.warn('app argument is required for EventManager.',
|
||||||
|
RemovedInSphinx40Warning)
|
||||||
|
self.app = app
|
||||||
self.events = core_events.copy()
|
self.events = core_events.copy()
|
||||||
self.listeners = defaultdict(OrderedDict) # type: Dict[str, Dict[int, Callable]]
|
self.listeners = defaultdict(OrderedDict) # type: Dict[str, Dict[int, Callable]]
|
||||||
self.next_listener_id = 0
|
self.next_listener_id = 0
|
||||||
|
|
||||||
def add(self, name):
|
def add(self, name):
|
||||||
# type: (str) -> None
|
# type: (str) -> None
|
||||||
|
"""Register a custom Sphinx event."""
|
||||||
if name in self.events:
|
if name in self.events:
|
||||||
raise ExtensionError(__('Event %r already present') % name)
|
raise ExtensionError(__('Event %r already present') % name)
|
||||||
self.events[name] = ''
|
self.events[name] = ''
|
||||||
|
|
||||||
def connect(self, name, callback):
|
def connect(self, name, callback):
|
||||||
# type: (str, Callable) -> int
|
# type: (str, Callable) -> int
|
||||||
|
"""Connect a handler to specific event."""
|
||||||
if name not in self.events:
|
if name not in self.events:
|
||||||
raise ExtensionError(__('Unknown event name: %s') % name)
|
raise ExtensionError(__('Unknown event name: %s') % name)
|
||||||
|
|
||||||
@ -66,18 +80,35 @@ class EventManager:
|
|||||||
|
|
||||||
def disconnect(self, listener_id):
|
def disconnect(self, listener_id):
|
||||||
# type: (int) -> None
|
# type: (int) -> None
|
||||||
|
"""Disconnect a handler."""
|
||||||
for event in self.listeners.values():
|
for event in self.listeners.values():
|
||||||
event.pop(listener_id, None)
|
event.pop(listener_id, None)
|
||||||
|
|
||||||
def emit(self, name, *args):
|
def emit(self, name, *args):
|
||||||
# type: (str, Any) -> List
|
# type: (str, Any) -> List
|
||||||
|
"""Emit a Sphinx event."""
|
||||||
|
try:
|
||||||
|
logger.debug('[app] emitting event: %r%s', name, repr(args)[:100])
|
||||||
|
except Exception:
|
||||||
|
# not every object likes to be repr()'d (think
|
||||||
|
# random stuff coming via autodoc)
|
||||||
|
pass
|
||||||
|
|
||||||
results = []
|
results = []
|
||||||
for callback in self.listeners[name].values():
|
for callback in self.listeners[name].values():
|
||||||
|
if self.app is None:
|
||||||
|
# for compatibility; RemovedInSphinx40Warning
|
||||||
results.append(callback(*args))
|
results.append(callback(*args))
|
||||||
|
else:
|
||||||
|
results.append(callback(self.app, *args))
|
||||||
return results
|
return results
|
||||||
|
|
||||||
def emit_firstresult(self, name, *args):
|
def emit_firstresult(self, name, *args):
|
||||||
# type: (str, Any) -> Any
|
# type: (str, Any) -> Any
|
||||||
|
"""Emit a Sphinx event and returns first result.
|
||||||
|
|
||||||
|
This returns the result of the first handler that doesn't return ``None``.
|
||||||
|
"""
|
||||||
for result in self.emit(name, *args):
|
for result in self.emit(name, *args):
|
||||||
if result is not None:
|
if result is not None:
|
||||||
return result
|
return result
|
||||||
|
@ -405,8 +405,8 @@ class Documenter:
|
|||||||
|
|
||||||
retann = self.retann
|
retann = self.retann
|
||||||
|
|
||||||
result = self.env.app.emit_firstresult(
|
result = self.env.events.emit_firstresult('autodoc-process-signature',
|
||||||
'autodoc-process-signature', self.objtype, self.fullname,
|
self.objtype, self.fullname,
|
||||||
self.object, self.options, args, retann)
|
self.object, self.options, args, retann)
|
||||||
if result:
|
if result:
|
||||||
args, retann = result
|
args, retann = result
|
||||||
@ -993,7 +993,9 @@ class FunctionDocumenter(DocstringSignatureMixin, ModuleLevelDocumenter): # typ
|
|||||||
@classmethod
|
@classmethod
|
||||||
def can_document_member(cls, member, membername, isattr, parent):
|
def can_document_member(cls, member, membername, isattr, parent):
|
||||||
# type: (Any, str, bool, Any) -> bool
|
# type: (Any, str, bool, Any) -> bool
|
||||||
return inspect.isfunction(member) or inspect.isbuiltin(member)
|
# supports functions, builtins and bound methods exported at the module level
|
||||||
|
return (inspect.isfunction(member) or inspect.isbuiltin(member) or
|
||||||
|
(inspect.isroutine(member) and isinstance(parent, ModuleDocumenter)))
|
||||||
|
|
||||||
def format_args(self):
|
def format_args(self):
|
||||||
# type: () -> str
|
# type: () -> str
|
||||||
@ -1347,17 +1349,14 @@ class AttributeDocumenter(DocstringStripSignatureMixin, ClassLevelDocumenter):
|
|||||||
@classmethod
|
@classmethod
|
||||||
def can_document_member(cls, member, membername, isattr, parent):
|
def can_document_member(cls, member, membername, isattr, parent):
|
||||||
# type: (Any, str, bool, Any) -> bool
|
# type: (Any, str, bool, Any) -> bool
|
||||||
non_attr_types = (type, MethodDescriptorType)
|
if inspect.isattributedescriptor(member):
|
||||||
isdatadesc = inspect.isdescriptor(member) and not \
|
return True
|
||||||
cls.is_function_or_method(member) and not \
|
elif (not isinstance(parent, ModuleDocumenter) and
|
||||||
isinstance(member, non_attr_types) and not \
|
|
||||||
type(member).__name__ == "instancemethod"
|
|
||||||
# That last condition addresses an obscure case of C-defined
|
|
||||||
# methods using a deprecated type in Python 3, that is not otherwise
|
|
||||||
# exported anywhere by Python
|
|
||||||
return isdatadesc or (not isinstance(parent, ModuleDocumenter) and
|
|
||||||
not inspect.isroutine(member) and
|
not inspect.isroutine(member) and
|
||||||
not isinstance(member, type))
|
not isinstance(member, type)):
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
|
||||||
def document_members(self, all_members=False):
|
def document_members(self, all_members=False):
|
||||||
# type: (bool) -> None
|
# type: (bool) -> None
|
||||||
@ -1368,8 +1367,7 @@ class AttributeDocumenter(DocstringStripSignatureMixin, ClassLevelDocumenter):
|
|||||||
ret = super().import_object()
|
ret = super().import_object()
|
||||||
if inspect.isenumattribute(self.object):
|
if inspect.isenumattribute(self.object):
|
||||||
self.object = self.object.value
|
self.object = self.object.value
|
||||||
if inspect.isdescriptor(self.object) and \
|
if inspect.isattributedescriptor(self.object):
|
||||||
not self.is_function_or_method(self.object):
|
|
||||||
self._datadescriptor = True
|
self._datadescriptor = True
|
||||||
else:
|
else:
|
||||||
# if it's not a data descriptor
|
# if it's not a data descriptor
|
||||||
|
@ -30,7 +30,8 @@ logger = logging.getLogger(__name__)
|
|||||||
# common option names for autodoc directives
|
# common option names for autodoc directives
|
||||||
AUTODOC_DEFAULT_OPTIONS = ['members', 'undoc-members', 'inherited-members',
|
AUTODOC_DEFAULT_OPTIONS = ['members', 'undoc-members', 'inherited-members',
|
||||||
'show-inheritance', 'private-members', 'special-members',
|
'show-inheritance', 'private-members', 'special-members',
|
||||||
'ignore-module-all', 'exclude-members', 'member-order']
|
'ignore-module-all', 'exclude-members', 'member-order',
|
||||||
|
'imported-members']
|
||||||
|
|
||||||
|
|
||||||
class DummyOptionSpec(dict):
|
class DummyOptionSpec(dict):
|
||||||
|
@ -21,12 +21,15 @@ from subprocess import CalledProcessError, PIPE
|
|||||||
from docutils import nodes
|
from docutils import nodes
|
||||||
|
|
||||||
import sphinx
|
import sphinx
|
||||||
|
from sphinx import package_dir
|
||||||
|
from sphinx.deprecation import RemovedInSphinx40Warning, deprecated_alias
|
||||||
from sphinx.errors import SphinxError
|
from sphinx.errors import SphinxError
|
||||||
from sphinx.locale import _, __
|
from sphinx.locale import _, __
|
||||||
from sphinx.util import logging
|
from sphinx.util import logging
|
||||||
from sphinx.util.math import get_node_equation_number, wrap_displaymath
|
from sphinx.util.math import get_node_equation_number, wrap_displaymath
|
||||||
from sphinx.util.osutil import ensuredir
|
from sphinx.util.osutil import ensuredir
|
||||||
from sphinx.util.png import read_png_depth, write_png_depth
|
from sphinx.util.png import read_png_depth, write_png_depth
|
||||||
|
from sphinx.util.template import LaTeXRenderer
|
||||||
|
|
||||||
if False:
|
if False:
|
||||||
# For type annotation
|
# For type annotation
|
||||||
@ -38,6 +41,8 @@ if False:
|
|||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
templates_path = path.join(package_dir, 'templates', 'imgmath')
|
||||||
|
|
||||||
|
|
||||||
class MathExtError(SphinxError):
|
class MathExtError(SphinxError):
|
||||||
category = 'Math extension error'
|
category = 'Math extension error'
|
||||||
@ -87,19 +92,27 @@ DOC_BODY_PREVIEW = r'''
|
|||||||
depth_re = re.compile(br'\[\d+ depth=(-?\d+)\]')
|
depth_re = re.compile(br'\[\d+ depth=(-?\d+)\]')
|
||||||
|
|
||||||
|
|
||||||
def generate_latex_macro(math, config):
|
def generate_latex_macro(math, config, confdir=''):
|
||||||
# type: (str, Config) -> str
|
# type: (str, Config, str) -> str
|
||||||
"""Generate LaTeX macro."""
|
"""Generate LaTeX macro."""
|
||||||
fontsize = config.imgmath_font_size
|
variables = {
|
||||||
baselineskip = int(round(fontsize * 1.2))
|
'fontsize': config.imgmath_font_size,
|
||||||
|
'baselineskip': int(round(config.imgmath_font_size * 1.2)),
|
||||||
|
'preamble': config.imgmath_latex_preamble,
|
||||||
|
'math': math
|
||||||
|
}
|
||||||
|
|
||||||
latex = DOC_HEAD + config.imgmath_latex_preamble
|
|
||||||
if config.imgmath_use_preview:
|
if config.imgmath_use_preview:
|
||||||
latex += DOC_BODY_PREVIEW % (fontsize, baselineskip, math)
|
template_name = 'preview.tex_t'
|
||||||
else:
|
else:
|
||||||
latex += DOC_BODY % (fontsize, baselineskip, math)
|
template_name = 'template.tex_t'
|
||||||
|
|
||||||
return latex
|
for template_dir in config.templates_path:
|
||||||
|
template = path.join(confdir, template_dir, template_name)
|
||||||
|
if path.exists(template):
|
||||||
|
return LaTeXRenderer().render(template, variables)
|
||||||
|
|
||||||
|
return LaTeXRenderer(templates_path).render(template_name, variables)
|
||||||
|
|
||||||
|
|
||||||
def ensure_tempdir(builder):
|
def ensure_tempdir(builder):
|
||||||
@ -220,7 +233,7 @@ def render_math(self, math):
|
|||||||
if image_format not in SUPPORT_FORMAT:
|
if image_format not in SUPPORT_FORMAT:
|
||||||
raise MathExtError('imgmath_image_format must be either "png" or "svg"')
|
raise MathExtError('imgmath_image_format must be either "png" or "svg"')
|
||||||
|
|
||||||
latex = generate_latex_macro(math, self.builder.config)
|
latex = generate_latex_macro(math, self.builder.config, self.builder.confdir)
|
||||||
|
|
||||||
filename = "%s.%s" % (sha1(latex.encode()).hexdigest(), image_format)
|
filename = "%s.%s" % (sha1(latex.encode()).hexdigest(), image_format)
|
||||||
relfn = posixpath.join(self.builder.imgpath, 'math', filename)
|
relfn = posixpath.join(self.builder.imgpath, 'math', filename)
|
||||||
@ -332,6 +345,15 @@ def html_visit_displaymath(self, node):
|
|||||||
raise nodes.SkipNode
|
raise nodes.SkipNode
|
||||||
|
|
||||||
|
|
||||||
|
deprecated_alias('sphinx.ext.imgmath',
|
||||||
|
{
|
||||||
|
'DOC_BODY': DOC_BODY,
|
||||||
|
'DOC_BODY_PREVIEW': DOC_BODY_PREVIEW,
|
||||||
|
'DOC_HEAD': DOC_HEAD,
|
||||||
|
},
|
||||||
|
RemovedInSphinx40Warning)
|
||||||
|
|
||||||
|
|
||||||
def setup(app):
|
def setup(app):
|
||||||
# type: (Sphinx) -> Dict[str, Any]
|
# type: (Sphinx) -> Dict[str, Any]
|
||||||
app.add_html_math_renderer('imgmath',
|
app.add_html_math_renderer('imgmath',
|
||||||
|
@ -86,7 +86,7 @@ def process_todos(app, doctree):
|
|||||||
if not hasattr(env, 'todo_all_todos'):
|
if not hasattr(env, 'todo_all_todos'):
|
||||||
env.todo_all_todos = [] # type: ignore
|
env.todo_all_todos = [] # type: ignore
|
||||||
for node in doctree.traverse(todo_node):
|
for node in doctree.traverse(todo_node):
|
||||||
app.emit('todo-defined', node)
|
app.events.emit('todo-defined', node)
|
||||||
|
|
||||||
newnode = node.deepcopy()
|
newnode = node.deepcopy()
|
||||||
newnode['ids'] = []
|
newnode['ids'] = []
|
||||||
|
18
sphinx/templates/imgmath/preview.tex_t
Normal file
18
sphinx/templates/imgmath/preview.tex_t
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
\documentclass[12pt]{article}
|
||||||
|
\usepackage[utf8x]{inputenc}
|
||||||
|
\usepackage{amsmath}
|
||||||
|
\usepackage{amsthm}
|
||||||
|
\usepackage{amssymb}
|
||||||
|
\usepackage{amsfonts}
|
||||||
|
\usepackage{anyfontsize}
|
||||||
|
\usepackage{bm}
|
||||||
|
\pagestyle{empty}
|
||||||
|
<%= preamble %>
|
||||||
|
|
||||||
|
\usepackage[active]{preview}
|
||||||
|
|
||||||
|
\begin{document}
|
||||||
|
\begin{preview}
|
||||||
|
\fontsize{<%= fontsize %>}{<%= baselineskip %}}\selectfont <%= math %>
|
||||||
|
\end{preview}
|
||||||
|
\end{document}
|
14
sphinx/templates/imgmath/template.tex_t
Normal file
14
sphinx/templates/imgmath/template.tex_t
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
\documentclass[12pt]{article}
|
||||||
|
\usepackage[utf8x]{inputenc}
|
||||||
|
\usepackage{amsmath}
|
||||||
|
\usepackage{amsthm}
|
||||||
|
\usepackage{amssymb}
|
||||||
|
\usepackage{amsfonts}
|
||||||
|
\usepackage{anyfontsize}
|
||||||
|
\usepackage{bm}
|
||||||
|
\pagestyle{empty}
|
||||||
|
<%= preamble %>
|
||||||
|
|
||||||
|
\begin{document}
|
||||||
|
\fontsize{<%= fontsize %>}{<%= baselineskip %>}\selectfont <%= math %>
|
||||||
|
\end{document}
|
@ -29,7 +29,7 @@ def deprecate_source_parsers(app, config):
|
|||||||
# type: (Sphinx, Config) -> None
|
# type: (Sphinx, Config) -> None
|
||||||
if config.source_parsers:
|
if config.source_parsers:
|
||||||
warnings.warn('The config variable "source_parsers" is deprecated. '
|
warnings.warn('The config variable "source_parsers" is deprecated. '
|
||||||
'Please use app.add_source_parser() API instead.',
|
'Please update your extension for the parser and remove the setting.',
|
||||||
RemovedInSphinx30Warning)
|
RemovedInSphinx30Warning)
|
||||||
for suffix, parser in config.source_parsers.items():
|
for suffix, parser in config.source_parsers.items():
|
||||||
if isinstance(parser, str):
|
if isinstance(parser, str):
|
||||||
|
@ -29,6 +29,17 @@ if False:
|
|||||||
# For type annotation
|
# For type annotation
|
||||||
from typing import Any, Callable, Mapping, List, Tuple, Type # NOQA
|
from typing import Any, Callable, Mapping, List, Tuple, Type # NOQA
|
||||||
|
|
||||||
|
if sys.version_info > (3, 7):
|
||||||
|
from types import (
|
||||||
|
ClassMethodDescriptorType,
|
||||||
|
MethodDescriptorType,
|
||||||
|
WrapperDescriptorType
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
ClassMethodDescriptorType = type(object.__init__)
|
||||||
|
MethodDescriptorType = type(str.join)
|
||||||
|
WrapperDescriptorType = type(dict.__dict__['fromkeys'])
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
memory_address_re = re.compile(r' at 0x[0-9a-f]{8,16}(?=>)', re.IGNORECASE)
|
memory_address_re = re.compile(r' at 0x[0-9a-f]{8,16}(?=>)', re.IGNORECASE)
|
||||||
@ -161,6 +172,34 @@ def isdescriptor(x):
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
def isattributedescriptor(obj):
|
||||||
|
# type: (Any) -> bool
|
||||||
|
"""Check if the object is an attribute like descriptor."""
|
||||||
|
if inspect.isdatadescriptor(object):
|
||||||
|
# data descriptor is kind of attribute
|
||||||
|
return True
|
||||||
|
elif isdescriptor(obj):
|
||||||
|
# non data descriptor
|
||||||
|
if isfunction(obj) or isbuiltin(obj) or inspect.ismethod(obj):
|
||||||
|
# attribute must not be either function, builtin and method
|
||||||
|
return False
|
||||||
|
elif inspect.isclass(obj):
|
||||||
|
# attribute must not be a class
|
||||||
|
return False
|
||||||
|
elif isinstance(obj, (ClassMethodDescriptorType,
|
||||||
|
MethodDescriptorType,
|
||||||
|
WrapperDescriptorType)):
|
||||||
|
# attribute must not be a method descriptor
|
||||||
|
return False
|
||||||
|
elif type(obj).__name__ == "instancemethod":
|
||||||
|
# attribute must not be an instancemethod (C-API)
|
||||||
|
return False
|
||||||
|
else:
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
def isfunction(obj):
|
def isfunction(obj):
|
||||||
# type: (Any) -> bool
|
# type: (Any) -> bool
|
||||||
"""Check if the object is function."""
|
"""Check if the object is function."""
|
||||||
|
@ -67,8 +67,9 @@ class SphinxRenderer(FileRenderer):
|
|||||||
|
|
||||||
|
|
||||||
class LaTeXRenderer(SphinxRenderer):
|
class LaTeXRenderer(SphinxRenderer):
|
||||||
def __init__(self):
|
def __init__(self, template_path=None):
|
||||||
# type: () -> None
|
# type: (str) -> None
|
||||||
|
if template_path is None:
|
||||||
template_path = os.path.join(package_dir, 'templates', 'latex')
|
template_path = os.path.join(package_dir, 'templates', 'latex')
|
||||||
super().__init__(template_path)
|
super().__init__(template_path)
|
||||||
|
|
||||||
|
7
tests/roots/test-ext-autodoc/target/bound_method.py
Normal file
7
tests/roots/test-ext-autodoc/target/bound_method.py
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
class Cls:
|
||||||
|
def method(self):
|
||||||
|
"""Method docstring"""
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
bound_method = Cls().method
|
@ -259,6 +259,11 @@ def test_format_signature():
|
|||||||
assert formatsig('method', 'H.foo', H.foo2, None, None) == '(*c)'
|
assert formatsig('method', 'H.foo', H.foo2, None, None) == '(*c)'
|
||||||
assert formatsig('method', 'H.foo', H.foo3, None, None) == r"(d='\\n')"
|
assert formatsig('method', 'H.foo', H.foo3, None, None) == r"(d='\\n')"
|
||||||
|
|
||||||
|
# test bound methods interpreted as functions
|
||||||
|
assert formatsig('function', 'foo', H().foo1, None, None) == '(b, *c)'
|
||||||
|
assert formatsig('function', 'foo', H().foo2, None, None) == '(*c)'
|
||||||
|
assert formatsig('function', 'foo', H().foo3, None, None) == r"(d='\\n')"
|
||||||
|
|
||||||
# test exception handling (exception is caught and args is '')
|
# test exception handling (exception is caught and args is '')
|
||||||
directive.env.config.autodoc_docstring_signature = False
|
directive.env.config.autodoc_docstring_signature = False
|
||||||
assert formatsig('function', 'int', int, None, None) == ''
|
assert formatsig('function', 'int', int, None, None) == ''
|
||||||
@ -451,6 +456,14 @@ def test_get_doc():
|
|||||||
directive.env.config.autoclass_content = 'both'
|
directive.env.config.autoclass_content = 'both'
|
||||||
assert getdocl('class', I) == ['Class docstring', '', 'New docstring']
|
assert getdocl('class', I) == ['Class docstring', '', 'New docstring']
|
||||||
|
|
||||||
|
# verify that method docstrings get extracted in both normal case
|
||||||
|
# and in case of bound method posing as a function
|
||||||
|
class J: # NOQA
|
||||||
|
def foo(self):
|
||||||
|
"""Method docstring"""
|
||||||
|
assert getdocl('method', J.foo) == ['Method docstring']
|
||||||
|
assert getdocl('function', J().foo) == ['Method docstring']
|
||||||
|
|
||||||
from target import Base, Derived
|
from target import Base, Derived
|
||||||
|
|
||||||
# NOTE: inspect.getdoc seems not to work with locally defined classes
|
# NOTE: inspect.getdoc seems not to work with locally defined classes
|
||||||
@ -1491,6 +1504,23 @@ def test_partialfunction():
|
|||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.usefixtures('setup_test')
|
||||||
|
def test_bound_method():
|
||||||
|
options = {"members": None}
|
||||||
|
actual = do_autodoc(app, 'module', 'target.bound_method', options)
|
||||||
|
assert list(actual) == [
|
||||||
|
'',
|
||||||
|
'.. py:module:: target.bound_method',
|
||||||
|
'',
|
||||||
|
'',
|
||||||
|
'.. py:function:: bound_method()',
|
||||||
|
' :module: target.bound_method',
|
||||||
|
'',
|
||||||
|
' Method docstring',
|
||||||
|
' ',
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.usefixtures('setup_test')
|
@pytest.mark.usefixtures('setup_test')
|
||||||
def test_coroutine():
|
def test_coroutine():
|
||||||
options = {"members": None}
|
options = {"members": None}
|
||||||
@ -1579,6 +1609,8 @@ def test_autodoc_default_options(app):
|
|||||||
assert ' .. py:attribute:: EnumCls.val4' not in actual
|
assert ' .. py:attribute:: EnumCls.val4' not in actual
|
||||||
actual = do_autodoc(app, 'class', 'target.CustomIter')
|
actual = do_autodoc(app, 'class', 'target.CustomIter')
|
||||||
assert ' .. py:method:: target.CustomIter' not in actual
|
assert ' .. py:method:: target.CustomIter' not in actual
|
||||||
|
actual = do_autodoc(app, 'module', 'target')
|
||||||
|
assert '.. py:function:: save_traceback(app)' not in actual
|
||||||
|
|
||||||
# with :members:
|
# with :members:
|
||||||
app.config.autodoc_default_options = {'members': None}
|
app.config.autodoc_default_options = {'members': None}
|
||||||
@ -1642,6 +1674,15 @@ def test_autodoc_default_options(app):
|
|||||||
assert ' .. py:method:: CustomIter.snafucate()' in actual
|
assert ' .. py:method:: CustomIter.snafucate()' in actual
|
||||||
assert ' Makes this snafucated.' in actual
|
assert ' Makes this snafucated.' in actual
|
||||||
|
|
||||||
|
# with :imported-members:
|
||||||
|
app.config.autodoc_default_options = {
|
||||||
|
'members': None,
|
||||||
|
'imported-members': None,
|
||||||
|
'ignore-module-all': None,
|
||||||
|
}
|
||||||
|
actual = do_autodoc(app, 'module', 'target')
|
||||||
|
assert '.. py:function:: save_traceback(app)' in actual
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.sphinx('html', testroot='ext-autodoc')
|
@pytest.mark.sphinx('html', testroot='ext-autodoc')
|
||||||
def test_autodoc_default_options_with_values(app):
|
def test_autodoc_default_options_with_values(app):
|
||||||
|
@ -8,7 +8,14 @@
|
|||||||
:license: BSD, see LICENSE for details.
|
:license: BSD, see LICENSE for details.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
from sphinx import addnodes
|
||||||
|
from sphinx.addnodes import (
|
||||||
|
desc, desc_addname, desc_content, desc_name, desc_optional, desc_parameter,
|
||||||
|
desc_parameterlist, desc_returns, desc_signature
|
||||||
|
)
|
||||||
from sphinx.domains.rst import parse_directive
|
from sphinx.domains.rst import parse_directive
|
||||||
|
from sphinx.testing import restructuredtext
|
||||||
|
from sphinx.testing.util import assert_node
|
||||||
|
|
||||||
|
|
||||||
def test_parse_directive():
|
def test_parse_directive():
|
||||||
@ -23,3 +30,52 @@ def test_parse_directive():
|
|||||||
|
|
||||||
s = parse_directive('.. :: bar')
|
s = parse_directive('.. :: bar')
|
||||||
assert s == ('.. :: bar', '')
|
assert s == ('.. :: bar', '')
|
||||||
|
|
||||||
|
|
||||||
|
def test_rst_directive(app):
|
||||||
|
# bare
|
||||||
|
text = ".. rst:directive:: toctree"
|
||||||
|
doctree = restructuredtext.parse(app, text)
|
||||||
|
assert_node(doctree, (addnodes.index,
|
||||||
|
[desc, ([desc_signature, desc_name, ".. toctree::"],
|
||||||
|
[desc_content, ()])]))
|
||||||
|
assert_node(doctree[0],
|
||||||
|
entries=[("single", "toctree (directive)", "directive-toctree", "", None)])
|
||||||
|
assert_node(doctree[1], addnodes.desc, desctype="directive",
|
||||||
|
domain="rst", objtype="directive", noindex=False)
|
||||||
|
|
||||||
|
# decorated
|
||||||
|
text = ".. rst:directive:: .. toctree::"
|
||||||
|
doctree = restructuredtext.parse(app, text)
|
||||||
|
assert_node(doctree, (addnodes.index,
|
||||||
|
[desc, ([desc_signature, desc_name, ".. toctree::"],
|
||||||
|
[desc_content, ()])]))
|
||||||
|
assert_node(doctree[0],
|
||||||
|
entries=[("single", "toctree (directive)", "directive-toctree", "", None)])
|
||||||
|
assert_node(doctree[1], addnodes.desc, desctype="directive",
|
||||||
|
domain="rst", objtype="directive", noindex=False)
|
||||||
|
|
||||||
|
|
||||||
|
def test_rst_directive_with_argument(app):
|
||||||
|
text = ".. rst:directive:: .. toctree:: foo bar baz"
|
||||||
|
doctree = restructuredtext.parse(app, text)
|
||||||
|
assert_node(doctree, (addnodes.index,
|
||||||
|
[desc, ([desc_signature, ([desc_name, ".. toctree::"],
|
||||||
|
[desc_addname, " foo bar baz"])],
|
||||||
|
[desc_content, ()])]))
|
||||||
|
assert_node(doctree[0],
|
||||||
|
entries=[("single", "toctree (directive)", "directive-toctree", "", None)])
|
||||||
|
assert_node(doctree[1], addnodes.desc, desctype="directive",
|
||||||
|
domain="rst", objtype="directive", noindex=False)
|
||||||
|
|
||||||
|
|
||||||
|
def test_rst_role(app):
|
||||||
|
text = ".. rst:role:: ref"
|
||||||
|
doctree = restructuredtext.parse(app, text)
|
||||||
|
assert_node(doctree, (addnodes.index,
|
||||||
|
[desc, ([desc_signature, desc_name, ":ref:"],
|
||||||
|
[desc_content, ()])]))
|
||||||
|
assert_node(doctree[0],
|
||||||
|
entries=[("single", "ref (role)", "role-ref", "", None)])
|
||||||
|
assert_node(doctree[1], addnodes.desc, desctype="role",
|
||||||
|
domain="rst", objtype="role", noindex=False)
|
||||||
|
@ -7,8 +7,12 @@
|
|||||||
:copyright: Copyright 2007-2019 by the Sphinx team, see AUTHORS.
|
:copyright: Copyright 2007-2019 by the Sphinx team, see AUTHORS.
|
||||||
:license: BSD, see LICENSE for details.
|
:license: BSD, see LICENSE for details.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
import _testcapi
|
||||||
|
import datetime
|
||||||
import functools
|
import functools
|
||||||
import sys
|
import sys
|
||||||
|
import types
|
||||||
from textwrap import dedent
|
from textwrap import dedent
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
@ -432,3 +436,26 @@ def test_isdescriptor(app):
|
|||||||
assert inspect.isdescriptor(Base.meth) is True # method of class
|
assert inspect.isdescriptor(Base.meth) is True # method of class
|
||||||
assert inspect.isdescriptor(Base().meth) is True # method of instance
|
assert inspect.isdescriptor(Base().meth) is True # method of instance
|
||||||
assert inspect.isdescriptor(func) is True # function
|
assert inspect.isdescriptor(func) is True # function
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.sphinx(testroot='ext-autodoc')
|
||||||
|
def test_isattributedescriptor(app):
|
||||||
|
from target.methods import Base
|
||||||
|
|
||||||
|
class Descriptor:
|
||||||
|
def __get__(self, obj, typ=None):
|
||||||
|
pass
|
||||||
|
|
||||||
|
testinstancemethod = _testcapi.instancemethod(str.__repr__)
|
||||||
|
|
||||||
|
assert inspect.isattributedescriptor(Base.prop) is True # property
|
||||||
|
assert inspect.isattributedescriptor(Base.meth) is False # method
|
||||||
|
assert inspect.isattributedescriptor(Base.staticmeth) is False # staticmethod
|
||||||
|
assert inspect.isattributedescriptor(Base.classmeth) is False # classmetho
|
||||||
|
assert inspect.isattributedescriptor(Descriptor) is False # custom descriptor class # NOQA
|
||||||
|
assert inspect.isattributedescriptor(str.join) is False # MethodDescriptorType # NOQA
|
||||||
|
assert inspect.isattributedescriptor(object.__init__) is False # WrapperDescriptorType # NOQA
|
||||||
|
assert inspect.isattributedescriptor(dict.__dict__['fromkeys']) is False # ClassMethodDescriptorType # NOQA
|
||||||
|
assert inspect.isattributedescriptor(types.FrameType.f_locals) is True # GetSetDescriptorType # NOQA
|
||||||
|
assert inspect.isattributedescriptor(datetime.timedelta.days) is True # MemberDescriptorType # NOQA
|
||||||
|
assert inspect.isattributedescriptor(testinstancemethod) is False # instancemethod (C-API) # NOQA
|
||||||
|
Loading…
Reference in New Issue
Block a user