summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CHANGES11
-rw-r--r--sphinx/builders/__init__.py2
-rw-r--r--sphinx/transforms.py27
-rw-r--r--tests/roots/test-intl/footnote.po7
-rw-r--r--tests/roots/test-intl/footnote.txt3
-rw-r--r--tests/test_intl.py230
6 files changed, 170 insertions, 110 deletions
diff --git a/CHANGES b/CHANGES
index 5dfef7d1..fbd0433f 100644
--- a/CHANGES
+++ b/CHANGES
@@ -32,10 +32,13 @@ Bugs fixed
characters to "Project name" on quickstart.
* #1190: Output TeX/texinfo/man filename has no basename (only extention)
when using multibyte characters to "Project name" on quickstart.
-* #1090: Fix multiple cross references (term, ref, doc) in the same line
- return the same link with i18n.
-* #1193: Fix multiple link references in the same line return the same
- link with i18n.
+* #1090: Fix i18n: multiple cross references (term, ref, doc) in the same line
+ return the same link.
+* #1193: Fix i18n: multiple link references in the same line return the same
+ link.
+* #1176: Fix i18n: footnote reference number missing for auto numbered named
+ footnote and auto symbol footnote.
+* PR#146,#1172: Fix ZeroDivisionError in Parallel Builds. Thanks to tychoish.
Release 1.2 (beta1 released Mar 31, 2013)
diff --git a/sphinx/builders/__init__.py b/sphinx/builders/__init__.py
index 41824361..15ac62c7 100644
--- a/sphinx/builders/__init__.py
+++ b/sphinx/builders/__init__.py
@@ -357,6 +357,8 @@ class Builder(object):
# for the rest, determine how many documents to write in one go
ndocs = len(docnames)
chunksize = min(ndocs // nproc, 10)
+ if chunksize == 0:
+ chunksize = 1
nchunks, rest = divmod(ndocs, chunksize)
if rest:
nchunks += 1
diff --git a/sphinx/transforms.py b/sphinx/transforms.py
index fbc77530..324df2ad 100644
--- a/sphinx/transforms.py
+++ b/sphinx/transforms.py
@@ -300,17 +300,38 @@ class Locale(Transform):
def is_autonumber_footnote_ref(node):
return isinstance(node, nodes.footnote_reference) and \
node.get('auto') == 1
+ def list_replace_or_append(lst, old, new):
+ if old in lst:
+ lst[lst.index(old)] = new
+ else:
+ lst.append(new)
old_foot_refs = node.traverse(is_autonumber_footnote_ref)
new_foot_refs = patch.traverse(is_autonumber_footnote_ref)
if len(old_foot_refs) != len(new_foot_refs):
env.warn_node('inconsistent footnote references in '
'translated message', node)
- for old, new in zip(old_foot_refs, new_foot_refs):
+ old_foot_namerefs = {}
+ for r in old_foot_refs:
+ old_foot_namerefs.setdefault(r.get('refname'), []).append(r)
+ for new in new_foot_refs:
+ refname = new.get('refname')
+ refs = old_foot_namerefs.get(refname, [])
+ if not refs:
+ continue
+
+ old = refs.pop(0)
new['ids'] = old['ids']
for id in new['ids']:
self.document.ids[id] = new
- self.document.autofootnote_refs.remove(old)
- self.document.note_autofootnote_ref(new)
+ list_replace_or_append(
+ self.document.autofootnote_refs, old, new)
+ if refname:
+ list_replace_or_append(
+ self.document.footnote_refs.setdefault(refname, []),
+ old, new)
+ list_replace_or_append(
+ self.document.refnames.setdefault(refname, []),
+ old, new)
# reference should use new (translated) 'refname'.
# * reference target ".. _Python: ..." is not translatable.
diff --git a/tests/roots/test-intl/footnote.po b/tests/roots/test-intl/footnote.po
index 47f8d3db..b3876f51 100644
--- a/tests/roots/test-intl/footnote.po
+++ b/tests/roots/test-intl/footnote.po
@@ -19,8 +19,8 @@ msgstr ""
msgid "i18n with Footnote"
msgstr "I18N WITH FOOTNOTE"
-msgid "[100]_ Contents [#]_ for `i18n with Footnote`_ [ref]_"
-msgstr "`I18N WITH FOOTNOTE`_ INCLUDE THIS CONTENTS [ref]_ [#]_ [100]_"
+msgid "[100]_ Contents [#]_ for `i18n with Footnote`_ [ref]_ [#named]_."
+msgstr "`I18N WITH FOOTNOTE`_ INCLUDE THIS CONTENTS [#named]_ [ref]_ [#]_ [100]_."
msgid "This is a auto numbered footnote."
msgstr "THIS IS A AUTO NUMBERED FOOTNOTE."
@@ -31,3 +31,6 @@ msgstr "THIS IS A NAMED FOOTNOTE."
msgid "This is a numbered footnote."
msgstr "THIS IS A NUMBERED FOOTNOTE."
+msgid "This is a auto numbered named footnote."
+msgstr "THIS IS A AUTO NUMBERED NAMED FOOTNOTE."
+
diff --git a/tests/roots/test-intl/footnote.txt b/tests/roots/test-intl/footnote.txt
index 3ef76bba..55da9c6d 100644
--- a/tests/roots/test-intl/footnote.txt
+++ b/tests/roots/test-intl/footnote.txt
@@ -4,8 +4,9 @@ i18n with Footnote
==================
.. #955 cant-build-html-with-footnotes-when-using
-[100]_ Contents [#]_ for `i18n with Footnote`_ [ref]_
+[100]_ Contents [#]_ for `i18n with Footnote`_ [ref]_ [#named]_.
.. [#] This is a auto numbered footnote.
.. [ref] This is a named footnote.
.. [100] This is a numbered footnote.
+.. [#named] This is a auto numbered named footnote.
diff --git a/tests/test_intl.py b/tests/test_intl.py
index 6a004a4e..ef412c1d 100644
--- a/tests/test_intl.py
+++ b/tests/test_intl.py
@@ -93,11 +93,16 @@ def elem_getref(elem):
return elem.attrib.get('refid') or elem.attrib.get('refuri')
-def assert_elem_text_refs(elem, text, refs):
- _text = elem_gettexts(elem)
- assert _text == text
- _refs = map(elem_getref, elem.findall('reference'))
- assert _refs == refs
+def assert_elem(elem, texts=None, refs=None, names=None):
+ if texts is not None:
+ _texts = elem_gettexts(elem)
+ assert _texts == texts
+ if refs is not None:
+ _refs = map(elem_getref, elem.findall('reference'))
+ assert _refs == refs
+ if names is not None:
+ _names = elem.attrib.get('names').split()
+ assert _names == names
@with_intl_app(buildername='text')
@@ -142,41 +147,63 @@ def test_i18n_footnote_break_refid(app):
# expect no error by build
-@with_intl_app(buildername='text', warning=warnfile)
+@with_intl_app(buildername='xml', warning=warnfile)
def test_i18n_footnote_regression(app):
- """regression test for fix #955"""
+ # regression test for fix #955, #1176
app.builddir.rmtree(True)
app.builder.build(['footnote'])
- result = (app.outdir / 'footnote.txt').text(encoding='utf-8')
- expect = (u"\nI18N WITH FOOTNOTE"
- u"\n******************\n" # underline matches new translation
- u"\nI18N WITH FOOTNOTE INCLUDE THIS CONTENTS [ref] [1] [100]\n"
- u"\n[1] THIS IS A AUTO NUMBERED FOOTNOTE.\n"
- u"\n[ref] THIS IS A NAMED FOOTNOTE.\n"
- u"\n[100] THIS IS A NUMBERED FOOTNOTE.\n")
- assert result == expect
+ et = ElementTree.parse(app.outdir / 'footnote.xml')
+ secs = et.findall('section')
+
+ para0 = secs[0].findall('paragraph')
+ assert_elem(
+ para0[0],
+ texts=['I18N WITH FOOTNOTE', 'INCLUDE THIS CONTENTS',
+ '2', '[ref]', '1', '100', '.'],
+ refs=['i18n-with-footnote', 'ref'])
+
+ footnote0 = secs[0].findall('footnote')
+ assert_elem(
+ footnote0[0],
+ texts=['1','THIS IS A AUTO NUMBERED FOOTNOTE.'],
+ names=['1'])
+ assert_elem(
+ footnote0[1],
+ texts=['100','THIS IS A NUMBERED FOOTNOTE.'],
+ names=['100'])
+ assert_elem(
+ footnote0[2],
+ texts=['2','THIS IS A AUTO NUMBERED NAMED FOOTNOTE.'],
+ names=['named'])
+
+ citation0 = secs[0].findall('citation')
+ assert_elem(
+ citation0[0],
+ texts=['ref','THIS IS A NAMED FOOTNOTE.'],
+ names=['ref'])
warnings = warnfile.getvalue().replace(os.sep, '/')
- warning_expr = u'.*/footnote.txt:\\d*: SEVERE: Duplicate ID: ".*".\n'
+ warning_expr = u'.*/footnote.xml:\\d*: SEVERE: Duplicate ID: ".*".\n'
assert not re.search(warning_expr, warnings)
-@with_intl_app(buildername='html', cleanenv=True)
+@with_intl_app(buildername='xml', cleanenv=True)
def test_i18n_footnote_backlink(app):
- """i18n test for #1058"""
+ # i18n test for #1058
app.builder.build(['footnote'])
- result = (app.outdir / 'footnote.html').text(encoding='utf-8')
- expects = [
- '<a class="footnote-reference" href="#id5" id="id1">[100]</a>',
- '<a class="footnote-reference" href="#id4" id="id2">[1]</a>',
- '<a class="reference internal" href="#ref" id="id3">[ref]</a>',
- '<a class="fn-backref" href="#id2">[1]</a>',
- '<a class="fn-backref" href="#id3">[ref]</a>',
- '<a class="fn-backref" href="#id1">[100]</a>',
- ]
- for expect in expects:
- matches = re.findall(re.escape(expect), result)
- assert len(matches) == 1
+ et = ElementTree.parse(app.outdir / 'footnote.xml')
+ secs = et.findall('section')
+
+ para0 = secs[0].findall('paragraph')
+ refs0 = para0[0].findall('footnote_reference')
+ refid2id = dict([
+ (r.attrib.get('refid'), r.attrib.get('ids')) for r in refs0])
+
+ footnote0 = secs[0].findall('footnote')
+ for footnote in footnote0:
+ ids = footnote.attrib.get('ids')
+ backrefs = footnote.attrib.get('backrefs')
+ assert refid2id[ids] == backrefs
@with_intl_app(buildername='text', warning=warnfile, cleanenv=True)
@@ -232,49 +259,52 @@ def test_i18n_keep_external_links(app):
para0 = secs[0].findall('paragraph')
# external link check
- assert_elem_text_refs(
+ assert_elem(
para0[0],
- ['EXTERNAL LINK TO', 'Python', '.'],
- ['http://python.org/index.html'])
+ texts=['EXTERNAL LINK TO', 'Python', '.'],
+ refs=['http://python.org/index.html'])
# internal link check
- assert_elem_text_refs(
+ assert_elem(
para0[1],
- ['EXTERNAL LINKS', 'IS INTERNAL LINK.'],
- ['i18n-with-external-links'])
+ texts=['EXTERNAL LINKS', 'IS INTERNAL LINK.'],
+ refs=['i18n-with-external-links'])
# inline link check
- assert_elem_text_refs(
+ assert_elem(
para0[2],
- ['INLINE LINK BY', 'THE SPHINX SITE', '.'],
- ['http://sphinx-doc.org'])
+ texts=['INLINE LINK BY', 'THE SPHINX SITE', '.'],
+ refs=['http://sphinx-doc.org'])
# unnamed link check
- assert_elem_text_refs(
+ assert_elem(
para0[3],
- ['UNNAMED', 'LINK', '.'],
- ['http://google.com'])
+ texts=['UNNAMED', 'LINK', '.'],
+ refs=['http://google.com'])
# link target swapped translation
para1 = secs[1].findall('paragraph')
- assert_elem_text_refs(
+ assert_elem(
para1[0],
- ['LINK TO', 'external2', 'AND', 'external1', '.'],
- ['http://example.com/external2', 'http://example.com/external1'])
- assert_elem_text_refs(
+ texts=['LINK TO', 'external2', 'AND', 'external1', '.'],
+ refs=['http://example.com/external2',
+ 'http://example.com/external1'])
+ assert_elem(
para1[1],
- ['LINK TO', 'THE PYTHON SITE', 'AND', 'THE SPHINX SITE', '.'],
- ['http://python.org', 'http://sphinx-doc.org'])
+ texts=['LINK TO', 'THE PYTHON SITE', 'AND', 'THE SPHINX SITE',
+ '.'],
+ refs=['http://python.org', 'http://sphinx-doc.org'])
# multiple references in the same line
para2 = secs[2].findall('paragraph')
- assert_elem_text_refs(
+ assert_elem(
para2[0],
- ['LINK TO', 'EXTERNAL LINKS', ',', 'Python', ',',
- 'THE SPHINX SITE', ',', 'UNNAMED', 'AND', 'THE PYTHON SITE', '.'],
- ['i18n-with-external-links', 'http://python.org/index.html',
- 'http://sphinx-doc.org', 'http://google.com',
- 'http://python.org'])
+ texts=['LINK TO', 'EXTERNAL LINKS', ',', 'Python', ',',
+ 'THE SPHINX SITE', ',', 'UNNAMED', 'AND',
+ 'THE PYTHON SITE', '.'],
+ refs=['i18n-with-external-links', 'http://python.org/index.html',
+ 'http://sphinx-doc.org', 'http://google.com',
+ 'http://python.org'])
@with_intl_app(buildername='text', warning=warnfile, cleanenv=True)
@@ -340,44 +370,46 @@ def test_i18n_role_xref(app):
sec1, sec2 = et.findall('section')
para1, = sec1.findall('paragraph')
- assert_elem_text_refs(
+ assert_elem(
para1,
- ['LINK TO', "I18N ROCK'N ROLE XREF", ',', 'CONTENTS', ',',
- 'SOME NEW TERM', '.'],
- ['i18n-role-xref',
- 'contents',
- 'glossary_terms#term-some-term'])
+ texts=['LINK TO', "I18N ROCK'N ROLE XREF", ',', 'CONTENTS', ',',
+ 'SOME NEW TERM', '.'],
+ refs=['i18n-role-xref', 'contents',
+ 'glossary_terms#term-some-term'])
para2 = sec2.findall('paragraph')
- assert_elem_text_refs(
+ assert_elem(
para2[0],
- ['LINK TO', 'SOME OTHER NEW TERM', 'AND', 'SOME NEW TERM', '.'],
- ['glossary_terms#term-some-other-term',
- 'glossary_terms#term-some-term'])
- assert_elem_text_refs(
+ texts=['LINK TO', 'SOME OTHER NEW TERM', 'AND', 'SOME NEW TERM',
+ '.'],
+ refs=['glossary_terms#term-some-other-term',
+ 'glossary_terms#term-some-term'])
+ assert_elem(
para2[1],
- ['LINK TO', 'SAME TYPE LINKS', 'AND', "I18N ROCK'N ROLE XREF", '.'],
- ['same-type-links', 'i18n-role-xref'])
- assert_elem_text_refs(
+ texts=['LINK TO', 'SAME TYPE LINKS', 'AND',
+ "I18N ROCK'N ROLE XREF", '.'],
+ refs=['same-type-links', 'i18n-role-xref'])
+ assert_elem(
para2[2],
- ['LINK TO', 'I18N WITH GLOSSARY TERMS', 'AND', 'CONTENTS', '.'],
- ['glossary_terms', 'contents'])
- assert_elem_text_refs(
+ texts=['LINK TO', 'I18N WITH GLOSSARY TERMS', 'AND', 'CONTENTS',
+ '.'],
+ refs=['glossary_terms', 'contents'])
+ assert_elem(
para2[3],
- ['LINK TO', '--module', 'AND', '-m', '.'],
- ['cmdoption--module', 'cmdoption-m'])
- assert_elem_text_refs(
+ texts=['LINK TO', '--module', 'AND', '-m', '.'],
+ refs=['cmdoption--module', 'cmdoption-m'])
+ assert_elem(
para2[4],
- ['LINK TO', 'env2', 'AND', 'env1', '.'],
- ['envvar-env2', 'envvar-env1'])
- assert_elem_text_refs(
+ texts=['LINK TO', 'env2', 'AND', 'env1', '.'],
+ refs=['envvar-env2', 'envvar-env1'])
+ assert_elem(
para2[5],
- ['LINK TO', 'token2', 'AND', 'token1', '.'],
- []) #TODO: how do I link token role to productionlist?
- assert_elem_text_refs(
+ texts=['LINK TO', 'token2', 'AND', 'token1', '.'],
+ refs=[]) #TODO: how do I link token role to productionlist?
+ assert_elem(
para2[6],
- ['LINK TO', 'same-type-links', 'AND', "i18n-role-xref", '.'],
- ['same-type-links', 'i18n-role-xref'])
+ texts=['LINK TO', 'same-type-links', 'AND', "i18n-role-xref", '.'],
+ refs=['same-type-links', 'i18n-role-xref'])
#warnings
warnings = warnfile.getvalue().replace(os.sep, '/')
@@ -393,37 +425,35 @@ def test_i18n_label_target(app):
et = ElementTree.parse(app.outdir / 'label_target.xml')
secs = et.findall('section')
- #debug
- print (app.outdir / 'label_target.xml').text()
-
para0 = secs[0].findall('paragraph')
- assert_elem_text_refs(
+ assert_elem(
para0[0],
- ['X SECTION AND LABEL', 'POINT TO', 'implicit-target', 'AND',
- 'X SECTION AND LABEL', 'POINT TO', 'section-and-label', '.'],
- ['implicit-target', 'section-and-label'])
+ texts=['X SECTION AND LABEL', 'POINT TO', 'implicit-target', 'AND',
+ 'X SECTION AND LABEL', 'POINT TO', 'section-and-label', '.'],
+ refs=['implicit-target', 'section-and-label'])
para1 = secs[1].findall('paragraph')
- assert_elem_text_refs(
+ assert_elem(
para1[0],
- ['X EXPLICIT-TARGET', 'POINT TO', 'explicit-target', 'AND',
- 'X EXPLICIT-TARGET', 'POINT TO DUPLICATED ID LIKE', 'id1', '.'],
- ['explicit-target', 'id1'])
+ texts=['X EXPLICIT-TARGET', 'POINT TO', 'explicit-target', 'AND',
+ 'X EXPLICIT-TARGET', 'POINT TO DUPLICATED ID LIKE', 'id1',
+ '.'],
+ refs=['explicit-target', 'id1'])
para2 = secs[2].findall('paragraph')
- assert_elem_text_refs(
+ assert_elem(
para2[0],
- ['X IMPLICIT SECTION NAME', 'POINT TO', 'implicit-section-name',
- '.'],
- ['implicit-section-name'])
+ texts=['X IMPLICIT SECTION NAME', 'POINT TO',
+ 'implicit-section-name', '.'],
+ refs=['implicit-section-name'])
sec2 = secs[2].findall('section')
para2_0 = sec2[0].findall('paragraph')
- assert_elem_text_refs(
+ assert_elem(
para2_0[0],
- ['`X DUPLICATED SUB SECTION`_', 'IS BROKEN LINK.'],
- [])
+ texts=['`X DUPLICATED SUB SECTION`_', 'IS BROKEN LINK.'],
+ refs=[])
@with_intl_app(buildername='text', warning=warnfile)