Skip to content

Commit

Permalink
Provide a way of checking if the catalogs are up-to-date (#831)
Browse files Browse the repository at this point in the history
  • Loading branch information
kjagiello authored Apr 8, 2022
1 parent 8ef344d commit d2d88da
Show file tree
Hide file tree
Showing 3 changed files with 118 additions and 4 deletions.
23 changes: 23 additions & 0 deletions babel/messages/catalog.py
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,13 @@ def __eq__(self, other):
def __ne__(self, other):
return self.__cmp__(other) != 0

def is_identical(self, other):
"""Checks whether messages are identical, taking into account all
properties.
"""
assert isinstance(other, Message)
return self.__dict__ == other.__dict__

def clone(self):
return Message(*map(copy, (self.id, self.string, self.locations,
self.flags, self.auto_comments,
Expand Down Expand Up @@ -837,3 +844,19 @@ def _key_for(self, id, context=None):
if context is not None:
key = (key, context)
return key

def is_identical(self, other):
"""Checks if catalogs are identical, taking into account messages and
headers.
"""
assert isinstance(other, Catalog)
for key in self._messages.keys() | other._messages.keys():
message_1 = self.get(key)
message_2 = other.get(key)
if (
message_1 is None
or message_2 is None
or not message_1.is_identical(message_2)
):
return False
return dict(self.mime_headers) == dict(other.mime_headers)
39 changes: 36 additions & 3 deletions babel/messages/frontend.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,14 +40,15 @@
distutils_log = log # "distutils.log → (no replacement yet)"

try:
from setuptools.errors import OptionError, SetupError
from setuptools.errors import OptionError, SetupError, BaseError
except ImportError: # Error aliases only added in setuptools 59 (2021-11).
OptionError = SetupError = Exception
OptionError = SetupError = BaseError = Exception

except ImportError:
from distutils import log as distutils_log
from distutils.cmd import Command as _Command
from distutils.errors import OptionError as OptionError, DistutilsSetupError as SetupError
from distutils.errors import OptionError as OptionError, DistutilsSetupError as SetupError, DistutilsError as BaseError



def listify_value(arg, split=None):
Expand Down Expand Up @@ -713,10 +714,15 @@ class update_catalog(Command):
'update target header comment'),
('previous', None,
'keep previous msgids of translated messages'),
('check=', None,
'don\'t update the catalog, just return the status. Return code 0 '
'means nothing would change. Return code 1 means that the catalog '
'would be updated'),
]
boolean_options = [
'omit-header', 'no-wrap', 'ignore-obsolete', 'init-missing',
'no-fuzzy-matching', 'previous', 'update-header-comment',
'check',
]

def initialize_options(self):
Expand All @@ -733,6 +739,7 @@ def initialize_options(self):
self.no_fuzzy_matching = False
self.update_header_comment = False
self.previous = False
self.check = False

def finalize_options(self):
if not self.input_file:
Expand Down Expand Up @@ -766,6 +773,7 @@ def finalize_options(self):
self.previous = False

def run(self):
check_status = {}
po_files = []
if not self.output_file:
if self.locale:
Expand Down Expand Up @@ -795,6 +803,9 @@ def run(self):

for locale, filename in po_files:
if self.init_missing and not os.path.exists(filename):
if self.check:
check_status[filename] = False
continue
self.log.info(
'creating catalog %s based on %s', filename, self.input_file
)
Expand Down Expand Up @@ -833,6 +844,16 @@ def run(self):
os.remove(tmpname)
raise

if self.check:
with open(filename, "rb") as origfile:
original_catalog = read_po(origfile)
with open(tmpname, "rb") as newfile:
updated_catalog = read_po(newfile)
updated_catalog.revision_date = original_catalog.revision_date
check_status[filename] = updated_catalog.is_identical(original_catalog)
os.remove(tmpname)
continue

try:
os.rename(tmpname, filename)
except OSError:
Expand All @@ -845,6 +866,18 @@ def run(self):
shutil.copy(tmpname, filename)
os.remove(tmpname)

if self.check:
for filename, up_to_date in check_status.items():
if up_to_date:
self.log.info('Catalog %s is up to date.', filename)
else:
self.log.warning('Catalog %s is out of date.', filename)
if not all(check_status.values()):
raise BaseError("Some catalogs are out of date.")
else:
self.log.info("All the catalogs are up-to-date.")
return


class CommandLineInterface(object):
"""Command-line interface.
Expand Down
60 changes: 59 additions & 1 deletion tests/messages/test_frontend.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
from babel import __version__ as VERSION
from babel.dates import format_datetime
from babel.messages import frontend, Catalog
from babel.messages.frontend import CommandLineInterface, extract_messages, update_catalog, OptionError
from babel.messages.frontend import CommandLineInterface, extract_messages, update_catalog, OptionError, BaseError
from babel.util import LOCALTZ
from babel.messages.pofile import read_po, write_po

Expand Down Expand Up @@ -1214,6 +1214,64 @@ def test_update(self):
catalog = read_po(infp)
assert len(catalog) == 4 # Catalog was updated

def test_check(self):
template = Catalog()
template.add("1")
template.add("2")
template.add("3")
tmpl_file = os.path.join(i18n_dir, 'temp-template.pot')
with open(tmpl_file, "wb") as outfp:
write_po(outfp, template)
po_file = os.path.join(i18n_dir, 'temp1.po')
self.cli.run(sys.argv + ['init',
'-l', 'fi_FI',
'-o', po_file,
'-i', tmpl_file
])

# Update the catalog file
self.cli.run(sys.argv + ['update',
'-l', 'fi_FI',
'-o', po_file,
'-i', tmpl_file])

# Run a check without introducing any changes to the template
self.cli.run(sys.argv + ['update',
'--check',
'-l', 'fi_FI',
'-o', po_file,
'-i', tmpl_file])

# Add a new entry and expect the check to fail
template.add("4")
with open(tmpl_file, "wb") as outfp:
write_po(outfp, template)

with self.assertRaises(BaseError):
self.cli.run(sys.argv + ['update',
'--check',
'-l', 'fi_FI',
'-o', po_file,
'-i', tmpl_file])

# Write the latest changes to the po-file
self.cli.run(sys.argv + ['update',
'-l', 'fi_FI',
'-o', po_file,
'-i', tmpl_file])

# Update an entry and expect the check to fail
template.add("4", locations=[("foo.py", 1)])
with open(tmpl_file, "wb") as outfp:
write_po(outfp, template)

with self.assertRaises(BaseError):
self.cli.run(sys.argv + ['update',
'--check',
'-l', 'fi_FI',
'-o', po_file,
'-i', tmpl_file])

def test_update_init_missing(self):
template = Catalog()
template.add("1")
Expand Down

0 comments on commit d2d88da

Please sign in to comment.