Fix #326: numref role can also refer sections

This commit is contained in:
Takeshi KOMIYA 2016-06-21 12:39:37 +09:00
parent 98886d3e07
commit 1aafc7e2e5
11 changed files with 122 additions and 38 deletions

View File

@ -118,6 +118,7 @@ Features added
* #2926: EPUB3 builder supports vertical mode (``epub3_writing_mode`` option)
* #2695: ``build_sphinx`` subcommand for setuptools handles exceptions as same
as ``sphinx-build`` does
* #326: `numref` role can also refer sections
Bugs fixed
----------

View File

@ -294,13 +294,16 @@ General configuration
.. confval:: numfig_format
A dictionary mapping ``'figure'``, ``'table'`` and ``'code-block'`` to
strings that are used for format of figure numbers. Default is to use
``'Fig. %s'`` for ``'figure'``, ``'Table %s'`` for ``'table'`` and
``'Listing %s'`` for ``'code-block'``.
A dictionary mapping ``'figure'``, ``'table'``, ``'code-block'`` and
``'section'`` to strings that are used for format of figure numbers. Default
is to use ``'Fig. %s'`` for ``'figure'``, ``'Table %s'`` for ``'table'``,
``'Listing %s'`` for ``'code-block'`` and ``'Section'`` for ``'section'``.
.. versionadded:: 1.3
.. versionchanged:: 1.5
Support format of section
.. confval:: numfig_secnum_depth
The scope of figure numbers, that is, the numfig feature numbers figures

View File

@ -212,11 +212,14 @@ Cross-referencing figures by figure number
.. versionadded:: 1.3
.. versionchanged:: 1.5
`numref` role can also refer sections
.. rst:role:: numref
Link to the specified figures, tables and code-blocks; the standard reST
labels are used. When you use this role, it will insert a reference to the
figure with link text by its figure number like "Fig. 1.1".
Link to the specified figures, tables, code-blocks and sections; the standard
reST labels are used. When you use this role, it will insert a reference to
the figure with link text by its figure number like "Fig. 1.1".
If an explicit link text is given (like usual: ``:numref:`Image of Sphinx (Fig.
%s) <my-figure>```), the link caption will be the title of the reference.

View File

@ -104,7 +104,8 @@ class Config(object):
nitpick_ignore = ([], None),
numfig = (False, 'env'),
numfig_secnum_depth = (1, 'env'),
numfig_format = ({'figure': l_('Fig. %s'),
numfig_format = ({'section': l_('Section %s'),
'figure': l_('Fig. %s'),
'table': l_('Table %s'),
'code-block': l_('Listing %s')},
'env'),

View File

@ -653,11 +653,10 @@ class StandardDomain(Domain):
return None
try:
figure_id = target_node['ids'][0]
fignumber = env.toc_fignumbers[docname][figtype][figure_id]
except (KeyError, IndexError):
# target_node is found, but fignumber is not assigned.
# Maybe it is defined in orphaned document.
fignumber = self.get_fignumber(env, builder, figtype, docname, target_node)
if fignumber is None:
return contnode
except ValueError:
env.warn_node("no number is assigned for %s: %s" % (figtype, labelid), node)
return contnode
@ -670,7 +669,7 @@ class StandardDomain(Domain):
newtitle = title % '.'.join(map(str, fignumber))
except TypeError:
env.warn_node('invalid numfig_format: %s' % title, node)
return None
return contnode
return self.build_reference_node(fromdocname, builder,
docname, labelid, newtitle, 'numref',
@ -804,7 +803,9 @@ class StandardDomain(Domain):
def has_child(node, cls):
return any(isinstance(child, cls) for child in node)
if isinstance(node, nodes.container):
if isinstance(node, nodes.section):
return 'section'
elif isinstance(node, nodes.container):
if node.get('literal_block') and has_child(node, nodes.literal_block):
return 'code-block'
else:
@ -813,6 +814,28 @@ class StandardDomain(Domain):
figtype, _ = self.enumerable_nodes.get(node.__class__, (None, None))
return figtype
def get_fignumber(self, env, builder, figtype, docname, target_node):
if figtype == 'section':
if builder.name == 'latex':
return tuple()
elif docname not in env.toc_secnumbers:
raise ValueError # no number assigned
else:
anchorname = '#' + target_node['ids'][0]
if anchorname not in env.toc_secnumbers[docname]:
# try first heading which has no anchor
return env.toc_secnumbers[docname].get('')
else:
return env.toc_secnumbers[docname].get(anchorname)
else:
try:
figure_id = target_node['ids'][0]
return env.toc_fignumbers[docname][figtype][figure_id]
except (KeyError, IndexError):
# target_node is found, but fignumber is not assigned.
# Maybe it is defined in orphaned document.
raise ValueError
def setup(app):
app.add_domain(StandardDomain)

View File

@ -1,7 +1,11 @@
.. _bar:
===
Bar
===
.. _bar_a:
Bar A
=====
@ -37,9 +41,13 @@ Bar A
print('hello world')
.. _bar_b:
Bar B
=====
.. _bar_b1:
Bar B1
------

View File

@ -1,3 +1,5 @@
.. _baz_a:
Baz A
-----

View File

@ -1,3 +1,5 @@
.. _foo:
===
Foo
===
@ -16,6 +18,8 @@ Foo
print('hello world')
.. _foo_a:
Foo A
=====
@ -47,12 +51,18 @@ Foo A
print('hello world')
.. _foo_a1:
Foo A1
------
.. _foo_b:
Foo B
=====
.. _foo_b1:
Foo B1
------

View File

@ -1,3 +1,5 @@
.. _index:
test-tocdepth
=============
@ -48,5 +50,8 @@ test-tocdepth
* Table.2.2 is :numref:`Table:%s <table22>`
* List.1 is :numref:`CODE_1`
* List.2.2 is :numref:`Code-%s <CODE22>`
* Section.1 is :numref:`foo`
* Section.2.1 is :numref:`bar_a`
* Unnumbered section is :numref:`index`
* Invalid numfig_format 01: :numref:`invalid <fig1>`
* Invalid numfig_format 02: :numref:`Fig %s %s <fig1>`

View File

@ -503,10 +503,11 @@ def test_tocdepth_singlehtml(app, status, warning):
def test_numfig_disabled(app, status, warning):
app.builder.build_all()
assert ('index.rst:45: WARNING: numfig is disabled. :numref: is ignored.'
in warning.getvalue())
assert 'index.rst:51: WARNING: invalid numfig_format: invalid' not in warning.getvalue()
assert 'index.rst:52: WARNING: invalid numfig_format: Fig %s %s' not in warning.getvalue()
warnings = warning.getvalue()
assert 'index.rst:47: WARNING: numfig is disabled. :numref: is ignored.' in warnings
assert 'index.rst:55: WARNING: no number is assigned for section: index' not in warnings
assert 'index.rst:56: WARNING: invalid numfig_format: invalid' not in warnings
assert 'index.rst:57: WARNING: invalid numfig_format: Fig %s %s' not in warnings
expects = {
'index.html': [
@ -521,6 +522,8 @@ def test_numfig_disabled(app, status, warning):
(".//li/code/span", '^Table:%s$', True),
(".//li/code/span", '^CODE_1$', True),
(".//li/code/span", '^Code-%s$', True),
(".//li/code/span", '^foo$', True),
(".//li/code/span", '^bar_a$', True),
],
'foo.html': [
(".//div[@class='figure']/p[@class='caption']/"
@ -562,10 +565,11 @@ def test_numfig_without_numbered_toctree(app, status, warning):
(app.srcdir / 'index.rst').write_text(index, encoding='utf-8')
app.builder.build_all()
assert ('index.rst:45: WARNING: numfig is disabled. :numref: is ignored.'
not in warning.getvalue())
assert 'index.rst:51: WARNING: invalid numfig_format: invalid' in warning.getvalue()
assert 'index.rst:52: WARNING: invalid numfig_format: Fig %s %s' in warning.getvalue()
warnings = warning.getvalue()
assert 'index.rst:47: WARNING: numfig is disabled. :numref: is ignored.' not in warnings
assert 'index.rst:55: WARNING: no number is assigned for section: index' in warnings
assert 'index.rst:56: WARNING: invalid numfig_format: invalid' in warnings
assert 'index.rst:57: WARNING: invalid numfig_format: Fig %s %s' in warnings
expects = {
'index.html': [
@ -587,6 +591,8 @@ def test_numfig_without_numbered_toctree(app, status, warning):
(".//li/a/span", '^Table:6$', True),
(".//li/a/span", '^Listing 9$', True),
(".//li/a/span", '^Code-6$', True),
(".//li/code/span", '^foo$', True),
(".//li/code/span", '^bar_a$', True),
],
'foo.html': [
(".//div[@class='figure']/p[@class='caption']/"
@ -657,10 +663,11 @@ def test_numfig_without_numbered_toctree(app, status, warning):
def test_numfig_with_numbered_toctree(app, status, warning):
app.builder.build_all()
assert ('index.rst:45: WARNING: numfig is disabled. :numref: is ignored.'
not in warning.getvalue())
assert 'index.rst:51: WARNING: invalid numfig_format: invalid' in warning.getvalue()
assert 'index.rst:52: WARNING: invalid numfig_format: Fig %s %s' in warning.getvalue()
warnings = warning.getvalue()
assert 'index.rst:47: WARNING: numfig is disabled. :numref: is ignored.' not in warnings
assert 'index.rst:55: WARNING: no number is assigned for section: index' in warnings
assert 'index.rst:56: WARNING: invalid numfig_format: invalid' in warnings
assert 'index.rst:57: WARNING: invalid numfig_format: Fig %s %s' in warnings
expects = {
'index.html': [
@ -682,6 +689,8 @@ def test_numfig_with_numbered_toctree(app, status, warning):
(".//li/a/span", '^Table:2.2$', True),
(".//li/a/span", '^Listing 1$', True),
(".//li/a/span", '^Code-2.2$', True),
(".//li/a/span", '^Section.1$', True),
(".//li/a/span", '^Section.2.1$', True),
],
'foo.html': [
(".//div[@class='figure']/p[@class='caption']/"
@ -751,14 +760,16 @@ def test_numfig_with_numbered_toctree(app, status, warning):
confoverrides={'numfig': True,
'numfig_format': {'figure': 'Figure:%s',
'table': 'Tab_%s',
'code-block': 'Code-%s'}})
'code-block': 'Code-%s',
'section': 'SECTION-%s'}})
def test_numfig_with_prefix(app, status, warning):
app.builder.build_all()
assert ('index.rst:45: WARNING: numfig is disabled. :numref: is ignored.'
not in warning.getvalue())
assert 'index.rst:51: WARNING: invalid numfig_format: invalid' in warning.getvalue()
assert 'index.rst:52: WARNING: invalid numfig_format: Fig %s %s' in warning.getvalue()
warnings = warning.getvalue()
assert 'index.rst:47: WARNING: numfig is disabled. :numref: is ignored.' not in warnings
assert 'index.rst:55: WARNING: no number is assigned for section: index' in warnings
assert 'index.rst:56: WARNING: invalid numfig_format: invalid' in warnings
assert 'index.rst:57: WARNING: invalid numfig_format: Fig %s %s' in warnings
expects = {
'index.html': [
@ -780,6 +791,8 @@ def test_numfig_with_prefix(app, status, warning):
(".//li/a/span", '^Table:2.2$', True),
(".//li/a/span", '^Code-1$', True),
(".//li/a/span", '^Code-2.2$', True),
(".//li/a/span", '^SECTION-1$', True),
(".//li/a/span", '^SECTION-2.1$', True),
],
'foo.html': [
(".//div[@class='figure']/p[@class='caption']/"
@ -850,10 +863,11 @@ def test_numfig_with_prefix(app, status, warning):
def test_numfig_with_secnum_depth(app, status, warning):
app.builder.build_all()
assert ('index.rst:45: WARNING: numfig is disabled. :numref: is ignored.'
not in warning.getvalue())
assert 'index.rst:51: WARNING: invalid numfig_format: invalid' in warning.getvalue()
assert 'index.rst:52: WARNING: invalid numfig_format: Fig %s %s' in warning.getvalue()
warnings = warning.getvalue()
assert 'index.rst:47: WARNING: numfig is disabled. :numref: is ignored.' not in warnings
assert 'index.rst:55: WARNING: no number is assigned for section: index' in warnings
assert 'index.rst:56: WARNING: invalid numfig_format: invalid' in warnings
assert 'index.rst:57: WARNING: invalid numfig_format: Fig %s %s' in warnings
expects = {
'index.html': [
@ -875,6 +889,8 @@ def test_numfig_with_secnum_depth(app, status, warning):
(".//li/a/span", '^Table:2.1.2$', True),
(".//li/a/span", '^Listing 1$', True),
(".//li/a/span", '^Code-2.1.2$', True),
(".//li/a/span", '^Section.1$', True),
(".//li/a/span", '^Section.2.1$', True),
],
'foo.html': [
(".//div[@class='figure']/p[@class='caption']/"
@ -965,6 +981,8 @@ def test_numfig_with_singlehtml(app, status, warning):
(".//li/a/span", '^Table:2.2$', True),
(".//li/a/span", '^Listing 1$', True),
(".//li/a/span", '^Code-2.2$', True),
(".//li/a/span", '^Section.1$', True),
(".//li/a/span", '^Section.2.1$', True),
(".//div[@class='figure']/p[@class='caption']/"
"span[@class='caption-number']", '^Fig. 1.1 $', True),
(".//div[@class='figure']/p[@class='caption']/"

View File

@ -151,13 +151,16 @@ def test_numref(app, status, warning):
assert '\\hyperref[baz:table22]{Table:\\ref{baz:table22}}' in result
assert '\\hyperref[index:code-1]{Listing \\ref{index:code-1}}' in result
assert '\\hyperref[baz:code22]{Code-\\ref{baz:code22}}' in result
assert '\\hyperref[foo:foo]{Section \\ref{foo:foo}}' in result
assert '\\hyperref[bar:bar-a]{Section \\ref{bar:bar-a}}' in result
@with_app(buildername='latex', testroot='numfig',
confoverrides={'numfig': True,
'numfig_format': {'figure': 'Figure:%s',
'table': 'Tab_%s',
'code-block': 'Code-%s'}})
'code-block': 'Code-%s',
'section': 'SECTION-%s'}})
def test_numref_with_prefix1(app, status, warning):
app.builder.build_all()
result = (app.outdir / 'Python.tex').text(encoding='utf8')
@ -179,13 +182,16 @@ def test_numref_with_prefix1(app, status, warning):
assert '\\hyperref[baz:table22]{Table:\\ref{baz:table22}}' in result
assert '\\hyperref[index:code-1]{Code-\\ref{index:code-1}}' in result
assert '\\hyperref[baz:code22]{Code-\\ref{baz:code22}}' in result
assert '\\hyperref[foo:foo]{SECTION-\\ref{foo:foo}}' in result
assert '\\hyperref[bar:bar-a]{SECTION-\\ref{bar:bar-a}}' in result
@with_app(buildername='latex', testroot='numfig',
confoverrides={'numfig': True,
'numfig_format': {'figure': 'Figure:%s.',
'table': 'Tab_%s:',
'code-block': 'Code-%s | '}})
'code-block': 'Code-%s | ',
'section': 'SECTION_%s_'}})
def test_numref_with_prefix2(app, status, warning):
app.builder.build_all()
result = (app.outdir / 'Python.tex').text(encoding='utf8')
@ -203,6 +209,8 @@ def test_numref_with_prefix2(app, status, warning):
assert '\\hyperref[baz:table22]{Table:\\ref{baz:table22}}' in result
assert '\\hyperref[index:code-1]{Code-\\ref{index:code-1} \\textbar{} }' in result
assert '\\hyperref[baz:code22]{Code-\\ref{baz:code22}}' in result
assert '\\hyperref[foo:foo]{SECTION\\_\\ref{foo:foo}\\_}' in result
assert '\\hyperref[bar:bar-a]{SECTION\\_\\ref{bar:bar-a}\\_}' in result
@with_app(buildername='latex', testroot='numfig',
@ -222,6 +230,8 @@ def test_numref_with_language_ja(app, status, warning):
assert '\\hyperref[baz:table22]{Table:\\ref{baz:table22}}' in result
assert '\\hyperref[index:code-1]{LIST \\ref{index:code-1}}' in result
assert '\\hyperref[baz:code22]{Code-\\ref{baz:code22}}' in result
assert '\\hyperref[foo:foo]{Section \\ref{foo:foo}}' in result
assert '\\hyperref[bar:bar-a]{Section \\ref{bar:bar-a}}' in result
@with_app(buildername='latex')