Merge branch '1.7-release'

This commit is contained in:
Takeshi KOMIYA 2018-01-20 02:22:48 +09:00
commit 3b28aadba9
10 changed files with 551 additions and 317 deletions

View File

@ -16,6 +16,9 @@ Features added
Bugs fixed Bugs fixed
---------- ----------
* #4415: autodoc classifies inherited classmethods as regular methods
* #4415: autodoc classifies inherited staticmethods as regular methods
Testing Testing
-------- --------
@ -45,7 +48,7 @@ Incompatible changes
* shebang line is removed from generated conf.py * shebang line is removed from generated conf.py
* #2557: autodoc: :confval:`autodoc_mock_imports` only mocks specified modules * #2557: autodoc: :confval:`autodoc_mock_imports` only mocks specified modules
with their descendants. It does not mock their ancestors. If you want to with their descendants. It does not mock their ancestors. If you want to
mock them, please specify the name of ancestors implicitly. mock them, please specify the name of ancestors explicitly.
* #3620: html theme: move DOCUMENTATION_OPTIONS to independent JavaScript file * #3620: html theme: move DOCUMENTATION_OPTIONS to independent JavaScript file
(refs: #4295) (refs: #4295)
* #4246: Limit width of text body for all themes. Conifigurable via theme * #4246: Limit width of text body for all themes. Conifigurable via theme
@ -190,7 +193,8 @@ Bugs fixed
---------- ----------
* #1922: html search: Upper characters problem in French * #1922: html search: Upper characters problem in French
* #4412: Updated jQuery version from 3.1.0 to 3.2.1
* #4438: math: math with labels with whitespace cause html error
Testing Testing
-------- --------

View File

@ -32,7 +32,7 @@ from sphinx.application import ExtensionError
from sphinx.util import logging from sphinx.util import logging
from sphinx.util.inspect import Signature, isdescriptor, safe_getmembers, \ from sphinx.util.inspect import Signature, isdescriptor, safe_getmembers, \
safe_getattr, object_description, is_builtin_class_method, \ safe_getattr, object_description, is_builtin_class_method, \
isenumattribute, getdoc isenumattribute, isclassmethod, isstaticmethod, getdoc
from sphinx.util.docstrings import prepare_docstring from sphinx.util.docstrings import prepare_docstring
if False: if False:
@ -1261,12 +1261,14 @@ class MethodDocumenter(DocstringSignatureMixin, ClassLevelDocumenter): # type:
# to distinguish classmethod/staticmethod # to distinguish classmethod/staticmethod
obj = self.parent.__dict__.get(self.object_name) obj = self.parent.__dict__.get(self.object_name)
if obj is None:
obj = self.object
if isinstance(obj, classmethod): if isclassmethod(obj):
self.directivetype = 'classmethod' self.directivetype = 'classmethod'
# document class and static members before ordinary ones # document class and static members before ordinary ones
self.member_order = self.member_order - 1 self.member_order = self.member_order - 1
elif isinstance(obj, staticmethod): elif isstaticmethod(obj, cls=self.parent, name=self.object_name):
self.directivetype = 'staticmethod' self.directivetype = 'staticmethod'
# document class and static members before ordinary ones # document class and static members before ordinary ones
self.member_order = self.member_order - 1 self.member_order = self.member_order - 1

View File

@ -10,6 +10,7 @@
""" """
from docutils import nodes, utils from docutils import nodes, utils
from docutils.nodes import make_id
from docutils.parsers.rst import Directive, directives from docutils.parsers.rst import Directive, directives
from sphinx.config import string_classes from sphinx.config import string_classes
@ -55,7 +56,8 @@ class MathDomain(Domain):
label = 'mathematics' label = 'mathematics'
initial_data = { initial_data = {
'objects': {}, # labelid -> (docname, eqno) 'nameids': {}, # label -> equation ID
'objects': {}, # equation ID -> (docname, eqno)
} # type: Dict[unicode, Dict[unicode, Tuple[unicode, int]]] } # type: Dict[unicode, Dict[unicode, Tuple[unicode, int]]]
dangling_warnings = { dangling_warnings = {
'eq': 'equation not found: %(target)s', 'eq': 'equation not found: %(target)s',
@ -63,9 +65,9 @@ class MathDomain(Domain):
def clear_doc(self, docname): def clear_doc(self, docname):
# type: (unicode) -> None # type: (unicode) -> None
for labelid, (doc, eqno) in list(self.data['objects'].items()): for equation_id, (doc, eqno) in list(self.data['objects'].items()):
if doc == docname: if doc == docname:
del self.data['objects'][labelid] del self.data['objects'][equation_id]
def merge_domaindata(self, docnames, otherdata): def merge_domaindata(self, docnames, otherdata):
# type: (Iterable[unicode], Dict) -> None # type: (Iterable[unicode], Dict) -> None
@ -84,10 +86,10 @@ class MathDomain(Domain):
newnode['target'] = target newnode['target'] = target
return newnode return newnode
else: else:
node_id = make_id('equation-%s' % target)
if env.config.math_numfig and env.config.numfig: if env.config.math_numfig and env.config.numfig:
if docname in env.toc_fignumbers: if docname in env.toc_fignumbers:
id = 'equation-' + target number = env.toc_fignumbers[docname]['displaymath'].get(node_id, ())
number = env.toc_fignumbers[docname]['displaymath'].get(id, ())
number = '.'.join(map(str, number)) number = '.'.join(map(str, number))
else: else:
number = '' number = ''
@ -98,8 +100,8 @@ class MathDomain(Domain):
logger.warning('Invalid math_eqref_format: %r', exc, logger.warning('Invalid math_eqref_format: %r', exc,
location=node) location=node)
title = nodes.Text("(%d)" % number) title = nodes.Text("(%d)" % number)
return make_refnode(builder, fromdocname, docname, title = nodes.Text("(%d)" % number)
"equation-" + target, title) return make_refnode(builder, fromdocname, docname, node_id, title)
else: else:
return None return None
@ -260,7 +262,8 @@ class MathDirective(Directive):
node['number'] = eqno node['number'] = eqno
# add target node # add target node
target = nodes.target('', '', ids=['equation-' + node['label']]) node_id = make_id('equation-%s' % node['label'])
target = nodes.target('', '', ids=[node_id])
self.state.document.note_explicit_target(target) self.state.document.note_explicit_target(target)
ret.insert(0, target) ret.insert(0, target)
except UserWarning as exc: except UserWarning as exc:

File diff suppressed because one or more lines are too long

View File

@ -154,6 +154,40 @@ def isenumattribute(x):
return isinstance(x, enum.Enum) return isinstance(x, enum.Enum)
def isclassmethod(obj):
# type: (Any) -> bool
"""Check if the object is classmethod."""
if isinstance(obj, classmethod):
return True
elif inspect.ismethod(obj):
if getattr(obj, 'im_self', None): # py2
return True
elif getattr(obj, '__self__', None): # py3
return True
return False
def isstaticmethod(obj, cls=None, name=None):
# type: (Any, Any, unicode) -> bool
"""Check if the object is staticmethod."""
if isinstance(obj, staticmethod):
return True
elif cls and name:
# trace __mro__ if the method is defined in parent class
#
# .. note:: This only works with new style classes.
for basecls in getattr(cls, '__mro__', []):
meth = basecls.__dict__.get(name)
if meth:
if isinstance(meth, staticmethod):
return True
else:
return False
return False
def isdescriptor(x): def isdescriptor(x):
# type: (Any) -> bool # type: (Any) -> bool
"""Check if the object is some kind of descriptor.""" """Check if the object is some kind of descriptor."""

View File

@ -64,6 +64,14 @@ class Base(object):
def inheritedmeth(self): def inheritedmeth(self):
"""Inherited function.""" """Inherited function."""
@classmethod
def inheritedclassmeth(cls):
"""Inherited class method."""
@staticmethod
def inheritedstaticmeth(cls):
"""Inherited static method."""
class Derived(Base): class Derived(Base):
def inheritedmeth(self): def inheritedmeth(self):

View File

@ -715,6 +715,8 @@ def test_generate():
assert_processes(should, 'class', 'Class') assert_processes(should, 'class', 'Class')
options.inherited_members = True options.inherited_members = True
should.append(('method', 'target.Class.inheritedmeth')) should.append(('method', 'target.Class.inheritedmeth'))
should.append(('method', 'target.Class.inheritedclassmeth'))
should.append(('method', 'target.Class.inheritedstaticmeth'))
assert_processes(should, 'class', 'Class') assert_processes(should, 'class', 'Class')
# test special members # test special members
@ -798,7 +800,9 @@ def test_generate():
' .. py:attribute:: Class.inst_attr_comment', ' .. py:attribute:: Class.inst_attr_comment',
' .. py:attribute:: Class.inst_attr_string', ' .. py:attribute:: Class.inst_attr_string',
' .. py:attribute:: Class._private_inst_attr', ' .. py:attribute:: Class._private_inst_attr',
' .. py:classmethod:: Class.inheritedclassmeth()',
' .. py:method:: Class.inheritedmeth()', ' .. py:method:: Class.inheritedmeth()',
' .. py:staticmethod:: Class.inheritedstaticmeth()',
], ],
'class', 'Class', member_order='bysource', all_members=True) 'class', 'Class', member_order='bysource', all_members=True)
del directive.env.ref_context['py:module'] del directive.env.ref_context['py:module']

View File

@ -40,9 +40,9 @@ def test_jsmath(app, status, warning):
assert (u'<span class="eqno">(1)<a class="headerlink" href="#equation-foo" ' assert (u'<span class="eqno">(1)<a class="headerlink" href="#equation-foo" '
u'title="Permalink to this equation">\xb6</a></span>' u'title="Permalink to this equation">\xb6</a></span>'
u'<div class="math notranslate" id="equation-foo">\ne^{i\\pi} = 1</div>' in content) u'<div class="math notranslate" id="equation-foo">\ne^{i\\pi} = 1</div>' in content)
assert (u'<span class="eqno">(2)<a class="headerlink" href="#equation-math:0" ' assert (u'<span class="eqno">(2)<a class="headerlink" href="#equation-math-0" '
u'title="Permalink to this equation">\xb6</a></span>' u'title="Permalink to this equation">\xb6</a></span>'
u'<div class="math notranslate" id="equation-math:0">\n' u'<div class="math notranslate" id="equation-math-0">\n'
u'e^{ix} = \\cos x + i\\sin x</div>' in content) u'e^{ix} = \\cos x + i\\sin x</div>' in content)
assert '<div class="math notranslate">\nn \\in \\mathbb N</div>' in content assert '<div class="math notranslate">\nn \\in \\mathbb N</div>' in content
assert '<div class="math notranslate">\na + 1 &lt; b</div>' in content assert '<div class="math notranslate">\na + 1 &lt; b</div>' in content
@ -102,7 +102,7 @@ def test_math_number_all_mathjax(app, status, warning):
app.builder.build_all() app.builder.build_all()
content = (app.outdir / 'index.html').text() content = (app.outdir / 'index.html').text()
html = (r'<div class="math notranslate" id="equation-index:0">\s*' html = (r'<div class="math notranslate" id="equation-index-0">\s*'
r'<span class="eqno">\(1\)<a .*>\xb6</a></span>\\\[a\^2\+b\^2=c\^2\\\]</div>') r'<span class="eqno">\(1\)<a .*>\xb6</a></span>\\\[a\^2\+b\^2=c\^2\\\]</div>')
assert re.search(html, content, re.S) assert re.search(html, content, re.S)

View File

@ -71,7 +71,7 @@ def test_js_source(app, status, warning):
app.builder.build(['contents']) app.builder.build(['contents'])
v = '3.1.0' v = '3.2.1'
msg = 'jquery.js version does not match to {v}'.format(v=v) msg = 'jquery.js version does not match to {v}'.format(v=v)
jquery_min = (app.outdir / '_static' / 'jquery.js').text() jquery_min = (app.outdir / '_static' / 'jquery.js').text()
assert 'jQuery v{v}'.format(v=v) in jquery_min, msg assert 'jQuery v{v}'.format(v=v) in jquery_min, msg