Mercurial > babel > old > mirror
changeset 354:249aab27c4b3
The builtin checkers don't require setuptools any longer, validate_format and python_format from the checkers module are merged into one now.
author | aronacher |
---|---|
date | Tue, 17 Jun 2008 19:54:34 +0000 |
parents | cd8702e96f45 |
children | 2b74cff3c612 |
files | babel/messages/catalog.py babel/messages/checkers.py babel/support.py |
diffstat | 3 files changed, 143 insertions(+), 136 deletions(-) [+] |
line wrap: on
line diff
--- a/babel/messages/catalog.py +++ b/babel/messages/catalog.py @@ -568,28 +568,26 @@ ``(message, errors)`` tuple, where ``message`` is the `Message` object and ``errors`` is a sequence of `TranslationError` objects. - :note: this feature requires ``setuptools``/``pkg_resources`` to be - installed; if it is not, this method will simply return an empty - iterator :rtype: ``iterator`` """ checkers = [] try: from pkg_resources import working_set except ImportError: - return + from babel.messages.checkers import builtin_checkers + checkers.extend(builtin_checkers) else: for entry_point in working_set.iter_entry_points('babel.checkers'): checkers.append(entry_point.load()) - for message in self._messages.values(): - errors = [] - for checker in checkers: - try: - checker(self, message) - except TranslationError, e: - errors.append(e) - if errors: - yield message, errors + for message in self._messages.values(): + errors = [] + for checker in checkers: + try: + checker(self, message) + except TranslationError, e: + errors.append(e) + if errors: + yield message, errors def get(self, id, context=None): """Return the message with the specified ID and context.
--- a/babel/messages/checkers.py +++ b/babel/messages/checkers.py @@ -16,8 +16,17 @@ :since: version 0.9 """ +from itertools import izip from babel.messages.catalog import TranslationError, PYTHON_FORMAT +#: list of format chars that are compatible to each other +_string_format_compatibilities = [ + set(['i', 'd', 'u']), + set(['x', 'X']), + set(['f', 'F', 'g', 'G']) +] + + def num_plurals(catalog, message): """Verify the number of plurals in the translation.""" if not message.pluralizable: @@ -33,19 +42,125 @@ raise TranslationError("Wrong number of plural forms (expected %d)" % catalog.num_plurals) + def python_format(catalog, message): - if 'python-format' in message.flags: - msgids = message.id - if not isinstance(msgids, (list, tuple)): - msgids = (msgids,) - msgstrs = message.string - if not isinstance(msgstrs, (list, tuple)): - msgstrs = (msgstrs,) - for idx, msgid in enumerate(msgids): - if not msgstrs[idx]: - continue # no translation - for match in PYTHON_FORMAT.finditer(msgid): - param = match.group(0) - if param not in msgstrs[idx]: - raise TranslationError("Python parameter %s not found in " - "translation" % param) + if 'python-format' not in message.flags: + return + msgids = message.id + if not isinstance(msgids, (list, tuple)): + msgids = (msgids,) + msgstrs = message.string + if not isinstance(msgstrs, (list, tuple)): + msgstrs = (msgstrs,) + + for msgid, msgstr in izip(msgids, msgstrs): + if msgstr: + _validate_format(msgid, msgstr) + + +def _validate_format(format, alternative): + """Test format string `alternative` against `format`. `format` can be the + msgid of a message and `alternative` one of the `msgstr`\s. The two + arguments are not interchangeable as `alternative` may contain less + placeholders if `format` uses named placeholders. + + If `format` does not use string formatting a `ValueError` is raised. + + If the string formatting of `alternative` is compatible to `format` the + function returns `None`, otherwise a `TranslationError` is raised. + + Examples for compatible format strings: + + >>> _validate_format('Hello %s!', 'Hallo %s!') + >>> _validate_format('Hello %i!', 'Hallo %d!') + + Example for an incompatible format strings: + + >>> _validate_format('Hello %(name)s!', 'Hallo %s!') + Traceback (most recent call last): + ... + TranslationError: the format strings are of different kinds + + This function is used by the `python_format` checker. + + :param format: The original format string + :param alternative: The alternative format string that should be checked + against format + :return: None on success + :raises TranslationError: on formatting errors + """ + + def _parse(string): + result = [] + for match in PYTHON_FORMAT.finditer(string): + name, format, typechar = match.groups() + if typechar == '%' and name is not None: + continue + result.append((name, typechar)) + return result + + def _compatible(a, b): + if a == b: + return True + for set in _string_format_compatibilities: + if a in set and b in set: + return True + return False + + def _check_positional(results): + positional = None + for name, char in results: + if positional is None: + positional = name is None + else: + if (name is None) != positional: + raise ValueError('format string mixes positional ' + 'and named placeholders') + return bool(positional) + + a, b = map(_parse, (format, alternative)) + + # if a does not use string formattings, we are dealing with invalid + # input data. This function only works if the first string provided + # does contain string format chars + if not a: + raise ValueError('original string provided does not use string ' + 'formatting.') + + # now check if both strings are positional or named + a_positional, b_positional = map(_check_positional, (a, b)) + if a_positional and not b_positional and not b: + raise TranslationError('placeholders are incompatible') + elif a_positional != b_positional: + raise TranslationError('the format strings are of different kinds') + + # if we are operating on positional strings both must have the + # same number of format chars and those must be compatible + if a_positional: + if len(a) != len(b): + raise TranslationError('positional format placeholders are ' + 'unbalanced') + for idx, ((_, first), (_, second)) in enumerate(izip(a, b)): + if not _compatible(first, second): + raise TranslationError('incompatible format for placeholder ' + '%d: %r and %r are not compatible' % + (idx + 1, first, second)) + + # otherwise the second string must not have names the first one + # doesn't have and the types of those included must be compatible + else: + type_map = dict(a) + for name, typechar in b: + if name not in type_map: + raise TranslationError('unknown named placeholder %r' % name) + elif not _compatible(typechar, type_map[name]): + raise TranslationErrorError('incompatible format for ' + 'placeholder %r: ' + '%r and %r are not compatible' % + (name, typechar, type_map[name])) + + +#: list of builtin checkers for babel installations without setuptools. +#: Keep this in sync with the mapping in the setup.py +#: :see: babel.messages.catalog.Catalog.check +builtin_checkers = [num_plurals, python_format]
--- a/babel/support.py +++ b/babel/support.py @@ -29,9 +29,9 @@ from babel.dates import format_date, format_datetime, format_time, LC_TIME from babel.numbers import format_number, format_decimal, format_currency, \ format_percent, format_scientific, LC_NUMERIC -from babel.util import UTC, PYTHON_FORMAT +from babel.util import UTC -__all__ = ['Format', 'LazyProxy', 'Translations', 'validate_format'] +__all__ = ['Format', 'LazyProxy', 'Translations'] __docformat__ = 'restructuredtext en' @@ -324,109 +324,3 @@ def __repr__(self): return "<%s>" % (type(self).__name__) - - -#: list of format chars that are compatible to each other -_string_format_compatibilities = [ - set(['i', 'd', 'u']), - set(['x', 'X']), - set(['f', 'F', 'g', 'G']) -] - - -def validate_format(format, alternative): - """Test format string `alternative` against `format`. `format` can be the - msgid of a message and `alternative` one of the `msgstr`\s. The two - arguments are not interchangeable as `alternative` may contain less - placeholders if `format` uses named placeholders. - - If `format` does not use string formatting a `TypeError` is raised. - - If the string formatting of `alternative` is compatible to `format` the - function returns `None`, otherwise a `ValueError` is raised. - - Examples for compatible format strings: - - >>> validate_format('Hello %s!', 'Hallo %s!') - >>> validate_format('Hello %i!', 'Hallo %d!') - - Example for an incompatible format strings: - - >>> validate_format('Hello %(name)s!', 'Hallo %s!') - Traceback (most recent call last): - ... - TypeError: the format strings are of different kinds - - :param format: The original format string - :param alternative: The alternative format string that should be checked - against format - :return: None on success - :raises ValueError: on an formatting error - """ - - def _parse(string): - result = [] - for match in PYTHON_FORMAT.finditer(string): - name, format, typechar = match.groups() - if typechar == '%' and name is not None: - continue - result.append((name, typechar)) - return result - - def _compatible(a, b): - if a == b: - return True - for set in _string_format_compatibilities: - if a in set and b in set: - return True - return False - - def _check_positional(results): - positional = None - for name, char in results: - if positional is None: - positional = name is None - else: - if (name is None) != positional: - raise ValueError('format string mixes positional ' - 'and named placeholders') - return bool(positional) - - a, b = map(_parse, (format, alternative)) - - # if a does not use string formattings, we are dealing with invalid - # input data. This function only works if the first string provided - # does contain string format chars - if not a: - raise TypeError('original string provided does not use string ' - 'formatting.') - - # now check if both strings are positional or named - a_positional, b_positional = map(_check_positional, (a, b)) - if a_positional and not b_positional and not b: - raise ValueError('placeholders are incompatible') - elif a_positional != b_positional: - raise TypeError('the format strings are of different kinds') - - # if we are operating on positional strings both must have the - # same number of format chars and those must be compatible - if a_positional: - if len(a) != len(b): - raise ValueError('positional format placeholders unbalanced') - for idx, ((_, first), (_, second)) in enumerate(zip(a, b)): - if not _compatible(first, second): - raise ValueError('incompatible format for placeholder %d: ' - '%r and %r are not compatible' % - (idx + 1, first, second)) - - # otherwise the second string must not have names the first one - # doesn't have and the types of those included must be compatible - else: - type_map = dict(a) - for name, typechar in b: - if name not in type_map: - raise ValueError('unknown named placeholder %r' % name) - elif not _compatible(typechar, type_map[name]): - raise ValueError('incompatible format for placeholder %r: ' - '%r and %r are not compatible' % - (name, typechar, type_map[name]))