Merge branch 'master' into 3866_suppress_pydomain_ref_warning

This commit is contained in:
Takeshi KOMIYA 2017-06-25 21:33:53 +09:00 committed by GitHub
commit 7358081bd0
30 changed files with 935 additions and 361 deletions

48
.appveyor.yml Normal file
View File

@ -0,0 +1,48 @@
environment:
global:
TEST: -v --durations 25
PYTHONFAULTHANDLER: x
PYTHONWARNINGS: all
matrix:
- PYTHON: 27
DOCUTILS: 0.12
TEST_IGNORE: --ignore py35
- PYTHON: 27
DOCUTILS: 0.13.1
TEST_IGNORE: --ignore py35
- PYTHON: 36
DOCUTILS: 0.13.1
- PYTHON: 36-x64
DOCUTILS: 0.13.1
install:
- C:\Python%PYTHON%\python.exe -m pip install -U pip setuptools
- C:\Python%PYTHON%\python.exe -m pip install docutils==%DOCUTILS%
- C:\Python%PYTHON%\python.exe -m pip install -r test-reqs.txt
# No automatic build, just run python tests
build: off
# Update build information before testing, no warnings during this step
before_test:
- ps: |
$py_warnings = $env:PYTHONWARNINGS
$env:PYTHONWARNINGS = 'ignore'
Update-AppveyorBuild -Version ((& "C:\Python$($env:PYTHON)\python.exe" -m sphinx --version).Split(' ')[2])
$env:PYTHONWARNINGS = $py_warnings
test_script:
- ps: |
Push-Location tests
$test_ignore = $env:TEST_IGNORE
if (-not $test_ignore) { $test_ignore = '' }
$tests = $env:TEST
if (-not $tests) { $tests = '' }
& "C:\Python$($env:PYTHON)\python.exe" run.py $test_ignore.Split(' ') --junitxml .junit.xml $tests.Split(' ')
Pop-Location
if ($LastExitCode -eq 1) { Write-Host "Test Failures Occurred, leaving for test result parsing" }
elseif ($LastExitCode -ne 0) { Write-Host "Other Error Occurred, aborting"; exit $LastExitCode }
after_test:
- ps: (New-Object System.Net.WebClient).UploadFile("https://ci.appveyor.com/api/testresults/junit/$($env:APPVEYOR_JOB_ID)", (Resolve-Path (Join-Path tests .junit.xml)))

17
CHANGES
View File

@ -50,6 +50,8 @@ Features removed
Bugs fixed
----------
* #3882: Update the order of files for HTMLHelp and QTHelp
Testing
--------
@ -68,10 +70,13 @@ Deprecated
Features added
--------------
* Now :confval:`suppress_warnings` accepts following configurations:
- ``ref.python`` (ref: #3866)
* latex: hint that code-block continues on next page (refs: #3764, #3792)
Bugs fixed
----------
@ -85,6 +90,16 @@ Bugs fixed
name contains spaces
* #3850: Fix color handling in make mode's help command
* #3865: use of self.env.warn in sphinx extension fails
* #3824: production lists apply smart quotes transform since Sphinx 1.6.1
* latex: fix ``\sphinxbfcode`` swallows initial space of argument
* #3878: Quotes in auto-documented class attributes should be straight quotes
in PDF output
* #3881: LaTeX figure floated to next page sometimes leaves extra vertical
whitespace
* #3885: duplicated footnotes raises IndexError
* #3873: Failure of deprecation warning mechanism of
``sphinx.util.compat.Directive``
* #3874: Bogus warnings for "citation not referenced" for cross-file citations
Testing
--------
@ -300,6 +315,8 @@ Bugs fixed
* #3661: sphinx-build crashes on parallel build
* #3669: gettext builder fails with "ValueError: substring not found"
* #3660: Sphinx always depends on sphinxcontrib-websupport and its dependencies
* #3472: smart quotes getting wrong in latex (at least with list of strings via
autoattribute) (refs: #3345, #3666)
1.6b3

View File

@ -1,5 +1,7 @@
.. highlight:: rest
.. _math-support:
Math support in Sphinx
======================
@ -231,10 +233,14 @@ Sphinx.
The path to the JavaScript file to include in the HTML files in order to load
MathJax.
The default is the ``http://`` URL that loads the JS files from the `MathJax
CDN <http://docs.mathjax.org/en/latest/start.html>`_. If you want MathJax to
be available offline, you have to download it and set this value to a
different path.
The default is the ``https://`` URL that loads the JS files from the
`cdnjs`__ Content Delivery Network. See the `MathJax Getting Started
page`__ for details. If you want MathJax to be available offline, you have
to download it and set this value to a different path.
__ https://cdjns.com
__ http://docs.mathjax.org/en/latest/start.html
The path can be absolute or relative; if it is relative, it is relative to
the ``_static`` directory of the built docs.

View File

@ -189,6 +189,15 @@ The available styling options
default ``true``. Tells whether long lines in :rst:dir:`code-block`\ 's
contents should wrap.
``verbatimhintsturnover``
default ``false``. If ``true``, code-blocks display "continued on next
page", "continued from previous page" hints in case of pagebreaks.
.. versionadded:: 1.6.3
the default will change to ``true`` at 1.7 and horizontal positioning
of continuation hints (currently right aligned only) will be
customizable.
``parsedliteralwraps``
default ``true``. Tells whether long lines in :dudir:`parsed-literal`\ 's
contents should wrap.
@ -347,9 +356,10 @@ Macros
with LaTeX packages.
- more text styling: ``\sphinxstyle<bar>`` with ``<bar>`` one of
``indexentry``, ``indexextra``, ``indexpageref``, ``topictitle``,
``sidebartitle``, ``othertitle``, ``sidebarsubtitle``, ``theadfamily``,
``emphasis``, ``literalemphasis``, ``strong``, ``literalstrong``,
``abbreviation``, ``literalintitle``.
``sidebartitle``, ``othertitle``, ``sidebarsubtitle``,
``theadfamily``, ``emphasis``, ``literalemphasis``, ``strong``,
``literalstrong``, ``abbreviation``, ``literalintitle``, ``codecontinued``,
``codecontinues``.
.. versionadded:: 1.5
these macros were formerly hard-coded as non customizable ``\texttt``,
@ -357,6 +367,8 @@ Macros
.. versionadded:: 1.6
``\sphinxstyletheadfamily`` which defaults to ``\sffamily`` and allows
multiple paragraphs in header cells of tables.
.. versionadded:: 1.6.3
``\sphinxstylecodecontinued`` and ``\sphinxstylecodecontinues``.
- by default the Sphinx style file ``sphinx.sty`` executes the command
``\fvset{fontsize=\small}`` as part of its configuration of
``fancyvrb.sty``. This may be overriden for example via

View File

@ -210,7 +210,17 @@ Including content based on tags
Tables
------
Use :ref:`standard reStructuredText tables <rst-tables>`. They work fine in
Use :ref:`reStructuredText tables <rst-tables>`, i.e. either
- grid table syntax (:duref:`ref <grid-tables>`),
- simple table syntax (:duref:`ref <simple-tables>`),
- :dudir:`csv-table` syntax,
- or :dudir:`list-table` syntax.
The :dudir:`table` directive serves as optional wrapper of the *grid* and
*simple* syntaxes.
They work fine in
HTML output, however there are some gotchas when using tables in LaTeX: the
column width is hard to determine correctly automatically. For this reason, the
following directive exists:
@ -313,6 +323,11 @@ following directive exists:
Sphinx's merged cells interact well with ``p{width}``, ``\X{a}{b}``, ``Y{f}``
and tabulary's columns.
Math
----
See :ref:`math-support`.
.. rubric:: Footnotes
.. [#] For most builders name and format are the same. At the moment only

View File

@ -159,7 +159,7 @@ rendered as "The next paragraph is a code sample:".
Tables
------
Two forms of tables are supported. For *grid tables* (:duref:`ref
For *grid tables* (:duref:`ref
<grid-tables>`), you have to "paint" the cell grid yourself. They look like
this::
@ -173,7 +173,7 @@ this::
+------------------------+------------+----------+----------+
*Simple tables* (:duref:`ref <simple-tables>`) are easier to write, but
limited: they must contain more than one row, and the first column cannot
limited: they must contain more than one row, and the first column cells cannot
contain multiple lines. They look like this::
===== ===== =======
@ -185,6 +185,8 @@ contain multiple lines. They look like this::
True True True
===== ===== =======
Two more syntaxes are supported: *CSV tables* and *List tables*. They use an
*explicit markup block*, see `Directives`_ section.
Hyperlinks
----------

View File

@ -246,7 +246,7 @@ class HTMLHelpBuilder(StandaloneHTMLBuilder):
olen = len(outdir)
for root, dirs, files in os.walk(outdir):
staticdir = root.startswith(path.join(outdir, '_static'))
for fn in files:
for fn in sorted(files):
if (staticdir and not fn.endswith('.js')) or \
fn.endswith('.html'):
print(path.join(root, fn)[olen:].replace(os.sep, '\\'),

View File

@ -188,7 +188,7 @@ class QtHelpBuilder(StandaloneHTMLBuilder):
for root, dirs, files in os.walk(outdir):
resourcedir = root.startswith(staticdir) or \
root.startswith(imagesdir)
for fn in files:
for fn in sorted(files):
if (resourcedir and not fn.endswith('.js')) or \
fn.endswith('.html'):
filename = path.join(root, fn)[olen:]

View File

@ -492,8 +492,8 @@ class StandardDomain(Domain):
initial_data = {
'progoptions': {}, # (program, name) -> docname, labelid
'objects': {}, # (type, name) -> docname, labelid
'citations': {}, # name -> docname, labelid, lineno
'citation_refs': {}, # labelid -> list of docnames
'citations': {}, # citation_name -> docname, labelid, lineno
'citation_refs': {}, # citation_name -> list of docnames
'labels': { # labelname -> docname, labelid, sectionname
'genindex': ('genindex', '', l_('Index')),
'modindex': ('py-modindex', '', l_('Module Index')),
@ -588,12 +588,11 @@ class StandardDomain(Domain):
def note_citation_refs(self, env, docname, document):
# type: (BuildEnvironment, unicode, nodes.Node) -> None
for name, refs in iteritems(document.citation_refs):
for ref in refs:
labelid = ref.get('refid')
if labelid:
citation_refs = self.data['citation_refs'].setdefault(labelid, [])
citation_refs.append(docname)
for node in document.traverse(addnodes.pending_xref):
if node['refdomain'] == 'std' and node['reftype'] == 'citation':
label = node['reftarget']
citation_refs = self.data['citation_refs'].setdefault(label, [])
citation_refs.append(docname)
def note_labels(self, env, docname, document):
# type: (BuildEnvironment, unicode, nodes.Node) -> None
@ -638,7 +637,7 @@ class StandardDomain(Domain):
def check_consistency(self):
# type: () -> None
for name, (docname, labelid, lineno) in iteritems(self.data['citations']):
if labelid not in self.data['citation_refs']:
if name not in self.data['citation_refs']:
logger.warning('Citation [%s] is not referenced.', name,
type='ref', subtype='citation',
location=(docname, lineno))

View File

@ -16,10 +16,8 @@ import sys
import inspect
import traceback
import warnings
from types import FunctionType, MethodType, ModuleType
from six import PY2, iterkeys, iteritems, itervalues, text_type, class_types, \
string_types, StringIO
from six import PY2, iterkeys, iteritems, itervalues, text_type, class_types, string_types
from docutils import nodes
from docutils.utils import assemble_option_dict
@ -27,31 +25,26 @@ from docutils.parsers.rst import Directive
from docutils.statemachine import ViewList
import sphinx
from sphinx.ext.autodoc.importer import _MockImporter
from sphinx.ext.autodoc.inspector import format_annotation, formatargspec # to keep compatibility # NOQA
from sphinx.util import rpartition, force_decode
from sphinx.locale import _
from sphinx.pycode import ModuleAnalyzer, PycodeError
from sphinx.application import ExtensionError
from sphinx.util import logging
from sphinx.util.nodes import nested_parse_with_titles
from sphinx.util.inspect import getargspec, isdescriptor, safe_getmembers, \
from sphinx.util.inspect import Signature, isdescriptor, safe_getmembers, \
safe_getattr, object_description, is_builtin_class_method, \
isenumclass, isenumattribute
from sphinx.util.docstrings import prepare_docstring
if False:
# For type annotation
from types import ModuleType # NOQA
from typing import Any, Callable, Dict, Iterator, List, Sequence, Set, Tuple, Type, Union # NOQA
from docutils.utils import Reporter # NOQA
from sphinx.application import Sphinx # NOQA
try:
if sys.version_info >= (3,):
import typing
else:
typing = None
except ImportError:
typing = None
logger = logging.getLogger(__name__)
# This type isn't exposed directly in any modules, but can be found
@ -106,103 +99,6 @@ class Options(dict):
return None
class _MockObject(object):
"""Used by autodoc_mock_imports."""
def __init__(self, *args, **kwargs):
# type: (Any, Any) -> None
pass
def __len__(self):
# type: () -> int
return 0
def __contains__(self, key):
# type: (str) -> bool
return False
def __iter__(self):
# type: () -> None
pass
def __getitem__(self, key):
# type: (str) -> _MockObject
return self
def __getattr__(self, key):
# type: (str) -> _MockObject
return self
def __call__(self, *args, **kw):
# type: (Any, Any) -> Any
if args and type(args[0]) in [FunctionType, MethodType]:
# Appears to be a decorator, pass through unchanged
return args[0]
return self
class _MockModule(ModuleType):
"""Used by autodoc_mock_imports."""
__file__ = '/dev/null'
def __init__(self, name, loader):
# type: (str, _MockImporter) -> None
self.__name__ = self.__package__ = name
self.__loader__ = loader
self.__all__ = [] # type: List[str]
self.__path__ = [] # type: List[str]
def __getattr__(self, name):
# type: (str) -> _MockObject
o = _MockObject()
o.__module__ = self.__name__
return o
class _MockImporter(object):
def __init__(self, names):
# type: (List[str]) -> None
self.base_packages = set() # type: Set[str]
for n in names:
# Convert module names:
# ['a.b.c', 'd.e']
# to a set of base packages:
# set(['a', 'd'])
self.base_packages.add(n.split('.')[0])
self.mocked_modules = [] # type: List[str]
# enable hook by adding itself to meta_path
sys.meta_path = sys.meta_path + [self]
def disable(self):
# remove `self` from `sys.meta_path` to disable import hook
sys.meta_path = [i for i in sys.meta_path if i is not self]
# remove mocked modules from sys.modules to avoid side effects after
# running auto-documenter
for m in self.mocked_modules:
if m in sys.modules:
del sys.modules[m]
def find_module(self, name, path=None):
# type: (str, str) -> Any
base_package = name.split('.')[0]
if base_package in self.base_packages:
return self
return None
def load_module(self, name):
# type: (str) -> ModuleType
if name in sys.modules:
# module has already been imported, return it
return sys.modules[name]
else:
logger.debug('[autodoc] adding a mock module %s!', name)
module = _MockModule(name, self)
sys.modules[name] = module
self.mocked_modules.append(name)
return module
ALL = object()
INSTANCEATTR = object()
@ -359,162 +255,6 @@ def between(marker, what=None, keepempty=False, exclude=False):
return process
def format_annotation(annotation):
# type: (Any) -> str
"""Return formatted representation of a type annotation.
Show qualified names for types and additional details for types from
the ``typing`` module.
Displaying complex types from ``typing`` relies on its private API.
"""
if typing and isinstance(annotation, typing.TypeVar): # type: ignore
return annotation.__name__
if annotation == Ellipsis:
return '...'
if not isinstance(annotation, type):
return repr(annotation)
qualified_name = (annotation.__module__ + '.' + annotation.__qualname__ # type: ignore
if annotation else repr(annotation))
if annotation.__module__ == 'builtins':
return annotation.__qualname__ # type: ignore
elif typing:
if hasattr(typing, 'GenericMeta') and \
isinstance(annotation, typing.GenericMeta):
# In Python 3.5.2+, all arguments are stored in __args__,
# whereas __parameters__ only contains generic parameters.
#
# Prior to Python 3.5.2, __args__ is not available, and all
# arguments are in __parameters__.
params = None
if hasattr(annotation, '__args__'):
if annotation.__args__ is None or len(annotation.__args__) <= 2:
params = annotation.__args__
else: # typing.Callable
args = ', '.join(format_annotation(a) for a in annotation.__args__[:-1])
result = format_annotation(annotation.__args__[-1])
return '%s[[%s], %s]' % (qualified_name, args, result)
elif hasattr(annotation, '__parameters__'):
params = annotation.__parameters__
if params is not None:
param_str = ', '.join(format_annotation(p) for p in params)
return '%s[%s]' % (qualified_name, param_str)
elif hasattr(typing, 'UnionMeta') and \
isinstance(annotation, typing.UnionMeta) and \
hasattr(annotation, '__union_params__'):
params = annotation.__union_params__
if params is not None:
param_str = ', '.join(format_annotation(p) for p in params)
return '%s[%s]' % (qualified_name, param_str)
elif hasattr(typing, 'CallableMeta') and \
isinstance(annotation, typing.CallableMeta) and \
getattr(annotation, '__args__', None) is not None and \
hasattr(annotation, '__result__'):
# Skipped in the case of plain typing.Callable
args = annotation.__args__
if args is None:
return qualified_name
elif args is Ellipsis:
args_str = '...'
else:
formatted_args = (format_annotation(a) for a in args)
args_str = '[%s]' % ', '.join(formatted_args)
return '%s[%s, %s]' % (qualified_name,
args_str,
format_annotation(annotation.__result__))
elif hasattr(typing, 'TupleMeta') and \
isinstance(annotation, typing.TupleMeta) and \
hasattr(annotation, '__tuple_params__') and \
hasattr(annotation, '__tuple_use_ellipsis__'):
params = annotation.__tuple_params__
if params is not None:
param_strings = [format_annotation(p) for p in params]
if annotation.__tuple_use_ellipsis__:
param_strings.append('...')
return '%s[%s]' % (qualified_name,
', '.join(param_strings))
return qualified_name
def formatargspec(function, args, varargs=None, varkw=None, defaults=None,
kwonlyargs=(), kwonlydefaults={}, annotations={}):
# type: (Callable, Tuple[str, ...], str, str, Any, Tuple, Dict, Dict[str, Any]) -> str
"""Return a string representation of an ``inspect.FullArgSpec`` tuple.
An enhanced version of ``inspect.formatargspec()`` that handles typing
annotations better.
"""
def format_arg_with_annotation(name):
# type: (str) -> str
if name in annotations:
return '%s: %s' % (name, format_annotation(get_annotation(name)))
return name
def get_annotation(name):
# type: (str) -> str
value = annotations[name]
if isinstance(value, string_types):
return introspected_hints.get(name, value)
else:
return value
introspected_hints = (typing.get_type_hints(function) # type: ignore
if typing and hasattr(function, '__code__') else {})
fd = StringIO()
fd.write('(')
formatted = []
defaults_start = len(args) - len(defaults) if defaults else len(args)
for i, arg in enumerate(args):
arg_fd = StringIO()
if isinstance(arg, list):
# support tupled arguments list (only for py2): def foo((x, y))
arg_fd.write('(')
arg_fd.write(format_arg_with_annotation(arg[0]))
for param in arg[1:]:
arg_fd.write(', ')
arg_fd.write(format_arg_with_annotation(param))
arg_fd.write(')')
else:
arg_fd.write(format_arg_with_annotation(arg))
if defaults and i >= defaults_start:
arg_fd.write(' = ' if arg in annotations else '=')
arg_fd.write(object_description(defaults[i - defaults_start])) # type: ignore
formatted.append(arg_fd.getvalue())
if varargs:
formatted.append('*' + format_arg_with_annotation(varargs))
if kwonlyargs:
if not varargs:
formatted.append('*')
for kwarg in kwonlyargs:
arg_fd = StringIO()
arg_fd.write(format_arg_with_annotation(kwarg))
if kwonlydefaults and kwarg in kwonlydefaults:
arg_fd.write(' = ' if kwarg in annotations else '=')
arg_fd.write(object_description(kwonlydefaults[kwarg])) # type: ignore
formatted.append(arg_fd.getvalue())
if varkw:
formatted.append('**' + format_arg_with_annotation(varkw))
fd.write(', '.join(formatted))
fd.write(')')
if 'return' in annotations:
fd.write(' -> ')
fd.write(format_annotation(get_annotation('return')))
return fd.getvalue()
class Documenter(object):
"""
A Documenter knows how to autodocument a single object type. When
@ -1358,7 +1098,7 @@ class FunctionDocumenter(DocstringSignatureMixin, ModuleLevelDocumenter): # typ
# cannot introspect arguments of a C function or method
return None
try:
argspec = getargspec(self.object)
args = Signature(self.object).format_args()
except TypeError:
if (is_builtin_class_method(self.object, '__new__') and
is_builtin_class_method(self.object, '__init__')):
@ -1368,12 +1108,10 @@ class FunctionDocumenter(DocstringSignatureMixin, ModuleLevelDocumenter): # typ
# typing) we try to use the constructor signature as function
# signature without the first argument.
try:
argspec = getargspec(self.object.__new__)
args = Signature(self.object.__new__, bound_method=True).format_args()
except TypeError:
argspec = getargspec(self.object.__init__)
if argspec[0]:
del argspec[0][0]
args = formatargspec(self.object, *argspec)
args = Signature(self.object.__init__, bound_method=True).format_args()
# escape backslashes for reST
args = args.replace('\\', '\\\\')
return args
@ -1425,14 +1163,11 @@ class ClassDocumenter(DocstringSignatureMixin, ModuleLevelDocumenter): # type:
not(inspect.ismethod(initmeth) or inspect.isfunction(initmeth)):
return None
try:
argspec = getargspec(initmeth)
return Signature(initmeth, bound_method=True).format_args()
except TypeError:
# still not possible: happens e.g. for old-style classes
# with __init__ in C
return None
if argspec[0] and argspec[0][0] in ('cls', 'self'):
del argspec[0][0]
return formatargspec(initmeth, *argspec)
def format_signature(self):
# type: () -> unicode
@ -1619,10 +1354,7 @@ class MethodDocumenter(DocstringSignatureMixin, ClassLevelDocumenter): # type:
inspect.ismethoddescriptor(self.object):
# can never get arguments of a C function or method
return None
argspec = getargspec(self.object)
if argspec[0] and argspec[0][0] in ('cls', 'self'):
del argspec[0][0]
args = formatargspec(self.object, *argspec)
args = Signature(self.object, bound_method=True).format_args()
# escape backslashes for reST
args = args.replace('\\', '\\\\')
return args

View File

@ -0,0 +1,118 @@
# -*- coding: utf-8 -*-
"""
sphinx.ext.autodoc.importer
~~~~~~~~~~~~~~~~~~~~~~~~~~~
Importer utilities for autodoc
:copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
import sys
from types import FunctionType, MethodType, ModuleType
from sphinx.util import logging
if False:
# For type annotation
from typing import Any, List, Set # NOQA
logger = logging.getLogger(__name__)
class _MockObject(object):
"""Used by autodoc_mock_imports."""
def __init__(self, *args, **kwargs):
# type: (Any, Any) -> None
pass
def __len__(self):
# type: () -> int
return 0
def __contains__(self, key):
# type: (str) -> bool
return False
def __iter__(self):
# type: () -> None
pass
def __getitem__(self, key):
# type: (str) -> _MockObject
return self
def __getattr__(self, key):
# type: (str) -> _MockObject
return self
def __call__(self, *args, **kw):
# type: (Any, Any) -> Any
if args and type(args[0]) in [FunctionType, MethodType]:
# Appears to be a decorator, pass through unchanged
return args[0]
return self
class _MockModule(ModuleType):
"""Used by autodoc_mock_imports."""
__file__ = '/dev/null'
def __init__(self, name, loader):
# type: (str, _MockImporter) -> None
self.__name__ = self.__package__ = name
self.__loader__ = loader
self.__all__ = [] # type: List[str]
self.__path__ = [] # type: List[str]
def __getattr__(self, name):
# type: (str) -> _MockObject
o = _MockObject()
o.__module__ = self.__name__
return o
class _MockImporter(object):
def __init__(self, names):
# type: (List[str]) -> None
self.base_packages = set() # type: Set[str]
for n in names:
# Convert module names:
# ['a.b.c', 'd.e']
# to a set of base packages:
# set(['a', 'd'])
self.base_packages.add(n.split('.')[0])
self.mocked_modules = [] # type: List[str]
# enable hook by adding itself to meta_path
sys.meta_path = sys.meta_path + [self]
def disable(self):
# remove `self` from `sys.meta_path` to disable import hook
sys.meta_path = [i for i in sys.meta_path if i is not self]
# remove mocked modules from sys.modules to avoid side effects after
# running auto-documenter
for m in self.mocked_modules:
if m in sys.modules:
del sys.modules[m]
def find_module(self, name, path=None):
# type: (str, str) -> Any
base_package = name.split('.')[0]
if base_package in self.base_packages:
return self
return None
def load_module(self, name):
# type: (str) -> ModuleType
if name in sys.modules:
# module has already been imported, return it
return sys.modules[name]
else:
logger.debug('[autodoc] adding a mock module %s!', name)
module = _MockModule(name, self)
sys.modules[name] = module
self.mocked_modules.append(name)
return module

View File

@ -0,0 +1,176 @@
# -*- coding: utf-8 -*-
"""
sphinx.ext.autodoc.inspector
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Inspect utilities for autodoc
:copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
import typing
from six import StringIO, string_types
from sphinx.util.inspect import object_description
if False:
# For type annotation
from typing import Any, Callable, Dict, Tuple # NOQA
def format_annotation(annotation):
# type: (Any) -> str
"""Return formatted representation of a type annotation.
Show qualified names for types and additional details for types from
the ``typing`` module.
Displaying complex types from ``typing`` relies on its private API.
"""
if isinstance(annotation, typing.TypeVar): # type: ignore
return annotation.__name__
if annotation == Ellipsis:
return '...'
if not isinstance(annotation, type):
return repr(annotation)
qualified_name = (annotation.__module__ + '.' + annotation.__qualname__ # type: ignore
if annotation else repr(annotation))
if annotation.__module__ == 'builtins':
return annotation.__qualname__ # type: ignore
else:
if hasattr(typing, 'GenericMeta') and \
isinstance(annotation, typing.GenericMeta):
# In Python 3.5.2+, all arguments are stored in __args__,
# whereas __parameters__ only contains generic parameters.
#
# Prior to Python 3.5.2, __args__ is not available, and all
# arguments are in __parameters__.
params = None
if hasattr(annotation, '__args__'):
if annotation.__args__ is None or len(annotation.__args__) <= 2: # type: ignore # NOQA
params = annotation.__args__ # type: ignore
else: # typing.Callable
args = ', '.join(format_annotation(a) for a in annotation.__args__[:-1]) # type: ignore # NOQA
result = format_annotation(annotation.__args__[-1]) # type: ignore
return '%s[[%s], %s]' % (qualified_name, args, result)
elif hasattr(annotation, '__parameters__'):
params = annotation.__parameters__ # type: ignore
if params is not None:
param_str = ', '.join(format_annotation(p) for p in params)
return '%s[%s]' % (qualified_name, param_str)
elif (hasattr(typing, 'UnionMeta') and
isinstance(annotation, typing.UnionMeta) and # type: ignore
hasattr(annotation, '__union_params__')):
params = annotation.__union_params__ # type: ignore
if params is not None:
param_str = ', '.join(format_annotation(p) for p in params)
return '%s[%s]' % (qualified_name, param_str)
elif (hasattr(typing, 'CallableMeta') and
isinstance(annotation, typing.CallableMeta) and # type: ignore
getattr(annotation, '__args__', None) is not None and
hasattr(annotation, '__result__')):
# Skipped in the case of plain typing.Callable
args = annotation.__args__ # type: ignore
if args is None:
return qualified_name
elif args is Ellipsis:
args_str = '...'
else:
formatted_args = (format_annotation(a) for a in args)
args_str = '[%s]' % ', '.join(formatted_args)
return '%s[%s, %s]' % (qualified_name,
args_str,
format_annotation(annotation.__result__)) # type: ignore
elif (hasattr(typing, 'TupleMeta') and
isinstance(annotation, typing.TupleMeta) and # type: ignore
hasattr(annotation, '__tuple_params__') and
hasattr(annotation, '__tuple_use_ellipsis__')):
params = annotation.__tuple_params__ # type: ignore
if params is not None:
param_strings = [format_annotation(p) for p in params]
if annotation.__tuple_use_ellipsis__: # type: ignore
param_strings.append('...')
return '%s[%s]' % (qualified_name,
', '.join(param_strings))
return qualified_name
def formatargspec(function, args, varargs=None, varkw=None, defaults=None,
kwonlyargs=(), kwonlydefaults={}, annotations={}):
# type: (Callable, Tuple[str, ...], str, str, Any, Tuple, Dict, Dict[str, Any]) -> str
"""Return a string representation of an ``inspect.FullArgSpec`` tuple.
An enhanced version of ``inspect.formatargspec()`` that handles typing
annotations better.
"""
def format_arg_with_annotation(name):
# type: (str) -> str
if name in annotations:
return '%s: %s' % (name, format_annotation(get_annotation(name)))
return name
def get_annotation(name):
# type: (str) -> str
value = annotations[name]
if isinstance(value, string_types):
return introspected_hints.get(name, value)
else:
return value
introspected_hints = (typing.get_type_hints(function)
if typing and hasattr(function, '__code__') else {})
fd = StringIO()
fd.write('(')
formatted = []
defaults_start = len(args) - len(defaults) if defaults else len(args)
for i, arg in enumerate(args):
arg_fd = StringIO()
if isinstance(arg, list):
# support tupled arguments list (only for py2): def foo((x, y))
arg_fd.write('(')
arg_fd.write(format_arg_with_annotation(arg[0]))
for param in arg[1:]:
arg_fd.write(', ')
arg_fd.write(format_arg_with_annotation(param))
arg_fd.write(')')
else:
arg_fd.write(format_arg_with_annotation(arg))
if defaults and i >= defaults_start:
arg_fd.write(' = ' if arg in annotations else '=')
arg_fd.write(object_description(defaults[i - defaults_start])) # type: ignore
formatted.append(arg_fd.getvalue())
if varargs:
formatted.append('*' + format_arg_with_annotation(varargs))
if kwonlyargs:
if not varargs:
formatted.append('*')
for kwarg in kwonlyargs:
arg_fd = StringIO()
arg_fd.write(format_arg_with_annotation(kwarg))
if kwonlydefaults and kwarg in kwonlydefaults:
arg_fd.write(' = ' if kwarg in annotations else '=')
arg_fd.write(object_description(kwonlydefaults[kwarg])) # type: ignore
formatted.append(arg_fd.getvalue())
if varkw:
formatted.append('**' + format_arg_with_annotation(varkw))
fd.write(', '.join(formatted))
fd.write(')')
if 'return' in annotations:
fd.write(' -> ')
fd.write(format_annotation(get_annotation('return')))
return fd.getvalue()

View File

@ -29,6 +29,7 @@
<%= hyperref %>
<%= contentsname %>
<%= numfig_format %>
<%= literalblockpto %>
<%= pageautorefname %>
<%= tocdepth %>
<%= secnumdepth %>

View File

@ -6,7 +6,7 @@
%
\NeedsTeXFormat{LaTeX2e}[1995/12/01]
\ProvidesPackage{sphinx}[2017/05/08 v1.6 LaTeX package (Sphinx markup)]
\ProvidesPackage{sphinx}[2017/06/17 v1.6.3 LaTeX package (Sphinx markup)]
% provides \ltx@ifundefined
% (many packages load ltxcmds: graphicx does for pdftex and lualatex but
@ -256,6 +256,7 @@
% verbatim
\DeclareBoolOption[true]{verbatimwithframe}
\DeclareBoolOption[true]{verbatimwrapslines}
\DeclareBoolOption[false]{verbatimhintsturnover}
\DeclareBoolOption[true]{inlineliteralwraps}
% parsed literal
\DeclareBoolOption[true]{parsedliteralwraps}
@ -682,7 +683,7 @@
% \sphinxVerbatimTitle is reset to empty after each use of Verbatim.
\newcommand*\sphinxVerbatimTitle {}
% This box to typeset the caption before framed.sty multiple passes for framing.
\newbox\spx@VerbatimTitleBox
\newbox\spx@Verbatim@TitleBox
% Holder macro for labels of literal blocks. Set-up by LaTeX writer.
\newcommand*\sphinxLiteralBlockLabel {}
\newcommand*\sphinxSetupCaptionForVerbatim [1]
@ -706,56 +707,77 @@
\newif\ifspx@inframed % flag set if we are already in a framed environment
% if forced use of minipage encapsulation is needed (e.g. table cells)
\newif\ifsphinxverbatimwithminipage \sphinxverbatimwithminipagefalse
\newcommand\spx@colorbox [2]{%
% #1 will be \fcolorbox or, for first part of frame: \spx@fcolorbox
\long\def\spx@colorbox #1#2#3{%
% let the framing obey the current indentation (adapted from framed.sty's code).
\hskip\@totalleftmargin
\hskip-\fboxsep\hskip-\fboxrule
#1{VerbatimBorderColor}{VerbatimColor}{#2}%
\spx@fcolorbox{VerbatimBorderColor}{VerbatimColor}{#1}{#2}{#3}%
\hskip-\fboxsep\hskip-\fboxrule
\hskip-\linewidth \hskip-\@totalleftmargin \hskip\columnwidth
}
% use of \color@b@x here is compatible with both xcolor.sty and color.sty
\def\spx@fcolorbox #1#2%
{\color@b@x {\fboxsep\z@\color{#1}\spx@VerbatimFBox}{\color{#2}}}%
% Inspired and adapted from framed.sty's \CustomFBox with extra handling
% of a non separable by pagebreak caption.
% The sole purpose of this macro is to add the framing and the title. The #1
% already has the typeset contents with background color, and took into
% account the \sphinxverbatimsep. The \fboxsep parameter is here zero
% (see \spx@fcolorbox definition) in order for the frame borders to exactly
% fit the colored contents.
\long\def\spx@VerbatimFBox#1{%
\long\def\spx@fcolorbox #1#2#3#4%
{\color@b@x {\color{#1}\spx@VerbatimFBox{#3}{#4}}{\color{#2}}}%
% Frame drawing macro
% #1 = used by default for title above frame, may contain "continued" hint
% #2 = for material underneath frame, used for "continues on next page" hint
% #3 = actual contents with background color
\long\def\spx@VerbatimFBox#1#2#3{%
\leavevmode
\begingroup
\setbox\@tempboxa\hbox{\kern\fboxsep{#1}\kern\fboxsep}%
\hbox
{\lower\dimexpr\fboxrule+\fboxsep+\dp\@tempboxa
\hbox{%
\vbox{\ifvoid\spx@VerbatimTitleBox\else
% add the caption in a centered way above possibly indented frame
% hide its width from framed.sty's measuring step
% note that the caption brings \abovecaptionskip top vertical space
\moveright\dimexpr\fboxrule+.5\wd\@tempboxa
\hb@xt@\z@{\hss\unhcopy\spx@VerbatimTitleBox\hss}\fi
\setbox\@tempboxa\hbox{{#3}}% inner braces to avoid color leaks
\hbox
{\lower\dimexpr\fboxrule+\dp\@tempboxa\hbox{%
\vbox{#1% above frame
% draw frame border _latest_ to avoid pdf viewer issue
\kern\fboxrule
\hbox{\kern\fboxrule
\vbox{\vskip\fboxsep\copy\@tempboxa\vskip\fboxsep}%
\copy\@tempboxa
\kern-\wd\@tempboxa\kern-\fboxrule
\vrule\@width\fboxrule
\kern\wd\@tempboxa
\vrule\@width\fboxrule}%
\kern-\dimexpr\fboxsep+\ht\@tempboxa+\dp\@tempboxa
+\fboxsep+\fboxrule\relax
\kern-\dimexpr\ht\@tempboxa+\dp\@tempboxa+\fboxrule\relax
\hrule\@height\fboxrule
\kern\dimexpr\fboxsep+\ht\@tempboxa+\dp\@tempboxa+\fboxsep\relax
\hrule\@height\fboxrule}%
}}%
\kern\dimexpr\ht\@tempboxa+\dp\@tempboxa\relax
\hrule\@height\fboxrule
#2% below frame
}%
}%
}%
\endgroup
}
% Customize framed.sty \MakeFramed to glue caption to literal block
% and add optional hint "continued on next page"
\def\spx@Verbatim@FrameCommand
{\spx@colorbox\spx@Verbatim@Title{}}%
% Macros for a frame with page breaks:
\def\spx@Verbatim@FirstFrameCommand
{\spx@colorbox\spx@Verbatim@Title\spx@Verbatim@Continues}%
\def\spx@Verbatim@MidFrameCommand
{\spx@colorbox\spx@Verbatim@Continued\spx@Verbatim@Continues}%
\def\spx@Verbatim@LastFrameCommand
{\spx@colorbox\spx@Verbatim@Continued{}}%
\def\spx@Verbatim@Title{% hide width from framed.sty measuring
\moveright\dimexpr\fboxrule+.5\wd\@tempboxa
\hb@xt@\z@{\hss\unhcopy\spx@Verbatim@TitleBox\hss}%
}%
\def\spx@Verbatim@Continued{%
\moveright\dimexpr\fboxrule+\wd\@tempboxa-\fboxsep
\hb@xt@\z@{\hss
{\normalcolor\sphinxstylecodecontinued\literalblockcontinuedname}}%
}%
\def\spx@Verbatim@Continues{%
\moveright\dimexpr\fboxrule+\wd\@tempboxa-\fboxsep
\hb@xt@\z@{\hss
{\normalcolor\sphinxstylecodecontinues\literalblockcontinuesname}}%
}%
% Defaults are redefined in document preamble according to language
\newcommand*\literalblockcontinuedname{continued from previous page}%
\newcommand*\literalblockcontinuesname{continues on next page}%
% For linebreaks inside Verbatim environment from package fancyvrb.
\newbox\sphinxcontinuationbox
\newbox\sphinxvisiblespacebox
@ -832,10 +854,10 @@
\needspace{\sphinxliteralblockwithoutcaptionneedspace}%
\phantomsection\sphinxLiteralBlockLabel
\fi
\setbox\spx@VerbatimTitleBox\box\voidb@x
\let\spx@Verbatim@Title\@empty
\else
% non-empty \sphinxVerbatimTitle has label inside it (in case there is one)
\setbox\spx@VerbatimTitleBox
\setbox\spx@Verbatim@TitleBox
\hbox{\begin{minipage}{\linewidth}%
\sphinxVerbatimTitle
\end{minipage}}%
@ -843,13 +865,14 @@
\fboxsep\sphinxverbatimsep \fboxrule\sphinxverbatimborder
% setting borderwidth to zero is simplest for no-frame effect with same pagebreaks
\ifspx@opt@verbatimwithframe\else\fboxrule\z@\fi
% Customize framed.sty \MakeFramed to glue caption to literal block
% via \spx@fcolorbox, will use \spx@VerbatimFBox which inserts title
\def\FrameCommand {\spx@colorbox\spx@fcolorbox }%
\let\FirstFrameCommand\FrameCommand
% for mid pages and last page portion of (long) split frame:
\def\MidFrameCommand{\spx@colorbox\fcolorbox }%
\let\LastFrameCommand\MidFrameCommand
\let\FrameCommand \spx@Verbatim@FrameCommand
\let\FirstFrameCommand\spx@Verbatim@FirstFrameCommand
\let\MidFrameCommand \spx@Verbatim@MidFrameCommand
\let\LastFrameCommand \spx@Verbatim@LastFrameCommand
\ifspx@opt@verbatimhintsturnover\else
\let\spx@Verbatim@Continued\@empty
\let\spx@Verbatim@Continues\@empty
\fi
\ifspx@opt@verbatimwrapslines
% fancyvrb's Verbatim puts each input line in (unbreakable) horizontal boxes.
% This customization wraps each line from the input in a \vtop, thus
@ -1369,7 +1392,7 @@
\lowercase{\endgroup\def~{\leavevmode\kern\z@\char`#1 }}}
\def\sphinx@literal@nolig@list {\do\`\do\<\do\>\do\'\do\-}%
\protected\def\sphinxbfcode#1{\sphinxcode{\bfseries#1}}
\protected\def\sphinxbfcode#1{\sphinxcode{\bfseries{}#1}}
\protected\def\sphinxemail#1{\textsf{#1}}
\protected\def\sphinxtablecontinued#1{\textsf{#1}}
\protected\def\sphinxtitleref#1{\emph{#1}}
@ -1398,6 +1421,8 @@
\protected\def\sphinxstyleliteralstrong {\sphinxbfcode}
\protected\def\sphinxstyleabbreviation {\textsc}
\protected\def\sphinxstyleliteralintitle {\sphinxcode}
\newcommand*\sphinxstylecodecontinued[1]{\footnotesize(#1)}%
\newcommand*\sphinxstylecodecontinues[1]{\footnotesize(#1)}%
% figure legend comes after caption and may contain arbitrary body elements
\newenvironment{sphinxlegend}{\par\small}{\par}

View File

@ -283,13 +283,16 @@ class ExtraTranslatableNodes(SphinxTransform):
class UnreferencedFootnotesDetector(SphinxTransform):
"""
detect unreferenced footnotes and citations, and emit warnings
detect unreferenced footnotes and emit warnings
"""
default_priority = 200
def apply(self):
for node in self.document.footnotes:
if node['names'][0] not in self.document.footnote_refs:
if node['names'] == []:
# footnote having duplicated number. It is already warned at parser.
pass
elif node['names'][0] not in self.document.footnote_refs:
logger.warning('Footnote [%s] is not referenced.', node['names'][0],
type='ref', subtype='footnote',
location=node)
@ -345,13 +348,17 @@ class SphinxSmartQuotes(SmartQuotes):
for txtnode in txtnodes:
nodetype = texttype[isinstance(txtnode.parent,
(nodes.literal,
nodes.literal_block,
addnodes.literal_emphasis,
addnodes.literal_strong,
addnodes.desc_signature,
addnodes.productionlist,
addnodes.desc_optional,
addnodes.desc_addname,
addnodes.desc_annotation,
addnodes.desc_name,
addnodes.desc_optional,
addnodes.desc_parameter,
addnodes.desc_parameterlist,
addnodes.desc_signature_line,
addnodes.desc_type,
addnodes.production,
nodes.math,
nodes.image,
nodes.raw,

View File

@ -8,21 +8,21 @@
:copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
from __future__ import absolute_import
import re
import typing
import inspect
from collections import OrderedDict
from six import PY3, binary_type
from six import PY2, PY3, StringIO, binary_type, string_types, itervalues
from six.moves import builtins
from sphinx.util import force_decode
if False:
# For type annotation
from typing import Any, Callable, List, Tuple, Type # NOQA
# this imports the standard library inspect module without resorting to
# relatively import this module
inspect = __import__('inspect')
from typing import Any, Callable, Dict, List, Tuple, Type # NOQA
memory_address_re = re.compile(r' at 0x[0-9a-f]{8,16}(?=>)', re.IGNORECASE)
@ -113,7 +113,7 @@ else: # 2.7
func = func.func
if not inspect.isfunction(func):
raise TypeError('%r is not a Python function' % func)
args, varargs, varkw = inspect.getargs(func.__code__)
args, varargs, varkw = inspect.getargs(func.__code__) # type: ignore
func_defaults = func.__defaults__
if func_defaults is None:
func_defaults = []
@ -231,3 +231,228 @@ def is_builtin_class_method(obj, attr_name):
if not hasattr(builtins, safe_getattr(cls, '__name__', '')): # type: ignore
return False
return getattr(builtins, safe_getattr(cls, '__name__', '')) is cls # type: ignore
class Parameter(object):
"""Fake parameter class for python2."""
POSITIONAL_ONLY = 0
POSITIONAL_OR_KEYWORD = 1
VAR_POSITIONAL = 2
KEYWORD_ONLY = 3
VAR_KEYWORD = 4
empty = object()
def __init__(self, name, kind=POSITIONAL_OR_KEYWORD, default=empty):
# type: (str, int, Any) -> None
self.name = name
self.kind = kind
self.default = default
self.annotation = self.empty
class Signature(object):
"""The Signature object represents the call signature of a callable object and
its return annotation.
"""
def __init__(self, subject, bound_method=False):
# type: (Callable, bool) -> None
# check subject is not a built-in class (ex. int, str)
if (isinstance(subject, type) and
is_builtin_class_method(subject, "__new__") and
is_builtin_class_method(subject, "__init__")):
raise TypeError("can't compute signature for built-in type {}".format(subject))
self.subject = subject
if PY3:
self.signature = inspect.signature(subject)
else:
self.argspec = getargspec(subject)
try:
self.annotations = typing.get_type_hints(subject)
except:
self.annotations = {}
if bound_method:
# client gives a hint that the subject is a bound method
if PY3 and inspect.ismethod(subject):
# inspect.signature already considers the subject is bound method.
# So it is not need to skip first argument.
self.skip_first_argument = False
else:
self.skip_first_argument = True
else:
if PY3:
# inspect.signature recognizes type of method properly without any hints
self.skip_first_argument = False
else:
# check the subject is bound method or not
self.skip_first_argument = inspect.ismethod(subject) and subject.__self__ # type: ignore # NOQA
@property
def parameters(self):
# type: () -> Dict
if PY3:
return self.signature.parameters
else:
params = OrderedDict() # type: Dict
positionals = len(self.argspec.args) - len(self.argspec.defaults)
for i, arg in enumerate(self.argspec.args):
if i < positionals:
params[arg] = Parameter(arg)
else:
default = self.argspec.defaults[i - positionals]
params[arg] = Parameter(arg, default=default)
if self.argspec.varargs:
params[self.argspec.varargs] = Parameter(self.argspec.varargs,
Parameter.VAR_POSITIONAL)
if self.argspec.keywords:
params[self.argspec.keywords] = Parameter(self.argspec.keywords,
Parameter.VAR_KEYWORD)
return params
@property
def return_annotation(self):
# type: () -> Any
if PY3:
return self.signature.return_annotation
else:
return None
def format_args(self):
# type: () -> unicode
args = []
last_kind = None
for i, param in enumerate(itervalues(self.parameters)):
# skip first argument if subject is bound method
if self.skip_first_argument and i == 0:
continue
arg = StringIO()
# insert '*' between POSITIONAL args and KEYWORD_ONLY args::
# func(a, b, *, c, d):
if param.kind == param.KEYWORD_ONLY and last_kind in (param.POSITIONAL_OR_KEYWORD,
param.POSITIONAL_ONLY):
args.append('*')
if param.kind in (param.POSITIONAL_ONLY,
param.POSITIONAL_OR_KEYWORD,
param.KEYWORD_ONLY):
arg.write(param.name)
if param.annotation is not param.empty:
if isinstance(param.annotation, string_types) and \
param.name in self.annotations:
arg.write(': ')
arg.write(self.format_annotation(self.annotations[param.name]))
else:
arg.write(': ')
arg.write(self.format_annotation(param.annotation))
if param.default is not param.empty:
if param.annotation is param.empty:
arg.write('=')
arg.write(object_description(param.default)) # type: ignore
else:
arg.write(' = ')
arg.write(object_description(param.default)) # type: ignore
elif param.kind == param.VAR_POSITIONAL:
arg.write('*')
arg.write(param.name)
elif param.kind == param.VAR_KEYWORD:
arg.write('**')
arg.write(param.name)
args.append(arg.getvalue())
last_kind = param.kind
if PY2 or self.return_annotation is inspect.Parameter.empty:
return '(%s)' % ', '.join(args)
else:
if isinstance(self.return_annotation, string_types) and \
'return' in self.annotations:
annotation = self.format_annotation(self.annotations['return'])
else:
annotation = self.format_annotation(self.return_annotation)
return '(%s) -> %s' % (', '.join(args), annotation)
def format_annotation(self, annotation):
# type: (Any) -> str
"""Return formatted representation of a type annotation.
Show qualified names for types and additional details for types from
the ``typing`` module.
Displaying complex types from ``typing`` relies on its private API.
"""
if isinstance(annotation, string_types):
return annotation # type: ignore
if isinstance(annotation, typing.TypeVar): # type: ignore
return annotation.__name__
if annotation == Ellipsis:
return '...'
if not isinstance(annotation, type):
return repr(annotation)
qualified_name = (annotation.__module__ + '.' + annotation.__qualname__ # type: ignore
if annotation else repr(annotation))
if annotation.__module__ == 'builtins':
return annotation.__qualname__ # type: ignore
elif isinstance(annotation, typing.GenericMeta):
# In Python 3.5.2+, all arguments are stored in __args__,
# whereas __parameters__ only contains generic parameters.
#
# Prior to Python 3.5.2, __args__ is not available, and all
# arguments are in __parameters__.
params = None
if hasattr(annotation, '__args__'):
if annotation.__args__ is None or len(annotation.__args__) <= 2: # type: ignore # NOQA
params = annotation.__args__ # type: ignore
else: # typing.Callable
args = ', '.join(self.format_annotation(arg) for arg
in annotation.__args__[:-1]) # type: ignore
result = self.format_annotation(annotation.__args__[-1]) # type: ignore
return '%s[[%s], %s]' % (qualified_name, args, result)
elif hasattr(annotation, '__parameters__'):
params = annotation.__parameters__ # type: ignore
if params is not None:
param_str = ', '.join(self.format_annotation(p) for p in params)
return '%s[%s]' % (qualified_name, param_str)
elif (hasattr(typing, 'UnionMeta') and # for py35 or below
isinstance(annotation, typing.UnionMeta) and # type: ignore
hasattr(annotation, '__union_params__')):
params = annotation.__union_params__ # type: ignore
if params is not None:
param_str = ', '.join(self.format_annotation(p) for p in params)
return '%s[%s]' % (qualified_name, param_str)
elif (isinstance(annotation, typing.CallableMeta) and # type: ignore
getattr(annotation, '__args__', None) is not None and
hasattr(annotation, '__result__')):
# Skipped in the case of plain typing.Callable
args = annotation.__args__ # type: ignore
if args is None:
return qualified_name
elif args is Ellipsis:
args_str = '...'
else:
formatted_args = (self.format_annotation(a) for a in args)
args_str = '[%s]' % ', '.join(formatted_args)
return '%s[%s, %s]' % (qualified_name,
args_str,
self.format_annotation(annotation.__result__)) # type: ignore # NOQA
elif (isinstance(annotation, typing.TupleMeta) and # type: ignore
hasattr(annotation, '__tuple_params__') and
hasattr(annotation, '__tuple_use_ellipsis__')):
params = annotation.__tuple_params__ # type: ignore
if params is not None:
param_strings = [self.format_annotation(p) for p in params]
if annotation.__tuple_use_ellipsis__: # type: ignore
param_strings.append('...')
return '%s[%s]' % (qualified_name,
', '.join(param_strings))
return qualified_name

View File

@ -98,6 +98,7 @@ DEFAULT_SETTINGS = {
'tocdepth': '',
'secnumdepth': '',
'pageautorefname': '',
'literalblockpto': '',
} # type: Dict[unicode, unicode]
ADDITIONAL_SETTINGS = {
@ -648,6 +649,14 @@ class LaTeXTranslator(nodes.NodeVisitor):
if self.elements['extraclassoptions']:
self.elements['classoptions'] += ',' + \
self.elements['extraclassoptions']
self.elements['literalblockpto'] = (
self.babel_renewcommand(
'\\literalblockcontinuedname', self.encode(_('continued from previous page'))
) +
self.babel_renewcommand(
'\\literalblockcontinuesname', self.encode(_('continues on next page'))
)
)
self.elements['pageautorefname'] = \
self.babel_defmacro('\\pageautorefname', self.encode(_('page')))
self.elements['numfig_format'] = self.generate_numfig_format(builder)
@ -1220,7 +1229,7 @@ class LaTeXTranslator(nodes.NodeVisitor):
def visit_desc_annotation(self, node):
# type: (nodes.Node) -> None
self.body.append(r'\sphinxstrong{')
self.body.append(r'\sphinxbfcode{')
def depart_desc_annotation(self, node):
# type: (nodes.Node) -> None
@ -1802,7 +1811,7 @@ class LaTeXTranslator(nodes.NodeVisitor):
# TODO non vertical space for other alignments.
align = '\\begin{flush%s}' % node.attributes['align']
align_end = '\\end{flush%s}' % node.attributes['align']
self.body.append('\\begin{figure}[%s]%s\n' % (
self.body.append('\n\\begin{figure}[%s]%s\n' % (
self.elements['figure_align'], align))
if any(isinstance(child, nodes.caption) for child in node):
self.body.append('\\capstart\n')

View File

@ -235,7 +235,7 @@ def test_format_signature():
pass
assert formatsig('method', 'H.foo', H.foo1, None, None) == '(b, *c)'
assert formatsig('method', 'H.foo', H.foo1, 'a', None) == '(a)'
assert formatsig('method', 'H.foo', H.foo2, None, None) == '(b, *c)'
assert formatsig('method', 'H.foo', H.foo2, None, None) == '(*c)'
assert formatsig('method', 'H.foo', H.foo3, None, None) == r"(d='\\n')"
# test exception handling (exception is caught and args is '')

View File

@ -15,6 +15,7 @@ import mock
import pytest
from textwrap import dedent
from sphinx.errors import SphinxError
import sys
from sphinx.testing.path import path
@ -31,6 +32,11 @@ def nonascii_srcdir(request, rootdir, sphinx_test_tempdir):
# If supported, build in a non-ASCII source dir
test_name = u'\u65e5\u672c\u8a9e'
basedir = sphinx_test_tempdir / request.node.originalname
# Windows with versions prior to 3.2 (I think) doesn't support unicode on system path
# so we force a non-unicode path in that case
if sys.platform == "win32" and \
not (sys.version_info.major >= 3 and sys.version_info.minor >= 2):
return basedir / 'all'
try:
srcdir = basedir / test_name
if not srcdir.exists():
@ -64,6 +70,7 @@ def nonascii_srcdir(request, rootdir, sphinx_test_tempdir):
)
@mock.patch('sphinx.builders.linkcheck.requests.head',
side_effect=request_session_head)
@pytest.mark.xfail(sys.platform == 'win32', reason="Not working on windows")
def test_build_all(requests_head, make_app, nonascii_srcdir, buildername):
app = make_app(buildername, srcdir=nonascii_srcdir)
app.build()

View File

@ -147,7 +147,7 @@ def check_extra_entries(outdir):
@pytest.mark.sphinx('html', testroot='warnings')
def test_html_warnings(app, warning):
app.build()
html_warnings = strip_escseq(warning.getvalue().replace(os.sep, '/'))
html_warnings = strip_escseq(re.sub(re.escape(os.sep) + '{1,2}', '/', warning.getvalue()))
html_warnings_exp = HTML_WARNINGS % {
'root': re.escape(app.srcdir.replace(os.sep, '/'))}
assert re.match(html_warnings_exp + '$', html_warnings), \
@ -1167,6 +1167,7 @@ def test_html_entity(app):
@pytest.mark.sphinx('html', testroot='basic')
@pytest.mark.xfail(os.name != 'posix', reason="Not working on windows")
def test_html_inventory(app):
app.builder.build_all()
with open(app.outdir / 'objects.inv', 'rb') as f:

View File

@ -155,7 +155,7 @@ def test_writer(app, status, warning):
def test_latex_warnings(app, status, warning):
app.builder.build_all()
warnings = strip_escseq(warning.getvalue().replace(os.sep, '/'))
warnings = strip_escseq(re.sub(re.escape(os.sep) + '{1,2}', '/', warning.getvalue()))
warnings_exp = LATEX_WARNINGS % {
'root': re.escape(app.srcdir.replace(os.sep, '/'))}
assert re.match(warnings_exp + '$', warnings), \

View File

@ -37,7 +37,7 @@ if PY3:
@pytest.mark.sphinx('texinfo', testroot='warnings', freshenv=True)
def test_texinfo_warnings(app, status, warning):
app.builder.build_all()
warnings = strip_escseq(warning.getvalue().replace(os.sep, '/'))
warnings = strip_escseq(re.sub(re.escape(os.sep) + '{1,2}', '/', warning.getvalue()))
warnings_exp = TEXINFO_WARNINGS % {
'root': re.escape(app.srcdir.replace(os.sep, '/'))}
assert re.match(warnings_exp + '$', warnings), \

View File

@ -10,6 +10,7 @@
"""
import pytest
import os
from sphinx.config import Config
from sphinx.directives.code import LiteralIncludeReader
@ -29,6 +30,7 @@ def literal_inc_path(testroot):
return testroot / 'literal.inc'
@pytest.mark.xfail(os.name != 'posix', reason="Not working on windows")
def test_LiteralIncludeReader(literal_inc_path):
options = {'lineno-match': True}
reader = LiteralIncludeReader(literal_inc_path, options, DUMMY_CONFIG)
@ -38,6 +40,7 @@ def test_LiteralIncludeReader(literal_inc_path):
assert reader.lineno_start == 1
@pytest.mark.xfail(os.name != 'posix', reason="Not working on windows")
def test_LiteralIncludeReader_lineno_start(literal_inc_path):
options = {'lineno-start': 5}
reader = LiteralIncludeReader(literal_inc_path, options, DUMMY_CONFIG)
@ -47,6 +50,7 @@ def test_LiteralIncludeReader_lineno_start(literal_inc_path):
assert reader.lineno_start == 5
@pytest.mark.xfail(os.name != 'posix', reason="Not working on windows")
def test_LiteralIncludeReader_pyobject1(literal_inc_path):
options = {'lineno-match': True, 'pyobject': 'Foo'}
reader = LiteralIncludeReader(literal_inc_path, options, DUMMY_CONFIG)
@ -56,6 +60,7 @@ def test_LiteralIncludeReader_pyobject1(literal_inc_path):
assert reader.lineno_start == 6
@pytest.mark.xfail(os.name != 'posix', reason="Not working on windows")
def test_LiteralIncludeReader_pyobject2(literal_inc_path):
options = {'pyobject': 'Bar'}
reader = LiteralIncludeReader(literal_inc_path, options, DUMMY_CONFIG)
@ -66,6 +71,7 @@ def test_LiteralIncludeReader_pyobject2(literal_inc_path):
assert reader.lineno_start == 1 # no lineno-match
@pytest.mark.xfail(os.name != 'posix', reason="Not working on windows")
def test_LiteralIncludeReader_pyobject3(literal_inc_path):
options = {'pyobject': 'Bar.baz'}
reader = LiteralIncludeReader(literal_inc_path, options, DUMMY_CONFIG)
@ -74,6 +80,7 @@ def test_LiteralIncludeReader_pyobject3(literal_inc_path):
" pass\n")
@pytest.mark.xfail(os.name != 'posix', reason="Not working on windows")
def test_LiteralIncludeReader_pyobject_and_lines(literal_inc_path):
options = {'pyobject': 'Bar', 'lines': '2-'}
reader = LiteralIncludeReader(literal_inc_path, options, DUMMY_CONFIG)
@ -82,6 +89,7 @@ def test_LiteralIncludeReader_pyobject_and_lines(literal_inc_path):
" pass\n")
@pytest.mark.xfail(os.name != 'posix', reason="Not working on windows")
def test_LiteralIncludeReader_lines1(literal_inc_path):
options = {'lines': '1-4'}
reader = LiteralIncludeReader(literal_inc_path, options, DUMMY_CONFIG)
@ -92,6 +100,7 @@ def test_LiteralIncludeReader_lines1(literal_inc_path):
u"foo = \"Including Unicode characters: üöä\"\n")
@pytest.mark.xfail(os.name != 'posix', reason="Not working on windows")
def test_LiteralIncludeReader_lines2(literal_inc_path):
options = {'lines': '1,4,6'}
reader = LiteralIncludeReader(literal_inc_path, options, DUMMY_CONFIG)
@ -101,6 +110,7 @@ def test_LiteralIncludeReader_lines2(literal_inc_path):
u"class Foo:\n")
@pytest.mark.xfail(os.name != 'posix', reason="Not working on windows")
def test_LiteralIncludeReader_lines_and_lineno_match1(literal_inc_path):
options = {'lines': '4-6', 'lineno-match': True}
reader = LiteralIncludeReader(literal_inc_path, options, DUMMY_CONFIG)
@ -127,6 +137,7 @@ def test_LiteralIncludeReader_lines_and_lineno_match3(literal_inc_path, app, sta
content, lines = reader.read()
@pytest.mark.xfail(os.name != 'posix', reason="Not working on windows")
def test_LiteralIncludeReader_start_at(literal_inc_path):
options = {'lineno-match': True, 'start-at': 'Foo', 'end-at': 'Bar'}
reader = LiteralIncludeReader(literal_inc_path, options, DUMMY_CONFIG)
@ -138,6 +149,7 @@ def test_LiteralIncludeReader_start_at(literal_inc_path):
assert reader.lineno_start == 6
@pytest.mark.xfail(os.name != 'posix', reason="Not working on windows")
def test_LiteralIncludeReader_start_after(literal_inc_path):
options = {'lineno-match': True, 'start-after': 'Foo', 'end-before': 'Bar'}
reader = LiteralIncludeReader(literal_inc_path, options, DUMMY_CONFIG)
@ -147,6 +159,7 @@ def test_LiteralIncludeReader_start_after(literal_inc_path):
assert reader.lineno_start == 7
@pytest.mark.xfail(os.name != 'posix', reason="Not working on windows")
def test_LiteralIncludeReader_start_after_and_lines(literal_inc_path):
options = {'lineno-match': True, 'lines': '6-',
'start-after': 'coding', 'end-before': 'comment'}
@ -160,6 +173,7 @@ def test_LiteralIncludeReader_start_after_and_lines(literal_inc_path):
assert reader.lineno_start == 8
@pytest.mark.xfail(os.name != 'posix', reason="Not working on windows")
def test_LiteralIncludeReader_start_at_and_lines(literal_inc_path):
options = {'lines': '2, 3, 5', 'start-at': 'foo', 'end-before': '#'}
reader = LiteralIncludeReader(literal_inc_path, options, DUMMY_CONFIG)
@ -192,6 +206,7 @@ def test_LiteralIncludeReader_missing_start_and_end(literal_inc_path):
content, lines = reader.read()
@pytest.mark.xfail(os.name != 'posix', reason="Not working on windows")
def test_LiteralIncludeReader_prepend(literal_inc_path):
options = {'lines': '1', 'prepend': 'Hello', 'append': 'Sphinx'}
reader = LiteralIncludeReader(literal_inc_path, options, DUMMY_CONFIG)
@ -201,6 +216,7 @@ def test_LiteralIncludeReader_prepend(literal_inc_path):
"Sphinx\n")
@pytest.mark.xfail(os.name != 'posix', reason="Not working on windows")
def test_LiteralIncludeReader_dedent(literal_inc_path):
# dedent: 2
options = {'lines': '10-12', 'dedent': 2}
@ -227,6 +243,7 @@ def test_LiteralIncludeReader_dedent(literal_inc_path):
"\n")
@pytest.mark.xfail(os.name != 'posix', reason="Not working on windows")
def test_LiteralIncludeReader_tabwidth(testroot):
# tab-width: 4
options = {'tab-width': 4, 'pyobject': 'Qux'}
@ -245,6 +262,7 @@ def test_LiteralIncludeReader_tabwidth(testroot):
" pass\n")
@pytest.mark.xfail(os.name != 'posix', reason="Not working on windows")
def test_LiteralIncludeReader_tabwidth_dedent(testroot):
options = {'tab-width': 4, 'dedent': 4, 'pyobject': 'Qux.quux'}
reader = LiteralIncludeReader(testroot / 'target.py', options, DUMMY_CONFIG)
@ -253,6 +271,7 @@ def test_LiteralIncludeReader_tabwidth_dedent(testroot):
" pass\n")
@pytest.mark.xfail(os.name != 'posix', reason="Not working on windows")
def test_LiteralIncludeReader_diff(testroot, literal_inc_path):
options = {'diff': testroot / 'literal-diff.inc'}
reader = LiteralIncludeReader(literal_inc_path, options, DUMMY_CONFIG)

View File

@ -10,6 +10,7 @@
"""
import re
import sys
import pytest
from sphinx.testing.path import path
@ -71,6 +72,9 @@ def test_texinfo(app, status, warning):
@pytest.mark.sphinx('html', testroot='docutilsconf',
docutilsconf='[general]\nsource_link=true\n')
@pytest.mark.skip(sys.platform == "win32" and \
not (sys.version_info.major >= 3 and sys.version_info.minor >= 2),
reason="Python < 3.2 on Win32 doesn't handle non-ASCII paths right")
def test_docutils_source_link_with_nonascii_file(app, status, warning):
srcdir = path(app.srcdir)
mb_name = u'\u65e5\u672c\u8a9e'

View File

@ -10,9 +10,11 @@
"""
import pytest
import os
@pytest.mark.sphinx('latex', testroot='ext-imgconverter')
@pytest.mark.xfail(os.name != 'posix', reason="Not working on windows")
def test_ext_imgconverter(app, status, warning):
app.builder.build_all()

View File

@ -16,6 +16,7 @@ import mock
import pytest
import requests
from io import BytesIO
import os
from sphinx import addnodes
from sphinx.ext.intersphinx import setup as intersphinx_setup
@ -86,6 +87,7 @@ def test_fetch_inventory_redirection(_read_from_url, InventoryFile, app, status,
assert InventoryFile.load.call_args[0][1] == 'http://hostname/'
@pytest.mark.xfail(os.name != 'posix', reason="Path separator mismatch issue")
def test_missing_reference(tempdir, app, status, warning):
inv_file = tempdir / 'inventory'
inv_file.write_bytes(inventory_v2)

View File

@ -152,6 +152,7 @@ def test_text_warning_node(app):
@sphinx_intl
@pytest.mark.sphinx('text')
@pytest.mark.test_params(shared_result='test_intl_basic')
@pytest.mark.xfail(os.name != 'posix', reason="Not working on windows")
def test_text_title_underline(app):
app.build()
# --- simple translation; check title underlines
@ -1084,6 +1085,7 @@ def test_text_references(app, warning):
srcdir='test_intl_images',
confoverrides={'language': 'xx'}
)
@pytest.mark.xfail(os.name != 'posix', reason="Not working on windows")
def test_image_glob_intl(app):
app.build()
# index.rst
@ -1131,6 +1133,7 @@ def test_image_glob_intl(app):
'figure_language_filename': u'{root}{ext}.{language}',
}
)
@pytest.mark.xfail(os.name != 'posix', reason="Not working on windows")
def test_image_glob_intl_using_figure_language_filename(app):
app.build()
# index.rst

View File

@ -186,6 +186,7 @@ def test_format_date():
assert i18n.format_date(format, date=date) == 'Feb 7, 2016'
@pytest.mark.xfail(os.name != 'posix', reason="Path separators don't match on windows")
def test_get_filename_for_language(app):
# language is None
app.env.config.language = None

View File

@ -113,6 +113,146 @@ def test_getargspec_bound_methods():
assert expected_bound == inspect.getargspec(wrapped_bound_method)
def test_Signature():
# literals
with pytest.raises(TypeError):
inspect.Signature(1)
with pytest.raises(TypeError):
inspect.Signature('')
# builitin classes
with pytest.raises(TypeError):
inspect.Signature(int)
with pytest.raises(TypeError):
inspect.Signature(str)
# normal function
def func(a, b, c=1, d=2, *e, **f):
pass
sig = inspect.Signature(func).format_args()
assert sig == '(a, b, c=1, d=2, *e, **f)'
def test_Signature_partial():
def fun(a, b, c=1, d=2):
pass
p = functools.partial(fun, 10, c=11)
sig = inspect.Signature(p).format_args()
if sys.version_info < (3,):
assert sig == '(b, d=2)'
else:
assert sig == '(b, *, c=11, d=2)'
def test_Signature_methods():
class Foo:
def meth1(self, arg1, **kwargs):
pass
@classmethod
def meth2(cls, arg1, *args, **kwargs):
pass
@staticmethod
def meth3(arg1, *args, **kwargs):
pass
@functools.wraps(Foo().meth1)
def wrapped_bound_method(*args, **kwargs):
pass
# unbound method
sig = inspect.Signature(Foo.meth1).format_args()
assert sig == '(self, arg1, **kwargs)'
sig = inspect.Signature(Foo.meth1, bound_method=True).format_args()
assert sig == '(arg1, **kwargs)'
# bound method
sig = inspect.Signature(Foo().meth1).format_args()
assert sig == '(arg1, **kwargs)'
# class method
sig = inspect.Signature(Foo.meth2).format_args()
assert sig == '(arg1, *args, **kwargs)'
sig = inspect.Signature(Foo().meth2).format_args()
assert sig == '(arg1, *args, **kwargs)'
# static method
sig = inspect.Signature(Foo.meth3).format_args()
assert sig == '(arg1, *args, **kwargs)'
sig = inspect.Signature(Foo().meth3).format_args()
assert sig == '(arg1, *args, **kwargs)'
# wrapped bound method
sig = inspect.Signature(wrapped_bound_method).format_args()
if sys.version_info < (3,):
assert sig == '(*args, **kwargs)'
elif sys.version_info < (3, 4, 4):
assert sig == '(self, arg1, **kwargs)'
else:
assert sig == '(arg1, **kwargs)'
@pytest.mark.skipif(sys.version_info < (3, 5),
reason='type annotation test is available on py35 or above')
def test_Signature_annotations():
from typing_test_data import f0, f1, f2, f3, f4, f5, f6, f7, f8, f9, f10, f11
# Class annotations
sig = inspect.Signature(f0).format_args()
assert sig == '(x: int, y: numbers.Integral) -> None'
# Generic types with concrete parameters
sig = inspect.Signature(f1).format_args()
assert sig == '(x: typing.List[int]) -> typing.List[int]'
# TypeVars and generic types with TypeVars
sig = inspect.Signature(f2).format_args()
assert sig == '(x: typing.List[T], y: typing.List[T_co], z: T) -> typing.List[T_contra]'
# Union types
sig = inspect.Signature(f3).format_args()
assert sig == '(x: typing.Union[str, numbers.Integral]) -> None'
# Quoted annotations
sig = inspect.Signature(f4).format_args()
assert sig == '(x: str, y: str) -> None'
# Keyword-only arguments
sig = inspect.Signature(f5).format_args()
assert sig == '(x: int, *, y: str, z: str) -> None'
# Keyword-only arguments with varargs
sig = inspect.Signature(f6).format_args()
assert sig == '(x: int, *args, y: str, z: str) -> None'
# Space around '=' for defaults
sig = inspect.Signature(f7).format_args()
assert sig == '(x: int = None, y: dict = {}) -> None'
# Callable types
sig = inspect.Signature(f8).format_args()
assert sig == '(x: typing.Callable[[int, str], int]) -> None'
sig = inspect.Signature(f9).format_args()
assert sig == '(x: typing.Callable) -> None'
# Tuple types
sig = inspect.Signature(f10).format_args()
assert sig == '(x: typing.Tuple[int, str], y: typing.Tuple[int, ...]) -> None'
# Instance annotations
sig = inspect.Signature(f11).format_args()
assert sig == '(x: CustomAnnotation, y: 123) -> None'
def test_safe_getattr_with_default():
class Foo(object):
def __getattr__(self, item):

View File

@ -22,6 +22,8 @@ from sphinx.util.parallel import ParallelTasks
import pytest
from sphinx.testing.util import strip_escseq
import os
def test_info_and_warning(app, status, warning):
app.verbosity = 2
@ -241,6 +243,7 @@ def test_colored_logs(app, status, warning):
assert colorize('red', 'message8') in status.getvalue()
@pytest.mark.xfail(os.name != 'posix', reason="Not working on windows")
def test_logging_in_ParallelTasks(app, status, warning):
logging.setup(app, status, warning)
logger = logging.getLogger(__name__)