Mercurial > babel > old > babel-test
comparison babel/support.py @ 352:20d10066a42a
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 | 7215876bdf89 |
children | 7826d8e9998f |
comparison
equal
deleted
inserted
replaced
351:7215876bdf89 | 352:20d10066a42a |
---|---|
27 | 27 |
28 from babel.core import Locale | 28 from babel.core import Locale |
29 from babel.dates import format_date, format_datetime, format_time, LC_TIME | 29 from babel.dates import format_date, format_datetime, format_time, LC_TIME |
30 from babel.numbers import format_number, format_decimal, format_currency, \ | 30 from babel.numbers import format_number, format_decimal, format_currency, \ |
31 format_percent, format_scientific, LC_NUMERIC | 31 format_percent, format_scientific, LC_NUMERIC |
32 from babel.util import UTC, PYTHON_FORMAT | 32 from babel.util import UTC |
33 | 33 |
34 __all__ = ['Format', 'LazyProxy', 'Translations', 'validate_format'] | 34 __all__ = ['Format', 'LazyProxy', 'Translations'] |
35 __docformat__ = 'restructuredtext en' | 35 __docformat__ = 'restructuredtext en' |
36 | 36 |
37 | 37 |
38 class Format(object): | 38 class Format(object): |
39 """Wrapper class providing the various date and number formatting functions | 39 """Wrapper class providing the various date and number formatting functions |
322 self.files.extend(translations.files) | 322 self.files.extend(translations.files) |
323 return self | 323 return self |
324 | 324 |
325 def __repr__(self): | 325 def __repr__(self): |
326 return "<%s>" % (type(self).__name__) | 326 return "<%s>" % (type(self).__name__) |
327 | |
328 | |
329 #: list of format chars that are compatible to each other | |
330 _string_format_compatibilities = [ | |
331 set(['i', 'd', 'u']), | |
332 set(['x', 'X']), | |
333 set(['f', 'F', 'g', 'G']) | |
334 ] | |
335 | |
336 | |
337 def validate_format(format, alternative): | |
338 """Test format string `alternative` against `format`. `format` can be the | |
339 msgid of a message and `alternative` one of the `msgstr`\s. The two | |
340 arguments are not interchangeable as `alternative` may contain less | |
341 placeholders if `format` uses named placeholders. | |
342 | |
343 If `format` does not use string formatting a `TypeError` is raised. | |
344 | |
345 If the string formatting of `alternative` is compatible to `format` the | |
346 function returns `None`, otherwise a `ValueError` is raised. | |
347 | |
348 Examples for compatible format strings: | |
349 | |
350 >>> validate_format('Hello %s!', 'Hallo %s!') | |
351 >>> validate_format('Hello %i!', 'Hallo %d!') | |
352 | |
353 Example for an incompatible format strings: | |
354 | |
355 >>> validate_format('Hello %(name)s!', 'Hallo %s!') | |
356 Traceback (most recent call last): | |
357 ... | |
358 TypeError: the format strings are of different kinds | |
359 | |
360 :param format: The original format string | |
361 :param alternative: The alternative format string that should be checked | |
362 against format | |
363 :return: None on success | |
364 :raises ValueError: on an formatting error | |
365 """ | |
366 | |
367 def _parse(string): | |
368 result = [] | |
369 for match in PYTHON_FORMAT.finditer(string): | |
370 name, format, typechar = match.groups() | |
371 if typechar == '%' and name is not None: | |
372 continue | |
373 result.append((name, typechar)) | |
374 return result | |
375 | |
376 def _compatible(a, b): | |
377 if a == b: | |
378 return True | |
379 for set in _string_format_compatibilities: | |
380 if a in set and b in set: | |
381 return True | |
382 return False | |
383 | |
384 def _check_positional(results): | |
385 positional = None | |
386 for name, char in results: | |
387 if positional is None: | |
388 positional = name is None | |
389 else: | |
390 if (name is None) != positional: | |
391 raise ValueError('format string mixes positional ' | |
392 'and named placeholders') | |
393 return bool(positional) | |
394 | |
395 a, b = map(_parse, (format, alternative)) | |
396 | |
397 # if a does not use string formattings, we are dealing with invalid | |
398 # input data. This function only works if the first string provided | |
399 # does contain string format chars | |
400 if not a: | |
401 raise TypeError('original string provided does not use string ' | |
402 'formatting.') | |
403 | |
404 # now check if both strings are positional or named | |
405 a_positional, b_positional = map(_check_positional, (a, b)) | |
406 if a_positional and not b_positional and not b: | |
407 raise ValueError('placeholders are incompatible') | |
408 elif a_positional != b_positional: | |
409 raise TypeError('the format strings are of different kinds') | |
410 | |
411 # if we are operating on positional strings both must have the | |
412 # same number of format chars and those must be compatible | |
413 if a_positional: | |
414 if len(a) != len(b): | |
415 raise ValueError('positional format placeholders unbalanced') | |
416 for idx, ((_, first), (_, second)) in enumerate(zip(a, b)): | |
417 if not _compatible(first, second): | |
418 raise ValueError('incompatible format for placeholder %d: ' | |
419 '%r and %r are not compatible' % | |
420 (idx + 1, first, second)) | |
421 | |
422 # otherwise the second string must not have names the first one | |
423 # doesn't have and the types of those included must be compatible | |
424 else: | |
425 type_map = dict(a) | |
426 for name, typechar in b: | |
427 if name not in type_map: | |
428 raise ValueError('unknown named placeholder %r' % name) | |
429 elif not _compatible(typechar, type_map[name]): | |
430 raise ValueError('incompatible format for placeholder %r: ' | |
431 '%r and %r are not compatible' % | |
432 (name, typechar, type_map[name])) |