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]))
Copyright (C) 2012-2017 Edgewall Software