diff --git a/sphinx/application.py b/sphinx/application.py index cee780c9c..c17cbb617 100644 --- a/sphinx/application.py +++ b/sphinx/application.py @@ -29,7 +29,7 @@ from docutils.parsers.rst import convert_directive_function, \ import sphinx from sphinx import package_dir, locale from sphinx.roles import XRefRole -from sphinx.config import Config +from sphinx.config import Config, convert_numfig_format from sphinx.errors import SphinxError, SphinxWarning, ExtensionError, \ VersionRequirementError, ConfigError from sphinx.domains import ObjType @@ -236,6 +236,7 @@ class Sphinx(object): self._init_i18n() # check all configuration values for permissible types self.config.check_types(self.warn) + self._post_init_config() # set up source_parsers self._init_source_parsers() # set up the build environment @@ -270,6 +271,9 @@ class Sphinx(object): else: self.info('not available for built-in messages') + def _post_init_config(self): + convert_numfig_format(self.config) + def _init_source_parsers(self): for suffix, parser in iteritems(self._additional_source_parsers): if suffix not in self.config.source_suffix: diff --git a/sphinx/config.py b/sphinx/config.py index 5741d66bf..1f1d6ec30 100644 --- a/sphinx/config.py +++ b/sphinx/config.py @@ -286,3 +286,12 @@ class Config(object): def __contains__(self, name): return name in self.values + + +def convert_numfig_format(config): + """ Convert numfi_format to new one. """ + + numfig_format = {} + for figtype, fmt in iteritems(config.numfig_format): + numfig_format[figtype] = fmt.replace('%s', '{number}') + config.numfig_format = numfig_format diff --git a/sphinx/domains/std.py b/sphinx/domains/std.py index b7f2597d4..f83afdab1 100644 --- a/sphinx/domains/std.py +++ b/sphinx/domains/std.py @@ -673,20 +673,18 @@ class StandardDomain(Domain): else: title = env.config.numfig_format.get(figtype, '') - if figname is None and '%{name}' in title: + # convert old styled numfig_format to new style + title = title.replace('%s', '{number}') + + if figname is None and '{name}' in title: env.warn_node('the link has no caption: %s' % title, node) return contnode else: fignum = '.'.join(map(str, fignumber)) - if '{name}' in title or 'number' in title: - # new style format (cf. "Fig.%{number}") - if figname: - newtitle = title.format(name=figname, number=fignum) - else: - newtitle = title.format(number=fignum) + if figname: + newtitle = title.format(name=figname, number=fignum) else: - # old style format (cf. "Fig.%s") - newtitle = title % fignum + newtitle = title.format(number=fignum) except KeyError as exc: env.warn_node('invalid numfig_format: %s (%r)' % (title, exc), node) return contnode diff --git a/sphinx/util/texescape.py b/sphinx/util/texescape.py index 0a3192f6a..861026b55 100644 --- a/sphinx/util/texescape.py +++ b/sphinx/util/texescape.py @@ -118,10 +118,17 @@ tex_replacements = [ ('Ω', r'\(\Omega\)'), ('Ω', r'\(\Omega\)'), ] +tex_pyformats = [ + # map special chars of str.format() to escaped one + ('{', '{{'), + ('}', '}}'), +] + tex_escape_map = {} tex_replace_map = {} tex_hl_escape_map_new = {} +tex_pyformat_map = {} def init(): @@ -133,3 +140,6 @@ def init(): if a in '[]{}\\': continue tex_hl_escape_map_new[ord(a)] = b + + for a, b in tex_pyformats: + tex_pyformat_map[ord(a)] = b diff --git a/sphinx/writers/html.py b/sphinx/writers/html.py index ba2b758d8..7dd286cf7 100644 --- a/sphinx/writers/html.py +++ b/sphinx/writers/html.py @@ -291,7 +291,7 @@ class HTMLTranslator(BaseTranslator): self.builder.warn(msg) else: numbers = self.builder.fignumbers[key][figure_id] - self.body.append(prefix % '.'.join(map(str, numbers)) + ' ') + self.body.append(prefix.format(number='.'.join(map(str, numbers))) + ' ') self.body.append('') figtype = self.builder.env.domains['std'].get_figtype(node) diff --git a/sphinx/writers/latex.py b/sphinx/writers/latex.py index ca0f1a7dd..0a158297b 100644 --- a/sphinx/writers/latex.py +++ b/sphinx/writers/latex.py @@ -29,7 +29,7 @@ from sphinx.util import split_into from sphinx.util.i18n import format_date from sphinx.util.nodes import clean_astext, traverse_parent from sphinx.util.template import LaTeXRenderer -from sphinx.util.texescape import tex_escape_map, tex_replace_map +from sphinx.util.texescape import tex_escape_map, tex_replace_map, tex_pyformat_map from sphinx.util.smartypants import educate_quotes_latex @@ -610,7 +610,7 @@ class LaTeXTranslator(nodes.NodeVisitor): def generate_numfig_format(self, builder): ret = [] - figure = self.builder.config.numfig_format['figure'].split('%s', 1) + figure = self.builder.config.numfig_format['figure'].split('{number}', 1) if len(figure) == 1: ret.append('\\def\\fnum@figure{%s}\n' % escape_abbr(text_type(figure[0]).translate(tex_escape_map))) @@ -623,7 +623,7 @@ class LaTeXTranslator(nodes.NodeVisitor): escape_abbr(text_type(figure[1]).translate(tex_escape_map))) ret.append('\\makeatother\n') - table = self.builder.config.numfig_format['table'].split('%s', 1) + table = self.builder.config.numfig_format['table'].split('{number}', 1) if len(table) == 1: ret.append('\\def\\fnum@table{%s}\n' % escape_abbr(text_type(table[0]).translate(tex_escape_map))) @@ -636,7 +636,7 @@ class LaTeXTranslator(nodes.NodeVisitor): escape_abbr(text_type(table[1]).translate(tex_escape_map))) ret.append('\\makeatother\n') - codeblock = self.builder.config.numfig_format['code-block'].split('%s', 1) + codeblock = self.builder.config.numfig_format['code-block'].split('{number}', 1) if len(codeblock) == 1: pass # FIXME else: @@ -1789,16 +1789,11 @@ class LaTeXTranslator(nodes.NodeVisitor): else: id = node.get('refuri', '')[1:].replace('#', ':') - title = node.get('title', '%s') - title = text_type(title).translate(tex_escape_map).replace('\\%s', '%s') - if '\\{name\\}' in title or '\\{number\\}' in title: - # new style format (cf. "Fig.%{number}") - title = title.replace('\\{name\\}', '{name}').replace('\\{number\\}', '{number}') - text = escape_abbr(title).format(name='\\nameref{%s}' % self.idescape(id), - number='\\ref{%s}' % self.idescape(id)) - else: - # old style format (cf. "Fig.%{number}") - text = escape_abbr(title) % ('\\ref{%s}' % self.idescape(id)) + title = node.get('title', '{number}').replace('%s', '{number}') + title = text_type(title).translate(tex_escape_map).translate(tex_pyformat_map) + title = title.replace('\\{{name\\}}', '{name}').replace('\\{{number\\}}', '{number}') + text = escape_abbr(title).format(name='\\nameref{%s}' % self.idescape(id), + number='\\ref{%s}' % self.idescape(id)) hyperref = '\\hyperref[%s]{%s}' % (self.idescape(id), text) self.body.append(hyperref) diff --git a/tests/roots/test-numfig/index.rst b/tests/roots/test-numfig/index.rst index 939903839..499a1b7dc 100644 --- a/tests/roots/test-numfig/index.rst +++ b/tests/roots/test-numfig/index.rst @@ -53,7 +53,5 @@ test-tocdepth * Section.1 is :numref:`foo` * Section.2.1 is :numref:`bar_a` * Unnumbered section is :numref:`index` -* Invalid numfig_format 01: :numref:`invalid ` -* Invalid numfig_format 02: :numref:`Fig %s %s ` * Fig.1 is :numref:`Fig.{number} {name} ` * Section.1 is :numref:`Sect.{number} {name} ` diff --git a/tests/test_build_html.py b/tests/test_build_html.py index d8aff88ab..176590f75 100644 --- a/tests/test_build_html.py +++ b/tests/test_build_html.py @@ -506,8 +506,6 @@ def test_numfig_disabled(app, status, warning): 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': [ @@ -570,8 +568,6 @@ def test_numfig_without_numbered_toctree(app, status, warning): 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': [ @@ -670,8 +666,6 @@ def test_numfig_with_numbered_toctree(app, status, warning): 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': [ @@ -774,8 +768,6 @@ def test_numfig_with_prefix(app, status, warning): 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': [ @@ -874,8 +866,6 @@ def test_numfig_with_secnum_depth(app, status, warning): 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': [