-
Notifications
You must be signed in to change notification settings - Fork 13
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
3 changed files
with
116 additions
and
41 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,54 +1,61 @@ | ||
from contextlib import closing | ||
|
||
import logging | ||
import os | ||
from os.path import join | ||
from stat import ST_MTIME | ||
logger = logging.getLogger('zope.i18n') | ||
|
||
def _cannot_compile_mo_file(domain, lc_messages_path): | ||
logger.critical("Unable to compile messages: Python `gettext` library missing.") | ||
return | ||
|
||
HAS_PYTHON_GETTEXT = True | ||
try: | ||
from pythongettext.msgfmt import Msgfmt | ||
from pythongettext.msgfmt import PoSyntaxError | ||
except ImportError: | ||
except ImportError: # pragma: no cover | ||
HAS_PYTHON_GETTEXT = False | ||
compile_mo_file = _cannot_compile_mo_file | ||
else: | ||
from contextlib import closing | ||
import os.path | ||
from os.path import join | ||
|
||
logger = logging.getLogger('zope.i18n') | ||
HAS_PYTHON_GETTEXT = True | ||
|
||
def _safe_mtime(path): | ||
try: | ||
return os.path.getmtime(path) | ||
except (IOError, OSError): | ||
return None | ||
|
||
def compile_mo_file(domain, lc_messages_path): | ||
"""Creates or updates a mo file in the locales folder.""" | ||
if not HAS_PYTHON_GETTEXT: | ||
logger.critical("Unable to compile messages: Python `gettext` library missing.") | ||
return | ||
def compile_mo_file(domain, lc_messages_path): | ||
"""Creates or updates a mo file in the locales folder.""" | ||
|
||
base = join(lc_messages_path, domain) | ||
pofile = str(base + '.po') | ||
mofile = str(base + '.mo') | ||
base = join(lc_messages_path, domain) | ||
pofile = str(base + '.po') | ||
mofile = str(base + '.mo') | ||
|
||
po_mtime = 0 | ||
try: | ||
po_mtime = os.stat(pofile)[ST_MTIME] | ||
except (IOError, OSError): | ||
return | ||
po_mtime = _safe_mtime(pofile) | ||
mo_mtime = _safe_mtime(mofile) if os.path.exists(mofile) else 0 | ||
|
||
mo_mtime = 0 | ||
if os.path.exists(mofile): | ||
# Update mo file? | ||
try: | ||
mo_mtime = os.stat(mofile)[ST_MTIME] | ||
except (IOError, OSError): | ||
if po_mtime is None or mo_mtime is None: | ||
logger.debug("Unable to access either %s (%s) or %s (%s)", | ||
pofile, po_mtime, mofile, mo_mtime) | ||
return | ||
|
||
if po_mtime > mo_mtime: | ||
try: | ||
# Msgfmt.getAsFile returns io.BytesIO on Python 3, and cStringIO.StringIO | ||
# on Python 2; sadly StringIO isn't a proper context manager, so we have to | ||
# wrap it with `closing`. Also, Msgfmt doesn't properly close a file | ||
# it opens for reading if you pass the path, but it does if you pass | ||
# the file. | ||
with closing(Msgfmt(open(pofile, 'rb'), domain).getAsFile()) as mo: | ||
with open(mofile, 'wb') as fd: | ||
fd.write(mo.read()) | ||
except PoSyntaxError as err: | ||
logger.warning('Syntax error while compiling %s (%s).', pofile, err.msg) | ||
except (IOError, OSError) as err: | ||
logger.warning('Error while compiling %s (%s).', pofile, err) | ||
if po_mtime > mo_mtime: | ||
try: | ||
# Msgfmt.getAsFile returns io.BytesIO on Python 3, and cStringIO.StringIO | ||
# on Python 2; sadly StringIO isn't a proper context manager, so we have to | ||
# wrap it with `closing`. Also, Msgfmt doesn't properly close a file | ||
# it opens for reading if you pass the path, but it does if you pass | ||
# the file. | ||
with open(pofile, 'rb') as pofd: | ||
with closing(Msgfmt(pofd, domain).getAsFile()) as mo: | ||
with open(mofile, 'wb') as fd: | ||
fd.write(mo.read()) | ||
# For testing we return distinct values for each scenario | ||
return True | ||
except PoSyntaxError as err: | ||
logger.warning('Syntax error while compiling %s (%s).', pofile, err.msg) | ||
return 0 | ||
except (IOError, OSError) as err: | ||
logger.warning('Error while compiling %s (%s).', pofile, err) | ||
return False |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,68 @@ | ||
############################################################################## | ||
# | ||
# Copyright (c) 2017 Zope Foundation and Contributors. | ||
# All Rights Reserved. | ||
# | ||
# This software is subject to the provisions of the Zope Public License, | ||
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. | ||
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED | ||
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | ||
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS | ||
# FOR A PARTICULAR PURPOSE. | ||
# | ||
############################################################################## | ||
|
||
import unittest | ||
|
||
from zope.i18n import compile | ||
|
||
|
||
@unittest.skipUnless(compile.HAS_PYTHON_GETTEXT, | ||
"Need python-gettext") | ||
class TestCompile(unittest.TestCase): | ||
|
||
def test_non_existant_path(self): | ||
|
||
self.assertIsNone(compile.compile_mo_file('no_such_domain', '')) | ||
|
||
def test_po_exists_but_invalid(self): | ||
import tempfile | ||
import shutil | ||
import os.path | ||
|
||
td = tempfile.mkdtemp() | ||
self.addCleanup(shutil.rmtree, td) | ||
|
||
with open(os.path.join(td, "foo.po"), 'w') as f: | ||
f.write("this should not compile") | ||
|
||
self.assertEqual( | ||
0, | ||
compile.compile_mo_file('foo', td)) | ||
|
||
def test_po_exists_cannot_write_mo(self): | ||
import tempfile | ||
import shutil | ||
import os | ||
import os.path | ||
|
||
td = tempfile.mkdtemp(suffix=".zopei18n_test_compile") | ||
self.addCleanup(shutil.rmtree, td) | ||
|
||
mofile = os.path.join(td, 'foo.mo') | ||
with open(mofile, 'w') as f: | ||
f.write("Touching") | ||
|
||
# Put it in the past, make it not writable | ||
os.utime(mofile, (1000, 1000)) | ||
os.chmod(mofile, 0) | ||
|
||
with open(os.path.join(td, "foo.po"), 'w') as f: | ||
f.write("# A comment") | ||
|
||
self.assertIs( | ||
False, | ||
compile.compile_mo_file('foo', td)) | ||
|
||
def test_cannot_compile(self): | ||
self.assertIsNone(compile._cannot_compile_mo_file(None, None)) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters