# -*- coding: utf-8 -*- """ test_intl ~~~~~~~~~ Test message patching for internationalization purposes. Runs the text builder in the test root. :copyright: Copyright 2007-2013 by the Sphinx team, see AUTHORS. :license: BSD, see LICENSE for details. """ import os import re from StringIO import StringIO from subprocess import Popen, PIPE from xml.etree import ElementTree from sphinx.util.pycompat import relpath from util import test_roots, path, with_app, SkipTest warnfile = StringIO() root = test_roots / 'test-intl' doctreedir = root / '_build' / 'doctree' def with_intl_app(*args, **kw): default_kw = { 'srcdir': root, 'doctreedir': doctreedir, 'confoverrides': { 'language': 'xx', 'locale_dirs': ['.'], 'gettext_compact': False, }, } default_kw.update(kw) return with_app(*args, **default_kw) def setup_module(): # Delete remnants left over after failed build (root / 'xx').rmtree(True) (root / 'xx' / 'LC_MESSAGES').makedirs() # Compile all required catalogs into binary format (*.mo). for dirpath, dirs, files in os.walk(root): dirpath = path(dirpath) for f in [f for f in files if f.endswith('.po')]: po = dirpath / f mo = root / 'xx' / 'LC_MESSAGES' / ( relpath(po[:-3], root) + '.mo') if not mo.parent.exists(): mo.parent.makedirs() try: p = Popen(['msgfmt', po, '-o', mo], stdout=PIPE, stderr=PIPE) except OSError: raise SkipTest # most likely msgfmt was not found else: stdout, stderr = p.communicate() if p.returncode != 0: print stdout print stderr assert False, \ 'msgfmt exited with return code %s' % p.returncode assert mo.isfile(), 'msgfmt failed' def teardown_module(): (root / '_build').rmtree(True) (root / 'xx').rmtree(True) def elem_gettexts(elem): def itertext(self): # this function copied from Python-2.7 'ElementTree.itertext'. # for compatibility to Python-2.5, 2.6, 3.1 tag = self.tag if not isinstance(tag, basestring) and tag is not None: return if self.text: yield self.text for e in self: for s in itertext(e): yield s if e.tail: yield e.tail return filter(None, [s.strip() for s in itertext(elem)]) 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 @with_intl_app(buildername='text') def test_simple(app): app.builder.build(['bom']) result = (app.outdir / 'bom.txt').text(encoding='utf-8') expect = (u"\nDatei mit UTF-8" u"\n***************\n" # underline matches new translation u"\nThis file has umlauts: äöü.\n") assert result == expect @with_intl_app(buildername='text') def test_subdir(app): app.builder.build(['subdir/contents']) result = (app.outdir / 'subdir' / 'contents.txt').text(encoding='utf-8') assert result.startswith(u"\nsubdir contents\n***************\n") @with_intl_app(buildername='text', warning=warnfile) def test_i18n_warnings_in_translation(app): app.builddir.rmtree(True) app.builder.build(['warnings']) result = (app.outdir / 'warnings.txt').text(encoding='utf-8') expect = (u"\nI18N WITH REST WARNINGS" u"\n***********************\n" u"\nLINE OF >>``<[100]', '[1]', '[ref]', '[1]', '[ref]', '[100]', ] for expect in expects: matches = re.findall(re.escape(expect), result) assert len(matches) == 1 @with_intl_app(buildername='text', warning=warnfile, cleanenv=True) def test_i18n_warn_for_number_of_references_inconsistency(app): app.builddir.rmtree(True) app.builder.build(['refs_inconsistency']) result = (app.outdir / 'refs_inconsistency.txt').text(encoding='utf-8') expect = (u"\nI18N WITH REFS INCONSISTENCY" u"\n****************************\n" u"\n* FOR FOOTNOTE [ref2].\n" u"\n* reference FOR reference.\n" u"\n* ORPHAN REFERENCE: I18N WITH REFS INCONSISTENCY.\n" u"\n[1] THIS IS A AUTO NUMBERED FOOTNOTE.\n" u"\n[ref2] THIS IS A NAMED FOOTNOTE.\n" u"\n[100] THIS IS A NUMBERED FOOTNOTE.\n") assert result == expect warnings = warnfile.getvalue().replace(os.sep, '/') warning_fmt = u'.*/refs_inconsistency.txt:\\d+: ' \ u'WARNING: inconsistent %s in translated message\n' expected_warning_expr = ( warning_fmt % 'footnote references' + warning_fmt % 'references' + warning_fmt % 'references') assert re.search(expected_warning_expr, warnings) @with_intl_app(buildername='html', cleanenv=True) def test_i18n_link_to_undefined_reference(app): app.builder.build(['refs_inconsistency']) result = (app.outdir / 'refs_inconsistency.html').text(encoding='utf-8') expected_expr = ('reference') assert len(re.findall(expected_expr, result)) == 2 expected_expr = ('reference') assert len(re.findall(expected_expr, result)) == 0 expected_expr = ('I18N WITH ' 'REFS INCONSISTENCY') assert len(re.findall(expected_expr, result)) == 1 @with_intl_app(buildername='xml', cleanenv=True) def test_i18n_keep_external_links(app): # regression test for #1044 app.builder.build(['external_links']) et = ElementTree.parse(app.outdir / 'external_links.xml') secs = et.findall('section') para0 = secs[0].findall('paragraph') # external link check assert_elem_text_refs( para0[0], ['EXTERNAL LINK TO', 'Python', '.'], ['http://python.org']) # internal link check assert_elem_text_refs( para0[1], ['EXTERNAL LINKS', 'IS INTERNAL LINK.'], ['i18n-with-external-links']) # inline link check assert_elem_text_refs( para0[2], ['INLINE LINK BY', 'SPHINX', '.'], ['http://sphinx-doc.org']) # unnamed link check assert_elem_text_refs( para0[3], ['UNNAMED', 'LINK', '.'], ['http://google.com']) # link target swapped translation para1 = secs[1].findall('paragraph') assert_elem_text_refs( para1[0], ['LINK TO', 'external2', 'AND', 'external1', '.'], ['http://example.com/external2', 'http://example.com/external1']) assert_elem_text_refs( para1[1], ['LINK TO', 'THE PYTHON', 'AND', 'THE SPHINX', '.'], ['http://python.org', 'http://sphinx-doc.org']) # multiple references in the same line para2 = secs[2].findall('paragraph') assert_elem_text_refs( para2[0], ['LINK TO', 'EXTERNAL LINKS', ',', 'Python', ',', 'THE SPHINX', ',', 'UNNAMED', 'AND', 'THE PYTHON', '.'], ['i18n-with-external-links', 'http://python.org', 'http://sphinx-doc.org', 'http://google.com', 'http://python.org']) @with_intl_app(buildername='text', warning=warnfile, cleanenv=True) def test_i18n_literalblock_warning(app): app.builddir.rmtree(True) #for warnings acceleration app.builder.build(['literalblock']) result = (app.outdir / 'literalblock.txt').text(encoding='utf-8') expect = (u"\nI18N WITH LITERAL BLOCK" u"\n***********************\n" u"\nCORRECT LITERAL BLOCK:\n" u"\n this is" u"\n literal block\n" u"\nMISSING LITERAL BLOCK:\n" u"\n\n*(.*?)' % name, result, re.DOTALL) if matched: return matched.group(1) else: return '' expect1 = ( u"""

Deprecated since version 1.0: """ u"""THIS IS THE FIRST PARAGRAPH OF DEPRECATED.

\n""" u"""

THIS IS THE SECOND PARAGRAPH OF DEPRECATED.

\n""") matched_content = get_content(result, "deprecated") assert expect1 == matched_content expect2 = ( u"""

New in version 1.0: """ u"""THIS IS THE FIRST PARAGRAPH OF VERSIONADDED.

\n""") matched_content = get_content(result, "versionadded") assert expect2 == matched_content expect3 = ( u"""

Changed in version 1.0: """ u"""THIS IS THE FIRST PARAGRAPH OF VERSIONCHANGED.

\n""") matched_content = get_content(result, "versionchanged") assert expect3 == matched_content @with_intl_app(buildername='text', cleanenv=True) def test_i18n_docfields(app): app.builder.build(['docfields']) result = (app.outdir / 'docfields.txt').text(encoding='utf-8') expect = (u"\nI18N WITH DOCFIELDS" u"\n*******************\n" u"\nclass class Cls1\n" u"\n Parameters:" u"\n **param** -- DESCRIPTION OF PARAMETER param\n" u"\nclass class Cls2\n" u"\n Parameters:" u"\n * **foo** -- DESCRIPTION OF PARAMETER foo\n" u"\n * **bar** -- DESCRIPTION OF PARAMETER bar\n" u"\nclass class Cls3(values)\n" u"\n Raises ValueError:" u"\n IF THE VALUES ARE OUT OF RANGE\n" u"\nclass class Cls4(values)\n" u"\n Raises:" u"\n * **TypeError** -- IF THE VALUES ARE NOT VALID\n" u"\n * **ValueError** -- IF THE VALUES ARE OUT OF RANGE\n" u"\nclass class Cls5\n" u"\n Returns:" u'\n A NEW "Cls3" INSTANCE\n') assert result == expect @with_intl_app(buildername='html', cleanenv=True) def test_i18n_docfields_html(app): app.builder.build(['docfields']) result = (app.outdir / 'docfields.html').text(encoding='utf-8') # expect no error by build @with_intl_app(buildername='html') def test_gettext_template(app): app.builder.build_all() result = (app.outdir / 'index.html').text(encoding='utf-8') assert "WELCOME" in result assert "SPHINX 2013.120" in result @with_intl_app(buildername='html') def test_rebuild_by_mo_mtime(app): app.builder.build_update() _, count, _ = app.env.update(app.config, app.srcdir, app.doctreedir, app) assert count == 0 mo = (app.srcdir / 'xx' / 'LC_MESSAGES' / 'bom.mo').bytes() (app.srcdir / 'xx' / 'LC_MESSAGES' / 'bom.mo').write_bytes(mo) _, count, _ = app.env.update(app.config, app.srcdir, app.doctreedir, app) assert count == 1