diff options
author | Maurits van Rees <maurits@vanrees.org> | 2021-09-02 12:58:01 +0200 |
---|---|---|
committer | Maurits van Rees <maurits@vanrees.org> | 2021-09-02 12:58:01 +0200 |
commit | 583cde4a37e6d76f11b767dea8c518433748ec17 (patch) | |
tree | adaf3e25199843fdc376457adf1c9a404b88c8a6 | |
parent | 5e1cd72d4db7cf076e34bb6d86fd03b683ab43a2 (diff) | |
download | zope-i18n-583cde4a37e6d76f11b767dea8c518433748ec17.tar.gz |
black without string normalization
36 files changed, 2411 insertions, 1441 deletions
@@ -21,17 +21,21 @@ import os from setuptools import setup, find_packages + def read(*rnames): with open(os.path.join(os.path.dirname(__file__), *rnames)) as f: return f.read() + def alltests(): import sys import unittest + # use the zope.testrunner machinery to find all the # test suites we've put under ourselves import zope.testrunner.find import zope.testrunner.options + here = os.path.abspath(os.path.join(os.path.dirname(__file__), 'src')) args = sys.argv[:] defaults = ["--test-path", here] @@ -39,6 +43,7 @@ def alltests(): suites = list(zope.testrunner.find.find_suites(options)) return unittest.TestSuite(suites) + COMPILE_REQUIRES = [ # python-gettext used to be here, but it's now # a fixed requirement. Keep the extra to avoid @@ -51,11 +56,15 @@ ZCML_REQUIRES = [ 'zope.security', ] -TESTS_REQUIRE = COMPILE_REQUIRES + ZCML_REQUIRES + [ - 'zope.publisher', - 'zope.testing', - 'zope.testrunner', -] +TESTS_REQUIRE = ( + COMPILE_REQUIRES + + ZCML_REQUIRES + + [ + 'zope.publisher', + 'zope.testing', + 'zope.testrunner', + ] +) setup( name='zope.i18n', @@ -63,14 +72,12 @@ setup( author='Zope Foundation and Contributors', author_email='zope-dev@zope.org', description='Zope Internationalization Support', - long_description=( - read('README.rst') - + '\n\n' + - read('CHANGES.rst') - ), + long_description=(read('README.rst') + '\n\n' + read('CHANGES.rst')), license='ZPL 2.1', - keywords=('zope3 internationalization localization i18n l10n ' - 'gettext ICU locale'), + keywords=( + 'zope3 internationalization localization i18n l10n ' + 'gettext ICU locale' + ), classifiers=[ 'Development Status :: 5 - Production/Stable', 'Environment :: Web Environment', @@ -93,7 +100,9 @@ setup( url='https://github.com/zopefoundation/zope.i18n', packages=find_packages('src'), package_dir={'': 'src'}, - namespace_packages=['zope',], + namespace_packages=[ + 'zope', + ], install_requires=[ 'setuptools', 'python-gettext', @@ -116,4 +125,4 @@ setup( test_suite='__main__.alltests', include_package_data=True, zip_safe=False, - ) +) diff --git a/src/zope/i18n/__init__.py b/src/zope/i18n/__init__.py index dd525ea..f7b582c 100644 --- a/src/zope/i18n/__init__.py +++ b/src/zope/i18n/__init__.py @@ -30,12 +30,12 @@ text_type = str if bytes is not str else unicode # zope.tal.taldefs module: NAME_RE = r"[a-zA-Z_][-a-zA-Z0-9_]*" -_interp_regex = re.compile(r'(?<!\$)(\$(?:(%(n)s)|{(%(n)s)}))' - % ({'n': NAME_RE})) +_interp_regex = re.compile( + r'(?<!\$)(\$(?:(%(n)s)|{(%(n)s)}))' % ({'n': NAME_RE}) +) class _FallbackNegotiator(object): - def getLanguage(self, _allowed, _context): return None @@ -81,9 +81,17 @@ def negotiate(context): return negotiator.getLanguage(ALLOWED_LANGUAGES, context) -def translate(msgid, domain=None, mapping=None, context=None, - target_language=None, default=None, msgid_plural=None, - default_plural=None, number=None): +def translate( + msgid, + domain=None, + mapping=None, + context=None, + target_language=None, + default=None, + msgid_plural=None, + default_plural=None, + number=None, +): """Translate text. First setup some test components: @@ -190,8 +198,15 @@ def translate(msgid, domain=None, mapping=None, context=None, target_language = negotiate(context) return util.translate( - msgid, mapping, context, target_language, default, - msgid_plural, default_plural, number) + msgid, + mapping, + context, + target_language, + default, + msgid_plural, + default_plural, + number, + ) def interpolate(text, mapping=None): diff --git a/src/zope/i18n/compile.py b/src/zope/i18n/compile.py index 834bf24..30f794e 100644 --- a/src/zope/i18n/compile.py +++ b/src/zope/i18n/compile.py @@ -11,12 +11,14 @@ 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.""" @@ -28,8 +30,7 @@ def compile_mo_file(domain, lc_messages_path): mo_mtime = _safe_mtime(mofile) or 0 if po_mtime is None: - logger.debug("Unable to access %s (%s)", - pofile, po_mtime) + logger.debug("Unable to access %s (%s)", pofile, po_mtime) return if po_mtime > mo_mtime: @@ -44,6 +45,8 @@ def compile_mo_file(domain, lc_messages_path): 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) + 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) diff --git a/src/zope/i18n/config.py b/src/zope/i18n/config.py index 1636083..ca60ed0 100644 --- a/src/zope/i18n/config.py +++ b/src/zope/i18n/config.py @@ -45,4 +45,6 @@ def _parse_languages(value): #: A set of languages that `zope.i18n.negotiate` will pass to the #: `zope.i18n.interfaces.INegotiator` utility. If this is None, #: no utility will be used. -ALLOWED_LANGUAGES = _parse_languages(os.environ.get(ALLOWED_LANGUAGES_KEY, None)) +ALLOWED_LANGUAGES = _parse_languages( + os.environ.get(ALLOWED_LANGUAGES_KEY, None) +) diff --git a/src/zope/i18n/format.py b/src/zope/i18n/format.py index abc3e50..43c19c9 100644 --- a/src/zope/i18n/format.py +++ b/src/zope/i18n/format.py @@ -31,7 +31,7 @@ NATIVE_NUMBER_TYPES = (int, float) try: NATIVE_NUMBER_TYPES += (long,) except NameError: - pass # Py3 + pass # Py3 def roundHalfUp(n): @@ -45,8 +45,11 @@ def roundHalfUp(n): def _findFormattingCharacterInPattern(char, pattern): - return [entry for entry in pattern - if isinstance(entry, tuple) and entry[0] == char] + return [ + entry + for entry in pattern + if isinstance(entry, tuple) and entry[0] == char + ] class DateTimeParseError(Exception): @@ -74,8 +77,9 @@ class DateTimeFormat(object): def setPattern(self, pattern): "See zope.i18n.interfaces.IFormat" self._pattern = pattern - self._bin_pattern = parseDateTimePattern(self._pattern, - self._DATETIMECHARS) + self._bin_pattern = parseDateTimePattern( + self._pattern, self._DATETIMECHARS + ) def getPattern(self): "See zope.i18n.interfaces.IFormat" @@ -100,8 +104,8 @@ class DateTimeFormat(object): results = re.match(regex, text).groups() except AttributeError: raise DateTimeParseError( - 'The datetime string did not match the pattern %r.' - % pattern) + 'The datetime string did not match the pattern %r.' % pattern + ) # Sometimes you only want the parse results if not asObject: return results @@ -138,11 +142,14 @@ class DateTimeFormat(object): ampm_entry = _findFormattingCharacterInPattern('a', bin_pattern) if not ampm_entry: raise DateTimeParseError( - 'Cannot handle 12-hour format without am/pm marker.') - ampm = self.calendar.pm == results[bin_pattern.index(ampm_entry[0])] + 'Cannot handle 12-hour format without am/pm marker.' + ) + ampm = ( + self.calendar.pm == results[bin_pattern.index(ampm_entry[0])] + ) if hour == 12: ampm = not ampm - ordered[3] = (hour + 12*ampm)%24 + ordered[3] = (hour + 12 * ampm) % 24 # Shortcut for the simple int functions dt_fields_map = {'d': 2, 'H': 3, 'm': 4, 's': 5, 'S': 6} @@ -155,7 +162,7 @@ class DateTimeFormat(object): # Handle timezones tzinfo = None - pytz_tzinfo = False # If True, we should use pytz specific syntax + pytz_tzinfo = False # If True, we should use pytz specific syntax tz_entry = _findFormattingCharacterInPattern('z', bin_pattern) if ordered[3:] != [None, None, None, None] and tz_entry: length = tz_entry[0][1] @@ -187,18 +194,20 @@ class DateTimeFormat(object): return tzinfo.localize( datetime.datetime.combine( datetime.date.today(), - datetime.time(*[e or 0 for e in ordered[3:]]))).timetz() + datetime.time(*[e or 0 for e in ordered[3:]]), + ) + ).timetz() return datetime.time( - *[e or 0 for e in ordered[3:]], **{'tzinfo' :tzinfo} + *[e or 0 for e in ordered[3:]], **{'tzinfo': tzinfo} ) if pytz_tzinfo: - return tzinfo.localize(datetime.datetime( - *[e or 0 for e in ordered] - )) + return tzinfo.localize( + datetime.datetime(*[e or 0 for e in ordered]) + ) return datetime.datetime( - *[e or 0 for e in ordered], **{'tzinfo' :tzinfo} + *[e or 0 for e in ordered], **{'tzinfo': tzinfo} ) def format(self, obj, pattern=None): @@ -235,7 +244,7 @@ class NumberFormat(object): self.symbols = { u"decimal": u".", u"group": u",", - u"list": u";", + u"list": u";", u"percentSign": u"%", u"nativeZeroDigit": u"0", u"patternDigit": u"#", @@ -244,7 +253,7 @@ class NumberFormat(object): u"exponential": u"E", u"perMille": u"\xe2\x88\x9e", u"infinity": u"\xef\xbf\xbd", - u"nan": u'' + u"nan": u'', } self.symbols.update(symbols) if pattern is not None: @@ -281,20 +290,20 @@ class NumberFormat(object): min_size = bin_pattern[sign][INTEGER].count('0') if bin_pattern[sign][GROUPING]: regex += self.symbols['group'] - min_size += min_size/3 - regex += ']{%i,100}' %(min_size) + min_size += min_size / 3 + regex += ']{%i,100}' % (min_size) if bin_pattern[sign][FRACTION]: max_precision = len(bin_pattern[sign][FRACTION]) min_precision = bin_pattern[sign][FRACTION].count('0') - regex += '['+self.symbols['decimal']+']?' - regex += '[0-9]{%i,%i}' %(min_precision, max_precision) + regex += '[' + self.symbols['decimal'] + ']?' + regex += '[0-9]{%i,%i}' % (min_precision, max_precision) if bin_pattern[sign][EXPONENTIAL] != '': regex += self.symbols['exponential'] min_exp_size = bin_pattern[sign][EXPONENTIAL].count('0') pre_symbols = self.symbols['minusSign'] if bin_pattern[sign][EXPONENTIAL][0] == '+': pre_symbols += self.symbols['plusSign'] - regex += '[%s]?[0-9]{%i,100}' %(pre_symbols, min_exp_size) + regex += '[%s]?[0-9]{%i,100}' % (pre_symbols, min_exp_size) regex += ')' if bin_pattern[sign][PADDING3] is not None: regex += '[' + bin_pattern[sign][PADDING3] + ']+' @@ -312,8 +321,9 @@ class NumberFormat(object): num_str = num_res[1].groups()[0] sign = -1 else: - raise NumberParseError('Not a valid number for this pattern %r.' - % pattern) + raise NumberParseError( + 'Not a valid number for this pattern %r.' % pattern + ) # Remove possible grouping separators num_str = num_str.replace(self.symbols['group'], '') # Extract number @@ -326,13 +336,15 @@ class NumberFormat(object): num_str = num_str.replace(self.symbols['exponential'], 'E') if self.type: type = self.type - return sign*type(num_str) + return sign * type(num_str) def _format_integer(self, integer, pattern): size = len(integer) min_size = pattern.count('0') if size < min_size: - integer = self.symbols['nativeZeroDigit']*(min_size-size) + integer + integer = ( + self.symbols['nativeZeroDigit'] * (min_size - size) + integer + ) return integer def _format_fraction(self, fraction, pattern, rounding=True): @@ -353,7 +365,7 @@ class NumberFormat(object): fractionLen = len(fraction) rounded = int(fraction) + 1 fraction = ('%0' + str(fractionLen) + 'i') % rounded - if len(fraction) > fractionLen: # rounded fraction >= 1 + if len(fraction) > fractionLen: # rounded fraction >= 1 roundInt = True fraction = fraction[1:] else: @@ -361,8 +373,9 @@ class NumberFormat(object): roundInt = True if precision < min_precision: - fraction += self.symbols['nativeZeroDigit']*(min_precision - - precision) + fraction += self.symbols['nativeZeroDigit'] * ( + min_precision - precision + ) if fraction != '': fraction = self.symbols['decimal'] + fraction return fraction, roundInt @@ -439,22 +452,24 @@ class NumberFormat(object): # abs() of number smaller 1 if len(obj_int_frac) > 1: res = re.match('(0*)[0-9]*', obj_int_frac[1]).groups()[0] - exponent = self._format_integer(str(len(res)+1), - exp_bin_pattern) - exponent = self.symbols['minusSign']+exponent - number = obj_int_frac[1][len(res):] + exponent = self._format_integer( + str(len(res) + 1), exp_bin_pattern + ) + exponent = self.symbols['minusSign'] + exponent + number = obj_int_frac[1][len(res) :] else: # We have exactly 0 exponent = self._format_integer('0', exp_bin_pattern) number = self.symbols['nativeZeroDigit'] else: - exponent = self._format_integer(str(len(obj_int_frac[0])-1), - exp_bin_pattern) + exponent = self._format_integer( + str(len(obj_int_frac[0]) - 1), exp_bin_pattern + ) number = ''.join(obj_int_frac) - fraction, roundInt = self._format_fraction(number[1:], - bin_pattern[FRACTION], - rounding=rounding) + fraction, roundInt = self._format_fraction( + number[1:], bin_pattern[FRACTION], rounding=rounding + ) if roundInt: number = str(int(number[0]) + 1) + fraction else: @@ -471,40 +486,42 @@ class NumberFormat(object): else: if len(obj_int_frac) > 1: fraction, roundInt = self._format_fraction( - obj_int_frac[1], bin_pattern[FRACTION], rounding=rounding) + obj_int_frac[1], bin_pattern[FRACTION], rounding=rounding + ) else: fraction = '' roundInt = False if roundInt: obj = roundHalfUp(obj) - integer = self._format_integer(str(int(math.fabs(obj))), - bin_pattern[INTEGER]) + integer = self._format_integer( + str(int(math.fabs(obj))), bin_pattern[INTEGER] + ) # Adding grouping if bin_pattern[GROUPING]: integer = self._group(integer, bin_pattern[GROUPING]) pre_padding = len(bin_pattern[INTEGER]) - len(integer) - post_padding = len(bin_pattern[FRACTION]) - len(fraction)+1 + post_padding = len(bin_pattern[FRACTION]) - len(fraction) + 1 number = integer + fraction # Put it all together text = '' if bin_pattern[PADDING1] is not None and pre_padding > 0: - text += bin_pattern[PADDING1]*pre_padding + text += bin_pattern[PADDING1] * pre_padding text += bin_pattern[PREFIX] if bin_pattern[PADDING2] is not None and pre_padding > 0: if bin_pattern[PADDING1] is not None: text += bin_pattern[PADDING2] - else: # pragma: no cover + else: # pragma: no cover text += bin_pattern[PADDING2] * pre_padding text += number if bin_pattern[PADDING3] is not None and post_padding > 0: if bin_pattern[PADDING4] is not None: text += bin_pattern[PADDING3] else: - text += bin_pattern[PADDING3]*post_padding + text += bin_pattern[PADDING3] * post_padding text += bin_pattern[SUFFIX] if bin_pattern[PADDING4] is not None and post_padding > 0: - text += bin_pattern[PADDING4]*post_padding + text += bin_pattern[PADDING4] * post_padding # TODO: Need to make sure unicode is everywhere return text_type(text) @@ -578,16 +595,18 @@ def parseDateTimePattern(pattern, DATETIMECHARS="aGyMdEDFwWhHmsSkKz"): # Some cleaning up if state == IN_QUOTE: - if quote_start == -1: # pragma: no cover + if quote_start == -1: # pragma: no cover # It should not be possible to get into this state. # The only time we set quote_start to -1 we also set the state # to DEFAULT. raise DateTimePatternParseError( - 'Waaa: state = IN_QUOTE and quote_start = -1!') + 'Waaa: state = IN_QUOTE and quote_start = -1!' + ) else: raise DateTimePatternParseError( - 'The quote starting at character %i is not closed.' % - quote_start) + 'The quote starting at character %i is not closed.' + % quote_start + ) elif state == IN_DATETIMEFIELD: result.append((helper[0], len(helper))) elif state == DEFAULT: @@ -605,7 +624,7 @@ def buildDateTimeParseInfo(calendar, pattern): for entry in _findFormattingCharacterInPattern(field, pattern): # The maximum amount of digits should be infinity, but 1000 is # close enough here. - info[entry] = r'([0-9]{%i,1000})' %entry[1] + info[entry] = r'([0-9]{%i,1000})' % entry[1] # year (Number) for entry in _findFormattingCharacterInPattern('y', pattern): @@ -618,12 +637,12 @@ def buildDateTimeParseInfo(calendar, pattern): # am/pm marker (Text) for entry in _findFormattingCharacterInPattern('a', pattern): - info[entry] = r'(%s|%s)' %(calendar.am, calendar.pm) + info[entry] = r'(%s|%s)' % (calendar.am, calendar.pm) # era designator (Text) # TODO: works for gregorian only right now for entry in _findFormattingCharacterInPattern('G', pattern): - info[entry] = r'(%s|%s)' %(calendar.eras[1][1], calendar.eras[2][1]) + info[entry] = r'(%s|%s)' % (calendar.eras[1][1], calendar.eras[2][1]) # time zone (Text) for entry in _findFormattingCharacterInPattern('z', pattern): @@ -643,9 +662,11 @@ def buildDateTimeParseInfo(calendar, pattern): elif entry[1] == 2: info[entry] = r'([0-9]{2})' elif entry[1] == 3: - info[entry] = r'('+'|'.join(calendar.getMonthAbbreviations())+')' + info[entry] = ( + r'(' + '|'.join(calendar.getMonthAbbreviations()) + ')' + ) else: - info[entry] = r'('+'|'.join(calendar.getMonthNames())+')' + info[entry] = r'(' + '|'.join(calendar.getMonthNames()) + ')' # day in week (Text and Number) for entry in _findFormattingCharacterInPattern('E', pattern): @@ -654,9 +675,9 @@ def buildDateTimeParseInfo(calendar, pattern): elif entry[1] == 2: info[entry] = r'([0-9]{2})' elif entry[1] == 3: - info[entry] = r'('+'|'.join(calendar.getDayAbbreviations())+')' + info[entry] = r'(' + '|'.join(calendar.getDayAbbreviations()) + ')' else: - info[entry] = r'('+'|'.join(calendar.getDayNames())+')' + info[entry] = r'(' + '|'.join(calendar.getDayNames()) + ')' return info @@ -665,10 +686,12 @@ def buildDateTimeInfo(dt, calendar, pattern): """Create the bits and pieces of the datetime object that can be put together.""" if isinstance(dt, datetime.time): - dt = datetime.datetime(1969, 1, 1, dt.hour, dt.minute, dt.second, - dt.microsecond) - elif (isinstance(dt, datetime.date) and - not isinstance(dt, datetime.datetime)): + dt = datetime.datetime( + 1969, 1, 1, dt.hour, dt.minute, dt.second, dt.microsecond + ) + elif isinstance(dt, datetime.date) and not isinstance( + dt, datetime.datetime + ): dt = datetime.datetime(dt.year, dt.month, dt.day) if dt.hour >= 12: @@ -676,7 +699,7 @@ def buildDateTimeInfo(dt, calendar, pattern): else: ampm = calendar.am - h = dt.hour%12 + h = dt.hour % 12 if h == 0: h = 12 @@ -693,7 +716,7 @@ def buildDateTimeInfo(dt, calendar, pattern): tz_mins = int(math.fabs(tz_secs % 3600 / 60)) tz_hours = int(math.fabs(tz_secs / 3600)) tz_sign = '-' if tz_secs < 0 else '+' - tz_defaultname = "%s%i%.2i" %(tz_sign, tz_hours, tz_mins) + tz_defaultname = "%s%i%.2i" % (tz_sign, tz_hours, tz_mins) tz_name = tzinfo.tzname(dt) or tz_defaultname tz_fullname = getattr(tzinfo, 'zone', None) or tz_name @@ -703,14 +726,22 @@ def buildDateTimeInfo(dt, calendar, pattern): } # Generic Numbers - for field, value in (('d', dt.day), ('D', int(dt.strftime('%j'))), - ('F', day_of_week_in_month), ('k', dt.hour or 24), - ('K', dt.hour%12), ('h', h), ('H', dt.hour), - ('m', dt.minute), ('s', dt.second), - ('S', dt.microsecond), ('w', int(dt.strftime('%W'))), - ('W', week_in_month)): + for field, value in ( + ('d', dt.day), + ('D', int(dt.strftime('%j'))), + ('F', day_of_week_in_month), + ('k', dt.hour or 24), + ('K', dt.hour % 12), + ('h', h), + ('H', dt.hour), + ('m', dt.minute), + ('s', dt.second), + ('S', dt.microsecond), + ('w', int(dt.strftime('%W'))), + ('W', week_in_month), + ): for entry in _findFormattingCharacterInPattern(field, pattern): - info[entry] = (u"%%.%ii" %entry[1]) %value + info[entry] = (u"%%.%ii" % entry[1]) % value # am/pm marker (Text) for entry in _findFormattingCharacterInPattern('a', pattern): @@ -724,9 +755,9 @@ def buildDateTimeInfo(dt, calendar, pattern): # time zone (Text) for entry in _findFormattingCharacterInPattern('z', pattern): if entry[1] == 1: - info[entry] = u"%s%i%.2i" %(tz_sign, tz_hours, tz_mins) + info[entry] = u"%s%i%.2i" % (tz_sign, tz_hours, tz_mins) elif entry[1] == 2: - info[entry] = u"%s%.2i:%.2i" %(tz_sign, tz_hours, tz_mins) + info[entry] = u"%s%.2i:%.2i" % (tz_sign, tz_hours, tz_mins) elif entry[1] == 3: info[entry] = tz_name else: @@ -820,7 +851,8 @@ def parseNumberPattern(pattern): helper += char else: raise NumberPatternParseError( - 'Wrong syntax at beginning of pattern.') + 'Wrong syntax at beginning of pattern.' + ) elif state == READ_PADDING_1: padding_1 = char @@ -964,15 +996,25 @@ def parseNumberPattern(pattern): last_index = -1 for index, char in enumerate(reversed(integer)): if char == ",": - grouping += (index-last_index-1,) + grouping += (index - last_index - 1,) last_index = index # use last group ad infinitum grouping += (0,) # remove grouping markers from integer pattern integer = integer.replace(",", "") - pattern = (padding_1, prefix, padding_2, integer, fraction, exponential, - padding_3, suffix, padding_4, grouping) + pattern = ( + padding_1, + prefix, + padding_2, + integer, + fraction, + exponential, + padding_3, + suffix, + padding_4, + grouping, + ) if neg_pattern is None: neg_pattern = pattern diff --git a/src/zope/i18n/gettextmessagecatalog.py b/src/zope/i18n/gettextmessagecatalog.py index 8e1272b..1fff37b 100644 --- a/src/zope/i18n/gettextmessagecatalog.py +++ b/src/zope/i18n/gettextmessagecatalog.py @@ -39,6 +39,7 @@ def plural_formatting(func): Please note that the interpolation can be done, alternatively, using the mapping. This is only present as a conveniance. """ + @wraps(func) def pformat(catalog, singular, plural, n, *args, **kwargs): msg = func(catalog, singular, plural, n, *args, **kwargs) @@ -47,6 +48,7 @@ def plural_formatting(func): except TypeError: # The message cannot be formatted : return it "raw". return msg + return pformat @@ -59,19 +61,23 @@ class GettextMessageCatalog(object): def __init__(self, language, domain, path_to_file): """Initialize the message catalog""" self.language = ( - language.decode('utf-8') if isinstance(language, bytes) - else language) + language.decode('utf-8') + if isinstance(language, bytes) + else language + ) self.domain = ( - domain.decode("utf-8") if isinstance(domain, bytes) - else domain) + domain.decode("utf-8") if isinstance(domain, bytes) else domain + ) self._path_to_file = path_to_file self.reload() catalog = self._catalog catalog.add_fallback(_KeyErrorRaisingFallback()) self._gettext = ( - catalog.gettext if str is not bytes else catalog.ugettext) + catalog.gettext if str is not bytes else catalog.ugettext + ) self._ngettext = ( - catalog.ngettext if str is not bytes else catalog.ungettext) + catalog.ngettext if str is not bytes else catalog.ungettext + ) def reload(self): 'See IMessageCatalog' diff --git a/src/zope/i18n/interfaces/__init__.py b/src/zope/i18n/interfaces/__init__.py index c5a3328..3231605 100644 --- a/src/zope/i18n/interfaces/__init__.py +++ b/src/zope/i18n/interfaces/__init__.py @@ -87,12 +87,14 @@ class IMessageCatalog(Interface): language = TextLine( title=u"Language", description=u"The language the catalog translates to.", - required=True) + required=True, + ) domain = TextLine( title=u"Domain", description=u"The domain the catalog is registered for.", - required=True) + required=True, + ) def getIdentifier(): """Return a identifier for this message catalog. Note that this @@ -104,7 +106,6 @@ class IMessageCatalog(Interface): class IGlobalMessageCatalog(IMessageCatalog): - def reload(): """Reload and parse .po file""" @@ -151,11 +152,19 @@ class ITranslationDomain(Interface): domain = TextLine( title=u"Domain Name", description=u"The name of the domain this object represents.", - required=True) - - def translate(msgid, mapping=None, context=None, target_language=None, - default=None, msgid_plural=None, default_plural=None, - number=None): + required=True, + ) + + def translate( + msgid, + mapping=None, + context=None, + target_language=None, + default=None, + msgid_plural=None, + default_plural=None, + number=None, + ): """Return the translation for the message referred to by msgid. Return the default if no translation is found. @@ -178,8 +187,7 @@ class IFallbackTranslationDomainFactory(Interface): """ def __call__(domain_id=u""): - """Return a fallback translation domain for the given domain id. - """ + """Return a fallback translation domain for the given domain id.""" class ITranslator(Interface): @@ -189,8 +197,14 @@ class ITranslator(Interface): the domain, context, and target language. """ - def translate(msgid, mapping=None, default=None, - msgid_plural=None, default_plural=None, number=None): + def translate( + msgid, + mapping=None, + default=None, + msgid_plural=None, + default_plural=None, + number=None, + ): """Translate the source msgid using the given mapping. See ITranslationService for details. @@ -200,21 +214,19 @@ class ITranslator(Interface): class IMessageImportFilter(Interface): """The Import Filter for Translation Service Messages. - Classes implementing this interface should usually be Adaptors, as - they adapt the IEditableTranslationService interface.""" - + Classes implementing this interface should usually be Adaptors, as + they adapt the IEditableTranslationService interface.""" def importMessages(domains, languages, file): """Import all messages that are defined in the specified domains and - languages. + languages. - Note that some implementations might limit to only one domain and - one language. A good example for that is a GettextFile. + Note that some implementations might limit to only one domain and + one language. A good example for that is a GettextFile. """ class ILanguageAvailability(Interface): - def getAvailableLanguages(): """Return a sequence of 3-tuples for available languages @@ -239,7 +251,6 @@ class IUserPreferredLanguages(Interface): class IModifiableUserPreferredLanguages(IUserPreferredLanguages): - def setPreferredLanguages(languages): """Set a sequence of user preferred languages. @@ -251,22 +262,20 @@ class IModifiableUserPreferredLanguages(IUserPreferredLanguages): class IMessageExportFilter(Interface): """The Export Filter for Translation Service Messages. - Classes implementing this interface should usually be Adaptors, as - they adapt the IEditableTranslationService interface.""" - + Classes implementing this interface should usually be Adaptors, as + they adapt the IEditableTranslationService interface.""" def exportMessages(domains, languages): """Export all messages that are defined in the specified domains and - languages. + languages. - Note that some implementations might limit to only one domain and - one language. A good example for that is a GettextFile. + Note that some implementations might limit to only one domain and + one language. A good example for that is a GettextFile. """ class INegotiator(Interface): - """A language negotiation service. - """ + """A language negotiation service.""" def getLanguage(langs, env): """Return the matching language to use. @@ -295,13 +304,12 @@ class INegotiator(Interface): class IUserPreferredCharsets(Interface): - """This interface provides charset negotiation based on user preferences. - """ + """This interface provides charset negotiation based on user preferences.""" def getPreferredCharsets(): """Return a sequence of user preferred charsets. Note that the order - should describe the order of preference. Therefore the first - character set in the list is the most preferred one. + should describe the order of preference. Therefore the first + character set in the list is the most preferred one. """ @@ -326,7 +334,6 @@ class IFormat(Interface): """Format an object to a string using the pattern as a rule.""" - class INumberFormat(IFormat): r"""Specific number formatting interface. Here are the formatting rules (I modified the rules from ICU a bit, since I think they did not @@ -383,21 +390,38 @@ class INumberFormat(IFormat): type = Field( title=u"Type", - description=((u"The type into which a string is parsed. If ``None``, " - u"then ``int`` will be used for whole numbers and " - u"``float`` for decimals.")), + description=( + ( + u"The type into which a string is parsed. If ``None``, " + u"then ``int`` will be used for whole numbers and " + u"``float`` for decimals." + ) + ), default=None, - required=False) + required=False, + ) symbols = Dict( title=u"Number Symbols", key_type=Choice( title=u"Dictionary Class", - values=(u"decimal", u"group", u"list", u"percentSign", - u"nativeZeroDigit", u"patternDigit", u"plusSign", - u"minusSign", u"exponential", u"perMille", - u"infinity", u"nan")), - value_type=TextLine(title=u"Symbol")) + values=( + u"decimal", + u"group", + u"list", + u"percentSign", + u"nativeZeroDigit", + u"patternDigit", + u"plusSign", + u"minusSign", + u"exponential", + u"perMille", + u"infinity", + u"nan", + ), + ), + value_type=TextLine(title=u"Symbol"), + ) class IDateTimeFormat(IFormat): @@ -448,7 +472,9 @@ class IDateTimeFormat(IFormat): Three or over, use text, otherwise use number. (for example, "M" produces "1", "MM" produces "01", "MMM" produces "Jan", and "MMMM" - produces "January".) """ + produces "January".)""" - calendar = Attribute("""This object must implement ILocaleCalendar. See - this interface's documentation for details.""") + calendar = Attribute( + """This object must implement ILocaleCalendar. See + this interface's documentation for details.""" + ) diff --git a/src/zope/i18n/interfaces/locales.py b/src/zope/i18n/interfaces/locales.py index f6017ad..98e6c18 100644 --- a/src/zope/i18n/interfaces/locales.py +++ b/src/zope/i18n/interfaces/locales.py @@ -16,8 +16,17 @@ import datetime import re from zope.interface import Interface, Attribute -from zope.schema import \ - Field, Text, TextLine, Int, Bool, Tuple, List, Dict, Date +from zope.schema import ( + Field, + Text, + TextLine, + Int, + Bool, + Tuple, + List, + Dict, + Date, +) from zope.schema import Choice @@ -28,7 +37,7 @@ class ILocaleProvider(Interface): This service will be singelton global service, since it doe not make much sense to have many locale facilities, especially since this one will be so - complete, since we will the ICU XML Files as data. """ + complete, since we will the ICU XML Files as data.""" def loadLocale(language=None, country=None, variant=None): """Load the locale with the specs that are given by the arguments of @@ -70,32 +79,39 @@ class ILocaleIdentity(Interface): description=u"The language for which a locale is applicable.", constraint=re.compile(r'[a-z]{2}').match, required=True, - readonly=True) + readonly=True, + ) script = TextLine( title=u"Script Type", - description=(u"""The script for which the language/locale is - applicable."""), - constraint=re.compile(r'[a-z]*').match) + description=( + u"""The script for which the language/locale is + applicable.""" + ), + constraint=re.compile(r'[a-z]*').match, + ) territory = TextLine( title=u"Territory Type", description=u"The territory for which a locale is applicable.", constraint=re.compile(r'[A-Z]{2}').match, required=True, - readonly=True) + readonly=True, + ) variant = TextLine( title=u"Variant Type", description=u"The variant for which a locale is applicable.", constraint=re.compile(r'[a-zA-Z]*').match, required=True, - readonly=True) + readonly=True, + ) version = Field( title=u"Locale Version", description=u"The value of this field is an ILocaleVersion object.", - readonly=True) + readonly=True, + ) def __repr__(self): """Defines the representation of the id, which should be a compact @@ -113,18 +129,21 @@ class ILocaleVersion(Interface): description=u"The version number of the locale.", constraint=re.compile(r'^([0-9].)*[0-9]$').match, required=True, - readonly=True) + readonly=True, + ) generationDate = Date( title=u"Generation Date", description=u"Specifies the creation date of the locale.", constraint=lambda date: date < datetime.datetime.now(), - readonly=True) + readonly=True, + ) notes = Text( title=u"Notes", description=u"Some release notes for the version of this locale.", - readonly=True) + readonly=True, + ) class ILocaleDisplayNames(Interface): @@ -138,32 +157,38 @@ class ILocaleDisplayNames(Interface): languages = Dict( title=u"Language type to translated name", key_type=TextLine(title=u"Language Type"), - value_type=TextLine(title=u"Language Name")) + value_type=TextLine(title=u"Language Name"), + ) scripts = Dict( title=u"Script type to script name", key_type=TextLine(title=u"Script Type"), - value_type=TextLine(title=u"Script Name")) + value_type=TextLine(title=u"Script Name"), + ) territories = Dict( title=u"Territory type to translated territory name", key_type=TextLine(title=u"Territory Type"), - value_type=TextLine(title=u"Territory Name")) + value_type=TextLine(title=u"Territory Name"), + ) variants = Dict( title=u"Variant type to name", key_type=TextLine(title=u"Variant Type"), - value_type=TextLine(title=u"Variant Name")) + value_type=TextLine(title=u"Variant Name"), + ) keys = Dict( title=u"Key type to name", key_type=TextLine(title=u"Key Type"), - value_type=TextLine(title=u"Key Name")) + value_type=TextLine(title=u"Key Name"), + ) types = Dict( title=u"Type type and key to localized name", key_type=Tuple(title=u"Type Type and Key"), - value_type=TextLine(title=u"Type Name")) + value_type=TextLine(title=u"Type Name"), + ) class ILocaleTimeZone(Interface): @@ -179,26 +204,32 @@ class ILocaleTimeZone(Interface): title=u"Time Zone Type", description=u"Standard name of the timezone for unique referencing.", required=True, - readonly=True) + readonly=True, + ) cities = List( title=u"Cities", description=u"Cities in Timezone", value_type=TextLine(title=u"City Name"), required=True, - readonly=True) - + readonly=True, + ) names = Dict( title=u"Time Zone Names", description=u"Various names of the timezone.", key_type=Choice( title=u"Time Zone Name Type", - values=(u"generic", u"standard", u"daylight")), - value_type=Tuple(title=u"Time Zone Name and Abbreviation", - min_length=2, max_length=2), + values=(u"generic", u"standard", u"daylight"), + ), + value_type=Tuple( + title=u"Time Zone Name and Abbreviation", + min_length=2, + max_length=2, + ), required=True, - readonly=True) + readonly=True, + ) class ILocaleFormat(Interface): @@ -208,19 +239,22 @@ class ILocaleFormat(Interface): title=u"Format Type", description=u"The name of the format", required=False, - readonly=True) + readonly=True, + ) displayName = TextLine( title=u"Display Name", description=u"Name of the calendar, for example 'gregorian'.", required=False, - readonly=True) + readonly=True, + ) pattern = TextLine( title=u"Format Pattern", description=u"The pattern that is used to format the object.", required=True, - readonly=True) + readonly=True, + ) class ILocaleFormatLength(Interface): @@ -229,12 +263,13 @@ class ILocaleFormatLength(Interface): type = Choice( title=u"Format Length Type", description=u"Name of the format length", - values=(u"full", u"long", u"medium", u"short") - ) + values=(u"full", u"long", u"medium", u"short"), + ) default = TextLine( title=u"Default Format", - description=u"The name of the defaulkt format.") + description=u"The name of the defaulkt format.", + ) formats = Dict( title=u"Formats", @@ -242,9 +277,11 @@ class ILocaleFormatLength(Interface): key_type=TextLine(title=u"Format Type"), value_type=Field( title=u"Format Object", - description=u"Values are ILocaleFormat objects."), + description=u"Values are ILocaleFormat objects.", + ), required=True, - readonly=True) + readonly=True, + ) class ILocaleMonthContext(Interface): @@ -252,23 +289,25 @@ class ILocaleMonthContext(Interface): type = TextLine( title=u"Month context type", - description=u"Name of the month context, format or stand-alone.") + description=u"Name of the month context, format or stand-alone.", + ) - defaultWidth = TextLine( - title=u"Default month name width", - default=u"wide") + defaultWidth = TextLine(title=u"Default month name width", default=u"wide") months = Dict( title=u"Month Names", - description=(u"A mapping of month name widths to a mapping of" - u"corresponding month names."), + description=( + u"A mapping of month name widths to a mapping of" + u"corresponding month names." + ), key_type=Choice( - title=u"Width type", - values=(u"wide", u"abbreviated", u"narrow")), + title=u"Width type", values=(u"wide", u"abbreviated", u"narrow") + ), value_type=Dict( title=u"Month name", key_type=Int(title=u"Type", min=1, max=12), - value_type=TextLine(title=u"Month Name")) + value_type=TextLine(title=u"Month Name"), + ), ) @@ -277,26 +316,36 @@ class ILocaleDayContext(Interface): type = TextLine( title=u"Day context type", - description=u"Name of the day context, format or stand-alone.") + description=u"Name of the day context, format or stand-alone.", + ) - defaultWidth = TextLine( - title=u"Default day name width", - default=u"wide") + defaultWidth = TextLine(title=u"Default day name width", default=u"wide") days = Dict( title=u"Day Names", - description=(u"A mapping of day name widths to a mapping of" - u"corresponding day names."), + description=( + u"A mapping of day name widths to a mapping of" + u"corresponding day names." + ), key_type=Choice( - title=u"Width type", - values=(u"wide", u"abbreviated", u"narrow")), + title=u"Width type", values=(u"wide", u"abbreviated", u"narrow") + ), value_type=Dict( title=u"Day name", key_type=Choice( title=u"Type", - values=(u"sun", u"mon", u"tue", u"wed", - u"thu", u"fri", u"sat")), - value_type=TextLine(title=u"Day Name")) + values=( + u"sun", + u"mon", + u"tue", + u"wed", + u"thu", + u"fri", + u"sat", + ), + ), + value_type=TextLine(title=u"Day Name"), + ), ) @@ -306,56 +355,66 @@ class ILocaleCalendar(Interface): type = TextLine( title=u"Calendar Type", - description=u"Name of the calendar, for example 'gregorian'.") + description=u"Name of the calendar, for example 'gregorian'.", + ) defaultMonthContext = TextLine( - title=u"Default month context", - default=u"format") + title=u"Default month context", default=u"format" + ) monthContexts = Dict( title=u"Month Contexts", - description=(u"A mapping of month context types to " - u"ILocaleMonthContext objects"), - key_type=Choice(title=u"Type", - values=(u"format", u"stand-alone")), - value_type=Field(title=u"ILocaleMonthContext object")) + description=( + u"A mapping of month context types to " + u"ILocaleMonthContext objects" + ), + key_type=Choice(title=u"Type", values=(u"format", u"stand-alone")), + value_type=Field(title=u"ILocaleMonthContext object"), + ) # BBB: leftover from CLDR 1.0 months = Dict( title=u"Month Names", description=u"A mapping of all month names and abbreviations", key_type=Int(title=u"Type", min=1, max=12), - value_type=Tuple(title=u"Month Name and Abbreviation", - min_length=2, max_length=2)) + value_type=Tuple( + title=u"Month Name and Abbreviation", min_length=2, max_length=2 + ), + ) defaultDayContext = TextLine( - title=u"Default day context", - default=u"format") + title=u"Default day context", default=u"format" + ) dayContexts = Dict( title=u"Day Contexts", - description=(u"A mapping of day context types to " - u"ILocaleDayContext objects"), - key_type=Choice(title=u"Type", - values=(u"format", u"stand-alone")), - value_type=Field(title=u"ILocaleDayContext object")) + description=( + u"A mapping of day context types to " u"ILocaleDayContext objects" + ), + key_type=Choice(title=u"Type", values=(u"format", u"stand-alone")), + value_type=Field(title=u"ILocaleDayContext object"), + ) # BBB: leftover from CLDR 1.0 days = Dict( title=u"Weekdays Names", description=u"A mapping of all month names and abbreviations", - key_type=Choice(title=u"Type", - values=(u"sun", u"mon", u"tue", u"wed", - u"thu", u"fri", u"sat")), - value_type=Tuple(title=u"Weekdays Name and Abbreviation", - min_length=2, max_length=2)) + key_type=Choice( + title=u"Type", + values=(u"sun", u"mon", u"tue", u"wed", u"thu", u"fri", u"sat"), + ), + value_type=Tuple( + title=u"Weekdays Name and Abbreviation", min_length=2, max_length=2 + ), + ) week = Dict( title=u"Week Information", description=u"Contains various week information", key_type=Choice( title=u"Type", - description=(u""" + description=( + u""" Varies Week information: - 'minDays' is just an integer between 1 and 7. @@ -364,9 +423,11 @@ class ILocaleCalendar(Interface): - The 'weekendStart' and 'weekendEnd' are tuples of the form (weekDayNumber, datetime.time) - """), - values=(u"minDays", u"firstDay", - u"weekendStart", u"weekendEnd"))) + """ + ), + values=(u"minDays", u"firstDay", u"weekendStart", u"weekendEnd"), + ), + ) am = TextLine(title=u"AM String") @@ -375,8 +436,10 @@ class ILocaleCalendar(Interface): eras = Dict( title=u"Era Names", key_type=Int(title=u"Type", min=0), - value_type=Tuple(title=u"Era Name and Abbreviation", - min_length=2, max_length=2)) + value_type=Tuple( + title=u"Era Name and Abbreviation", min_length=2, max_length=2 + ), + ) defaultDateFormat = TextLine(title=u"Default Date Format Type") @@ -386,8 +449,10 @@ class ILocaleCalendar(Interface): key_type=Choice( title=u"Type", description=u"Name of the format length", - values=(u"full", u"long", u"medium", u"short")), - value_type=Field(title=u"ILocaleFormatLength object")) + values=(u"full", u"long", u"medium", u"short"), + ), + value_type=Field(title=u"ILocaleFormatLength object"), + ) defaultTimeFormat = TextLine(title=u"Default Time Format Type") @@ -397,8 +462,10 @@ class ILocaleCalendar(Interface): key_type=Choice( title=u"Type", description=u"Name of the format length", - values=(u"full", u"long", u"medium", u"short")), - value_type=Field(title=u"ILocaleFormatLength object")) + values=(u"full", u"long", u"medium", u"short"), + ), + value_type=Field(title=u"ILocaleFormatLength object"), + ) defaultDateTimeFormat = TextLine(title=u"Default Date-Time Format Type") @@ -408,8 +475,10 @@ class ILocaleCalendar(Interface): key_type=Choice( title=u"Type", description=u"Name of the format length", - values=(u"full", u"long", u"medium", u"short")), - value_type=Field(title=u"ILocaleFormatLength object")) + values=(u"full", u"long", u"medium", u"short"), + ), + value_type=Field(title=u"ILocaleFormatLength object"), + ) def getMonthNames(): """Return a list of month names.""" @@ -447,27 +516,36 @@ class ILocaleDates(Interface): localizedPatternChars = TextLine( title=u"Localized Pattern Characters", - description=u"Localized pattern characters used in dates and times") + description=u"Localized pattern characters used in dates and times", + ) calendars = Dict( title=u"Calendar type to ILocaleCalendar", key_type=Choice( title=u"Calendar Type", - values=(u"gregorian", - u"arabic", - u"chinese", - u"civil-arabic", - u"hebrew", - u"japanese", - u"thai-buddhist")), - value_type=Field(title=u"Calendar", - description=u"This is a ILocaleCalendar object.")) + values=( + u"gregorian", + u"arabic", + u"chinese", + u"civil-arabic", + u"hebrew", + u"japanese", + u"thai-buddhist", + ), + ), + value_type=Field( + title=u"Calendar", description=u"This is a ILocaleCalendar object." + ), + ) timezones = Dict( title=u"Time zone type to ILocaleTimezone", key_type=TextLine(title=u"Time Zone type"), - value_type=Field(title=u"Time Zone", - description=u"This is a ILocaleTimeZone object.")) + value_type=Field( + title=u"Time Zone", + description=u"This is a ILocaleTimeZone object.", + ), + ) def getFormatter(category, length=None, name=None, calendar=u"gregorian"): """Get a date/time formatter. @@ -491,6 +569,7 @@ class ILocaleCurrency(Interface): symbolChoice = Bool(title=u"Symbol Choice") + class ILocaleNumbers(Interface): """This object contains various data about numbers and currencies.""" @@ -498,11 +577,23 @@ class ILocaleNumbers(Interface): title=u"Number Symbols", key_type=Choice( title=u"Format Name", - values=(u"decimal", u"group", u"list", u"percentSign", - u"nativeZeroDigit", u"patternDigit", u"plusSign", - u"minusSign", u"exponential", u"perMille", - u"infinity", u"nan")), - value_type=TextLine(title=u"Symbol")) + values=( + u"decimal", + u"group", + u"list", + u"percentSign", + u"nativeZeroDigit", + u"patternDigit", + u"plusSign", + u"minusSign", + u"exponential", + u"perMille", + u"infinity", + u"nan", + ), + ), + value_type=TextLine(title=u"Symbol"), + ) defaultDecimalFormat = TextLine(title=u"Default Decimal Format Type") @@ -512,8 +603,10 @@ class ILocaleNumbers(Interface): key_type=Choice( title=u"Type", description=u"Name of the format length", - values=(u"full", u"long", u"medium", u"short")), - value_type=Field(title=u"ILocaleFormatLength object")) + values=(u"full", u"long", u"medium", u"short"), + ), + value_type=Field(title=u"ILocaleFormatLength object"), + ) defaultScientificFormat = TextLine(title=u"Default Scientific Format Type") @@ -523,8 +616,10 @@ class ILocaleNumbers(Interface): key_type=Choice( title=u"Type", description=u"Name of the format length", - values=(u"full", u"long", u"medium", u"short")), - value_type=Field(title=u"ILocaleFormatLength object")) + values=(u"full", u"long", u"medium", u"short"), + ), + value_type=Field(title=u"ILocaleFormatLength object"), + ) defaultPercentFormat = TextLine(title=u"Default Percent Format Type") @@ -534,8 +629,10 @@ class ILocaleNumbers(Interface): key_type=Choice( title=u"Type", description=u"Name of the format length", - values=(u"full", u"long", u"medium", u"short")), - value_type=Field(title=u"ILocaleFormatLength object")) + values=(u"full", u"long", u"medium", u"short"), + ), + value_type=Field(title=u"ILocaleFormatLength object"), + ) defaultCurrencyFormat = TextLine(title=u"Default Currency Format Type") @@ -545,17 +642,19 @@ class ILocaleNumbers(Interface): key_type=Choice( title=u"Type", description=u"Name of the format length", - values=(u"full", u"long", u"medium", u"short")), - value_type=Field(title=u"ILocaleFormatLength object")) + values=(u"full", u"long", u"medium", u"short"), + ), + value_type=Field(title=u"ILocaleFormatLength object"), + ) currencies = Dict( title=u"Currencies", description=u"Contains various Currency data.", key_type=TextLine( - title=u"Type", - description=u"Name of the format length"), - value_type=Field(title=u"ILocaleCurrency object")) - + title=u"Type", description=u"Name of the format length" + ), + value_type=Field(title=u"ILocaleCurrency object"), + ) def getFormatter(category, length=None, name=u""): """Get the NumberFormat based on the category, length and name of the @@ -577,22 +676,30 @@ class ILocaleNumbers(Interface): def getDefaultCurrency(): """Get the default currency.""" -_orientations = [u"left-to-right", u"right-to-left", - u"top-to-bottom", u"bottom-to-top"] + +_orientations = [ + u"left-to-right", + u"right-to-left", + u"top-to-bottom", + u"bottom-to-top", +] + + class ILocaleOrientation(Interface): """Information about the orientation of text.""" characters = Choice( title=u"Orientation of characters", values=_orientations, - default=u"left-to-right" - ) + default=u"left-to-right", + ) lines = Choice( title=u"Orientation of characters", values=_orientations, - default=u"top-to-bottom" - ) + default=u"top-to-bottom", + ) + class ILocale(Interface): """This class contains all important information about the locale. @@ -610,24 +717,31 @@ class ILocale(Interface): title=u"Locale identity", description=u"ILocaleIdentity object identifying the locale.", required=True, - readonly=True) + readonly=True, + ) displayNames = Field( title=u"Display Names", - description=(u"""ILocaleDisplayNames object that contains localized - names.""")) + description=( + u"""ILocaleDisplayNames object that contains localized + names.""" + ), + ) dates = Field( title=u"Dates", - description=u"ILocaleDates object that contains date/time data.") + description=u"ILocaleDates object that contains date/time data.", + ) numbers = Field( title=u"Numbers", - description=u"ILocaleNumbers object that contains number data.") + description=u"ILocaleNumbers object that contains number data.", + ) orientation = Field( title=u"Orientation", - description=u"ILocaleOrientation with text orientation info.") + description=u"ILocaleOrientation with text orientation info.", + ) delimiters = Dict( title=u"Delimiters", @@ -635,11 +749,15 @@ class ILocale(Interface): key_type=Choice( title=u"Delimiter Type", description=u"Delimiter name.", - values=(u"quotationStart", - u"quotationEnd", - u"alternateQuotationStart", - u"alternateQuotationEnd")), - value_type=Field(title=u"Delimiter symbol")) + values=( + u"quotationStart", + u"quotationEnd", + u"alternateQuotationStart", + u"alternateQuotationEnd", + ), + ), + value_type=Field(title=u"Delimiter symbol"), + ) def getLocaleID(): """Return a locale id as specified in the LDML specification""" @@ -657,8 +775,11 @@ class ILocaleInheritance(Interface): __name__ = TextLine( title=u"The name within the parent", - description=(u"""The parent can be traversed with this name to get - the object.""")) + description=( + u"""The parent can be traversed with this name to get + the object.""" + ), + ) def getInheritedSelf(): """Return itself but in the next higher up Locale.""" @@ -699,6 +820,7 @@ class IDictionaryInheritance(ILocaleInheritance): object is consulted. """ + class ICollator(Interface): """Provide support for collating text strings @@ -706,8 +828,7 @@ class ICollator(Interface): """ def key(text): - """Return a collation key for the given text. - """ + """Return a collation key for the given text.""" def cmp(text1, text2): """Compare two text strings. diff --git a/src/zope/i18n/locales/__init__.py b/src/zope/i18n/locales/__init__.py index 030c453..27de94c 100644 --- a/src/zope/i18n/locales/__init__.py +++ b/src/zope/i18n/locales/__init__.py @@ -28,13 +28,17 @@ from zope.i18n.interfaces.locales import ILocaleFormat, ILocaleFormatLength from zope.i18n.interfaces.locales import ILocaleOrientation from zope.i18n.interfaces.locales import ILocaleDayContext, ILocaleMonthContext from zope.i18n.format import NumberFormat, DateTimeFormat -from zope.i18n.locales.inheritance import \ - AttributeInheritance, InheritingDictionary, NoParentException +from zope.i18n.locales.inheritance import ( + AttributeInheritance, + InheritingDictionary, + NoParentException, +) from zope.i18n.locales.provider import LocaleProvider, LoadLocaleError # Setup the locale directory from zope import i18n + LOCALEDIR = os.path.join(os.path.dirname(i18n.__file__), "locales", "data") # Global LocaleProvider. We really just need this single one. @@ -63,15 +67,25 @@ FRIDAY = 5 SATURDAY = 6 SUNDAY = 7 -dayMapping = {'mon': 1, 'tue': 2, 'wed': 3, 'thu': 4, - 'fri': 5, 'sat': 6, 'sun': 7} +dayMapping = { + 'mon': 1, + 'tue': 2, + 'wed': 3, + 'thu': 4, + 'fri': 5, + 'sat': 6, + 'sun': 7, +} BC = 1 AD = 2 -calendarAliases = {'islamic': ('arabic',), - 'islamic-civil': ('civil-arabic',), - 'buddhist': ('thai-buddhist', )} +calendarAliases = { + 'islamic': ('arabic',), + 'islamic-civil': ('civil-arabic',), + 'buddhist': ('thai-buddhist',), +} + @implementer(ILocaleIdentity) class LocaleIdentity(object): @@ -102,7 +116,9 @@ class LocaleIdentity(object): <LocaleIdentity (en, None, US, POSIX)> """ - def __init__(self, language=None, script=None, territory=None, variant=None): + def __init__( + self, language=None, script=None, territory=None, variant=None + ): """Initialize object.""" self.language = language self.script = script @@ -110,10 +126,13 @@ class LocaleIdentity(object): self.variant = variant def __repr__(self): - """See zope.i18n.interfaces.ILocaleIdentity - """ - return "<LocaleIdentity (%s, %s, %s, %s)>" %( - self.language, self.script, self.territory, self.variant) + """See zope.i18n.interfaces.ILocaleIdentity""" + return "<LocaleIdentity (%s, %s, %s, %s)>" % ( + self.language, + self.script, + self.territory, + self.variant, + ) @implementer(ILocaleVersion) @@ -151,12 +170,17 @@ class LocaleVersion(object): self.notes = notes def __lt__(self, other): - return ((self.generationDate, self.number) < - (other.generationDate, other.number)) + return (self.generationDate, self.number) < ( + other.generationDate, + other.number, + ) def __eq__(self, other): - return ((self.generationDate, self.number) == - (other.generationDate, other.number)) + return (self.generationDate, self.number) == ( + other.generationDate, + other.number, + ) + @implementer(ILocaleDisplayNames) class LocaleDisplayNames(AttributeInheritance): @@ -233,7 +257,6 @@ class LocaleFormatLength(AttributeInheritance): """Specifies one of the format lengths of a specific quantity, like numbers, dates, times and datetimes.""" - def __init__(self, type=None): """Initialize the object.""" self.type = type @@ -242,7 +265,6 @@ class LocaleFormatLength(AttributeInheritance): @implementer(ILocaleMonthContext) class LocaleMonthContext(AttributeInheritance): - def __init__(self, type=None): """Initialize the object.""" self.type = type @@ -251,7 +273,6 @@ class LocaleMonthContext(AttributeInheritance): @implementer(ILocaleDayContext) class LocaleDayContext(AttributeInheritance): - def __init__(self, type=None): """Initialize the object.""" self.type = type @@ -343,7 +364,9 @@ class LocaleCalendar(AttributeInheritance): def getMonthNames(self): """See zope.i18n.interfaces.ILocaleCalendar""" - return [self.months.get(type, (None, None))[0] for type in range(1, 13)] + return [ + self.months.get(type, (None, None))[0] for type in range(1, 13) + ] def getMonthTypeFromName(self, name): """See zope.i18n.interfaces.ILocaleCalendar""" @@ -353,7 +376,9 @@ class LocaleCalendar(AttributeInheritance): def getMonthAbbreviations(self): """See zope.i18n.interfaces.ILocaleCalendar""" - return [self.months.get(type, (None, None))[1] for type in range(1, 13)] + return [ + self.months.get(type, (None, None))[1] for type in range(1, 13) + ] def getMonthTypeFromAbbreviation(self, abbr): """See zope.i18n.interfaces.ILocaleCalendar""" @@ -491,26 +516,34 @@ class LocaleDates(AttributeInheritance): """ - def getFormatter(self, category, length=None, name=None, - calendar=u"gregorian"): + def getFormatter( + self, category, length=None, name=None, calendar=u"gregorian" + ): """See zope.i18n.interfaces.locales.ILocaleDates""" if category not in (u"date", u"time", u"dateTime"): raise ValueError('Invalid category: %s' % category) - if calendar not in (u"gregorian", u"arabic", u"chinese", - u"civil-arabic", u"hebrew", u"japanese", - u"thai-buddhist"): + if calendar not in ( + u"gregorian", + u"arabic", + u"chinese", + u"civil-arabic", + u"hebrew", + u"japanese", + u"thai-buddhist", + ): raise ValueError('Invalid calendar: %s' % calendar) if length not in (u"short", u"medium", u"long", u"full", None): raise ValueError('Invalid format length: %s' % length) cal = self.calendars[calendar] - formats = getattr(cal, category+'Formats') + formats = getattr(cal, category + 'Formats') if length is None: length = getattr( cal, - 'default'+category[0].upper()+category[1:]+'Format', - list(formats.keys())[0]) + 'default' + category[0].upper() + category[1:] + 'Format', + list(formats.keys())[0], + ) # 'datetime' is always a bit special; we often do not have a length # specification, but we need it for looking up the date and time @@ -528,9 +561,11 @@ class LocaleDates(AttributeInheritance): if category == 'dateTime': date_pat = self.getFormatter( - 'date', length, name, calendar).getPattern() + 'date', length, name, calendar + ).getPattern() time_pat = self.getFormatter( - 'time', length, name, calendar).getPattern() + 'time', length, name, calendar + ).getPattern() pattern = pattern.replace('{1}', date_pat) pattern = pattern.replace('{0}', time_pat) @@ -636,7 +671,8 @@ class LocaleNumbers(AttributeInheritance): length = getattr( self, 'default' + category[0].upper() + category[1:] + 'Format', - list(formats.keys())[0]) + list(formats.keys())[0], + ) formatLength = formats[length] if name is None: @@ -649,8 +685,8 @@ class LocaleNumbers(AttributeInheritance): @implementer(ILocaleOrientation) class LocaleOrientation(AttributeInheritance): - """Implementation of ILocaleOrientation - """ + """Implementation of ILocaleOrientation""" + @implementer(ILocale) class Locale(AttributeInheritance): @@ -682,15 +718,15 @@ class Locale(AttributeInheritance): """ id = self.id - pieces = [x for x in - (id.language, id.script, id.territory, id.variant) - if x] + pieces = [ + x for x in (id.language, id.script, id.territory, id.variant) if x + ] id_string = '_'.join(pieces) # TODO: What about keys??? Where do I get this info from? # Notice that 'pieces' is always empty. pieces = [key + '=' + type for (key, type) in ()] assert not pieces - if pieces: # pragma: no cover + if pieces: # pragma: no cover id_string += '@' + ','.join(pieces) return id_string diff --git a/src/zope/i18n/locales/fallbackcollator.py b/src/zope/i18n/locales/fallbackcollator.py index fc76a58..f747b34 100644 --- a/src/zope/i18n/locales/fallbackcollator.py +++ b/src/zope/i18n/locales/fallbackcollator.py @@ -16,8 +16,8 @@ from unicodedata import normalize -class FallbackCollator: +class FallbackCollator: def __init__(self, locale): pass diff --git a/src/zope/i18n/locales/inheritance.py b/src/zope/i18n/locales/inheritance.py index 912d8c9..5cfe7e8 100644 --- a/src/zope/i18n/locales/inheritance.py +++ b/src/zope/i18n/locales/inheritance.py @@ -23,12 +23,17 @@ __docformat__ = 'restructuredtext' from zope.deprecation import deprecate from zope.interface import implementer -from zope.i18n.interfaces.locales import \ - ILocaleInheritance, IAttributeInheritance, IDictionaryInheritance +from zope.i18n.interfaces.locales import ( + ILocaleInheritance, + IAttributeInheritance, + IDictionaryInheritance, +) + class NoParentException(AttributeError): pass + @implementer(ILocaleInheritance) class Inheritance(object): """A simple base version of locale inheritance. @@ -37,7 +42,6 @@ class Inheritance(object): 'ILocaleInheritance' implementations. """ - # See zope.i18n.interfaces.locales.ILocaleInheritance __parent__ = None @@ -100,18 +104,15 @@ class AttributeInheritance(Inheritance): True """ - def __setattr__(self, name, value): """See zope.i18n.interfaces.locales.ILocaleInheritance""" # If we have a value that can also inherit data from other locales, we # set its parent and name, so that we know how to get to it. - if (ILocaleInheritance.providedBy(value) and - not name.startswith('__')): + if ILocaleInheritance.providedBy(value) and not name.startswith('__'): value.__parent__ = self value.__name__ = name super(AttributeInheritance, self).__setattr__(name, value) - def __getattr__(self, name): """See zope.i18n.interfaces.locales.ILocaleInheritance""" try: @@ -119,9 +120,10 @@ class AttributeInheritance(Inheritance): except NoParentException: # There was simply no parent anymore, so let's raise an error # for good - raise AttributeError("'%s' object (or any of its parents) has no " - "attribute '%s'" % (self.__class__.__name__, - name)) + raise AttributeError( + "'%s' object (or any of its parents) has no " + "attribute '%s'" % (self.__class__.__name__, name) + ) else: value = getattr(selfUp, name) # Since a locale hierarchy never changes after startup, we can @@ -134,7 +136,6 @@ class AttributeInheritance(Inheritance): return value - @implementer(IDictionaryInheritance) class InheritingDictionary(Inheritance, dict): """Implementation of a dictionary that can also inherit values. @@ -197,7 +198,6 @@ class InheritingDictionary(Inheritance, dict): `value` is a deprecated synonym for `values` """ - def __setitem__(self, name, value): """See zope.i18n.interfaces.locales.ILocaleInheritance""" if ILocaleInheritance.providedBy(value): diff --git a/src/zope/i18n/locales/provider.py b/src/zope/i18n/locales/provider.py index 3864b7a..5ed415f 100644 --- a/src/zope/i18n/locales/provider.py +++ b/src/zope/i18n/locales/provider.py @@ -20,6 +20,7 @@ import os from zope.interface import implementer from zope.i18n.interfaces.locales import ILocaleProvider + class LoadLocaleError(Exception): """This error is raised if a locale cannot be loaded.""" @@ -28,7 +29,6 @@ class LoadLocaleError(Exception): class LocaleProvider(object): """A locale provider that gets its data from the XML data.""" - def __init__(self, locale_dir): self._locales = {} self._locale_dir = locale_dir @@ -55,7 +55,8 @@ class LocaleProvider(object): path = os.path.join(self._locale_dir, filename) if not os.path.exists(path): raise LoadLocaleError( - 'The desired locale is not available.\nPath: %s' % path) + 'The desired locale is not available.\nPath: %s' % path + ) # Import here to avoid circular imports from zope.i18n.locales.xmlfactory import LocaleFactory diff --git a/src/zope/i18n/locales/tests/test_docstrings.py b/src/zope/i18n/locales/tests/test_docstrings.py index fa52078..c40b691 100644 --- a/src/zope/i18n/locales/tests/test_docstrings.py +++ b/src/zope/i18n/locales/tests/test_docstrings.py @@ -20,8 +20,8 @@ from zope.i18n.locales.inheritance import NoParentException from zope.i18n.testing import unicode_checker -class LocaleInheritanceStub(AttributeInheritance): +class LocaleInheritanceStub(AttributeInheritance): def __init__(self, nextLocale=None): self.__nextLocale__ = nextLocale @@ -32,11 +32,18 @@ class LocaleInheritanceStub(AttributeInheritance): def test_suite(): - return unittest.TestSuite(( - DocTestSuite('zope.i18n.locales', checker=unicode_checker), - DocTestSuite('zope.i18n.locales.inheritance', checker=unicode_checker), - DocTestSuite('zope.i18n.locales.xmlfactory', checker=unicode_checker), - )) + return unittest.TestSuite( + ( + DocTestSuite('zope.i18n.locales', checker=unicode_checker), + DocTestSuite( + 'zope.i18n.locales.inheritance', checker=unicode_checker + ), + DocTestSuite( + 'zope.i18n.locales.xmlfactory', checker=unicode_checker + ), + ) + ) + if __name__ == '__main__': unittest.main() diff --git a/src/zope/i18n/locales/tests/test_fallbackcollator.py b/src/zope/i18n/locales/tests/test_fallbackcollator.py index 7e10b50..4ecf34d 100644 --- a/src/zope/i18n/locales/tests/test_fallbackcollator.py +++ b/src/zope/i18n/locales/tests/test_fallbackcollator.py @@ -17,11 +17,16 @@ import doctest from zope.i18n.testing import unicode_checker + def test_suite(): - return unittest.TestSuite(( - doctest.DocFileSuite('../fallbackcollator.txt', checker=unicode_checker), - )) + return unittest.TestSuite( + ( + doctest.DocFileSuite( + '../fallbackcollator.txt', checker=unicode_checker + ), + ) + ) + if __name__ == '__main__': unittest.main(defaultTest='test_suite') - diff --git a/src/zope/i18n/locales/tests/test_locales.py b/src/zope/i18n/locales/tests/test_locales.py index 2b100b8..58cecda 100644 --- a/src/zope/i18n/locales/tests/test_locales.py +++ b/src/zope/i18n/locales/tests/test_locales.py @@ -22,8 +22,10 @@ from zope.i18n.locales import locales from zope.i18n.locales.provider import LocaleProvider, LoadLocaleError import zope.i18n + datadir = os.path.join(os.path.dirname(zope.i18n.__file__), 'locales', 'data') + class AbstractTestILocaleProviderMixin(object): """Test the functionality of an implmentation of the ILocaleProvider interface.""" @@ -60,14 +62,14 @@ class AbstractTestILocaleProviderMixin(object): class TestLocaleProvider(AbstractTestILocaleProviderMixin, TestCase): - def _makeNewProvider(self): return LocaleProvider(datadir) def test_loadLocale(self): self.locales.loadLocale(None, None, None) - self.assertEqual(list(self.locales._locales.keys()), - [(None, None, None)]) + self.assertEqual( + list(self.locales._locales.keys()), [(None, None, None)] + ) self.locales.loadLocale('en', None, None) self.assertIn(('en', None, None), self.locales._locales.keys()) @@ -94,27 +96,34 @@ class TestLocaleAndProvider(TestCase): def test_getTimeFormatter(self): formatter = self.locale.dates.getFormatter('time', 'medium') self.assertEqual(formatter.getPattern(), 'h:mm:ss a') - self.assertEqual(formatter.format(datetime.time(12, 30, 10)), - '12:30:10 PM') - self.assertEqual(formatter.parse('12:30:10 PM'), - datetime.time(12, 30, 10)) + self.assertEqual( + formatter.format(datetime.time(12, 30, 10)), '12:30:10 PM' + ) + self.assertEqual( + formatter.parse('12:30:10 PM'), datetime.time(12, 30, 10) + ) def test_getDateFormatter(self): formatter = self.locale.dates.getFormatter('date', 'medium') self.assertEqual(formatter.getPattern(), 'MMM d, yyyy') - self.assertEqual(formatter.format(datetime.date(2003, 1, 2)), - 'Jan 2, 2003') - self.assertEqual(formatter.parse('Jan 2, 2003'), - datetime.date(2003, 1, 2)) + self.assertEqual( + formatter.format(datetime.date(2003, 1, 2)), 'Jan 2, 2003' + ) + self.assertEqual( + formatter.parse('Jan 2, 2003'), datetime.date(2003, 1, 2) + ) def test_getDateTimeFormatter(self): formatter = self.locale.dates.getFormatter('dateTime', 'medium') self.assertEqual(formatter.getPattern(), 'MMM d, yyyy h:mm:ss a') self.assertEqual( formatter.format(datetime.datetime(2003, 1, 2, 12, 30)), - 'Jan 2, 2003 12:30:00 PM') - self.assertEqual(formatter.parse('Jan 2, 2003 12:30:00 PM'), - datetime.datetime(2003, 1, 2, 12, 30)) + 'Jan 2, 2003 12:30:00 PM', + ) + self.assertEqual( + formatter.parse('Jan 2, 2003 12:30:00 PM'), + datetime.datetime(2003, 1, 2, 12, 30), + ) def test_getNumberFormatter(self): formatter = self.locale.numbers.getFormatter('decimal') @@ -126,7 +135,6 @@ class TestLocaleAndProvider(TestCase): class TestGlobalLocaleProvider(TestCase): - def testLoading(self): locales.loadLocale(None, None, None) self.assertIn((None, None, None), locales._locales) @@ -143,6 +151,7 @@ class TestGlobalLocaleProvider(TestCase): self.assertEqual(locale.id.territory, 'GB') self.assertEqual(locale.id.variant, None) + class TestRootLocale(TestCase): """There were some complaints that the root locale does not work correctly, so make sure it does.""" @@ -153,10 +162,14 @@ class TestRootLocale(TestCase): def test_dateFormatter(self): formatter = self.locale.dates.getFormatter('date') self.assertEqual( - formatter.format(datetime.date(2004, 10, 31), 'E'), '1') + formatter.format(datetime.date(2004, 10, 31), 'E'), '1' + ) self.assertEqual( - formatter.format(datetime.date(2004, 10, 31), 'EE'), '01') + formatter.format(datetime.date(2004, 10, 31), 'EE'), '01' + ) self.assertEqual( - formatter.format(datetime.date(2004, 10, 31), 'EEE'), '1') + formatter.format(datetime.date(2004, 10, 31), 'EEE'), '1' + ) self.assertEqual( - formatter.format(datetime.date(2004, 10, 31), 'EEEE'), '1') + formatter.format(datetime.date(2004, 10, 31), 'EEEE'), '1' + ) diff --git a/src/zope/i18n/locales/tests/test_xmlfactory.py b/src/zope/i18n/locales/tests/test_xmlfactory.py index 792c96d..b1c09bd 100644 --- a/src/zope/i18n/locales/tests/test_xmlfactory.py +++ b/src/zope/i18n/locales/tests/test_xmlfactory.py @@ -19,6 +19,7 @@ from unittest import TestCase, TestSuite from zope.i18n.locales.xmlfactory import LocaleFactory import zope.i18n + class LocaleXMLFileTestCase(TestCase): """This test verifies that every locale XML file can be loaded.""" @@ -34,13 +35,13 @@ class LocaleXMLFileTestCase(TestCase): # necessary for the xml files to have all format definitions. ## Making sure all number format patterns parse - #for category in (u'decimal', u'scientific', u'percent', u'currency'): + # for category in (u'decimal', u'scientific', u'percent', u'currency'): # for length in getattr(locale.numbers, category+'Formats').values(): # for format in length.formats.values(): # self.assert_(parseNumberPattern(format.pattern) is not None) ## Making sure all datetime patterns parse - #for calendar in locale.dates.calendars.values(): + # for calendar in locale.dates.calendars.values(): # for category in ('date', 'time', 'dateTime'): # for length in getattr(calendar, category+'Formats').values(): # for format in length.formats.values(): @@ -48,11 +49,11 @@ class LocaleXMLFileTestCase(TestCase): # parseDateTimePattern(format.pattern) is not None) - def test_suite(): suite = TestSuite() - locale_dir = os.path.join(os.path.dirname(zope.i18n.__file__), - 'locales', 'data') + locale_dir = os.path.join( + os.path.dirname(zope.i18n.__file__), 'locales', 'data' + ) for path in os.listdir(locale_dir): if not path.endswith(".xml"): continue diff --git a/src/zope/i18n/locales/xmlfactory.py b/src/zope/i18n/locales/xmlfactory.py index 5b2dbf5..244d17e 100644 --- a/src/zope/i18n/locales/xmlfactory.py +++ b/src/zope/i18n/locales/xmlfactory.py @@ -41,7 +41,6 @@ class LocaleFactory(object): rc = rc + node.data return rc - def _extractVersion(self, identity_node): """Extract the Locale's version info based on data from the DOM tree. @@ -81,7 +80,6 @@ class LocaleFactory(object): return LocaleVersion(number, generationDate, notes) - def _extractIdentity(self): """Extract the Locale's identity object based on info from the DOM tree. @@ -119,7 +117,7 @@ class LocaleFactory(object): # Retrieve the language of the locale nodes = identity.getElementsByTagName('language') if nodes != []: - id.language = nodes[0].getAttribute('type') or None + id.language = nodes[0].getAttribute('type') or None # Retrieve the territory of the locale nodes = identity.getElementsByTagName('territory') if nodes != []: @@ -132,7 +130,6 @@ class LocaleFactory(object): id.version = self._extractVersion(identity) return id - def _extractTypes(self, names_node): """Extract all types from the names_node. @@ -179,7 +176,6 @@ class LocaleFactory(object): types[(type, key)] = self._getText(type_node.childNodes) return types - def _extractDisplayNames(self): """Extract all display names from the DOM tree. @@ -265,11 +261,13 @@ class LocaleFactory(object): if names_nodes == []: return displayNames - for group_tag, single_tag in (('languages', 'language'), - ('scripts', 'script'), - ('territories', 'territory'), - ('variants', 'variant'), - ('keys', 'key')): + for group_tag, single_tag in ( + ('languages', 'language'), + ('scripts', 'script'), + ('territories', 'territory'), + ('variants', 'variant'), + ('keys', 'key'), + ): group_nodes = names_nodes[0].getElementsByTagName(group_tag) if group_nodes == []: continue @@ -285,7 +283,6 @@ class LocaleFactory(object): displayNames.types = types return displayNames - def _extractMonths(self, months_node, calendar): """Extract all month entries from cal_node and store them in calendar. @@ -385,14 +382,16 @@ class LocaleFactory(object): defaultMonthContext_node = months_node.getElementsByTagName('default') if defaultMonthContext_node: - calendar.defaultMonthContext = defaultMonthContext_node[0].getAttribute('type') + calendar.defaultMonthContext = defaultMonthContext_node[ + 0 + ].getAttribute('type') monthContext_nodes = months_node.getElementsByTagName('monthContext') if not monthContext_nodes: return calendar.monthContexts = InheritingDictionary() - names_node = abbrs_node = None # BBB + names_node = abbrs_node = None # BBB for node in monthContext_nodes: context_type = node.getAttribute('type') @@ -438,9 +437,10 @@ class LocaleFactory(object): # Put the info together calendar.months = InheritingDictionary() for type in range(1, 13): - calendar.months[type] = (names.get(type, None), - abbrs.get(type, None)) - + calendar.months[type] = ( + names.get(type, None), + abbrs.get(type, None), + ) def _extractDays(self, days_node, calendar): """Extract all day entries from cal_node and store them in @@ -529,14 +529,16 @@ class LocaleFactory(object): defaultDayContext_node = days_node.getElementsByTagName('default') if defaultDayContext_node: - calendar.defaultDayContext = defaultDayContext_node[0].getAttribute('type') + calendar.defaultDayContext = defaultDayContext_node[ + 0 + ].getAttribute('type') dayContext_nodes = days_node.getElementsByTagName('dayContext') if not dayContext_nodes: return calendar.dayContexts = InheritingDictionary() - names_node = abbrs_node = None # BBB + names_node = abbrs_node = None # BBB for node in dayContext_nodes: context_type = node.getAttribute('type') @@ -581,9 +583,10 @@ class LocaleFactory(object): # Put the info together calendar.days = InheritingDictionary() for type in range(1, 13): - calendar.days[type] = (names.get(type, None), - abbrs.get(type, None)) - + calendar.days[type] = ( + names.get(type, None), + abbrs.get(type, None), + ) def _extractWeek(self, cal_node, calendar): """Extract all week entries from cal_node and store them in @@ -644,7 +647,6 @@ class LocaleFactory(object): time_args = map(int, node.getAttribute('time').split(':')) calendar.week['weekendEnd'] = (day, time(*time_args)) - def _extractEras(self, cal_node, calendar): """Extract all era entries from cal_node and store them in calendar. @@ -703,8 +705,10 @@ class LocaleFactory(object): calendar.eras = InheritingDictionary() for type in abbrs.keys(): - calendar.eras[type] = (names.get(type, None), abbrs.get(type, None)) - + calendar.eras[type] = ( + names.get(type, None), + abbrs.get(type, None), + ) def _extractFormats(self, formats_node, lengthNodeName, formatNodeName): """Extract all format entries from formats_node and return a @@ -765,14 +769,18 @@ class LocaleFactory(object): if length_node.getElementsByTagName(formatNodeName): length.formats = InheritingDictionary() - for format_node in length_node.getElementsByTagName(formatNodeName): + for format_node in length_node.getElementsByTagName( + formatNodeName + ): format = LocaleFormat() format.type = format_node.getAttribute('type') or None pattern_node = format_node.getElementsByTagName('pattern')[0] format.pattern = self._getText(pattern_node.childNodes) name_nodes = format_node.getElementsByTagName('displayName') if name_nodes: - format.displayName = self._getText(name_nodes[0].childNodes) + format.displayName = self._getText( + name_nodes[0].childNodes + ) length.formats[format.type] = format lengths[length.type] = length @@ -905,17 +913,21 @@ class LocaleFactory(object): self._extractEras(cal_node, calendar) for formatsName, lengthName, formatName in ( - ('dateFormats', 'dateFormatLength', 'dateFormat'), - ('timeFormats', 'timeFormatLength', 'timeFormat'), - ('dateTimeFormats', 'dateTimeFormatLength', 'dateTimeFormat')): + ('dateFormats', 'dateFormatLength', 'dateFormat'), + ('timeFormats', 'timeFormatLength', 'timeFormat'), + ('dateTimeFormats', 'dateTimeFormatLength', 'dateTimeFormat'), + ): formats_nodes = cal_node.getElementsByTagName(formatsName) if formats_nodes: default, formats = self._extractFormats( - formats_nodes[0], lengthName, formatName) - setattr(calendar, - 'default'+formatName[0].upper()+formatName[1:], - default) + formats_nodes[0], lengthName, formatName + ) + setattr( + calendar, + 'default' + formatName[0].upper() + formatName[1:], + default, + ) setattr(calendar, formatsName, formats) calendars[calendar.type] = calendar @@ -925,7 +937,6 @@ class LocaleFactory(object): return calendars - def _extractTimeZones(self, dates_node): """Extract all timezone information for the locale from the DOM tree. @@ -1009,7 +1020,6 @@ class LocaleFactory(object): return zones - def _extractDates(self): """Extract all date information from the DOM tree""" dates_nodes = self._data.getElementsByTagName('dates') @@ -1025,7 +1035,6 @@ class LocaleFactory(object): dates.timezones = timezones return dates - def _extractSymbols(self, numbers_node): """Extract all week entries from cal_node and store them in calendar. @@ -1071,17 +1080,26 @@ class LocaleFactory(object): return symbols = InheritingDictionary() - for name in (u"decimal", u"group", u"list", u"percentSign", - u"nativeZeroDigit", u"patternDigit", u"plusSign", - u"minusSign", u"exponential", u"perMille", - u"infinity", u"nan"): + for name in ( + u"decimal", + u"group", + u"list", + u"percentSign", + u"nativeZeroDigit", + u"patternDigit", + u"plusSign", + u"minusSign", + u"exponential", + u"perMille", + u"infinity", + u"nan", + ): nodes = symbols_nodes[0].getElementsByTagName(name) if nodes: symbols[name] = self._getText(nodes[0].childNodes) return symbols - def _extractNumberFormats(self, numbers_node, numbers): """Extract all number formats from the numbers_node and save the data in numbers. @@ -1162,19 +1180,19 @@ class LocaleFactory(object): """ for category in ('decimal', 'scientific', 'percent', 'currency'): - formatsName = category+'Formats' - lengthName = category+'FormatLength' - formatName = category+'Format' - defaultName = 'default'+formatName[0].upper()+formatName[1:] + formatsName = category + 'Formats' + lengthName = category + 'FormatLength' + formatName = category + 'Format' + defaultName = 'default' + formatName[0].upper() + formatName[1:] formats_nodes = numbers_node.getElementsByTagName(formatsName) if formats_nodes: default, formats = self._extractFormats( - formats_nodes[0], lengthName, formatName) + formats_nodes[0], lengthName, formatName + ) setattr(numbers, defaultName, default) setattr(numbers, formatsName, formats) - def _extractCurrencies(self, numbers_node): """Extract all currency definitions and their information from the Locale's DOM tree. @@ -1231,8 +1249,9 @@ class LocaleFactory(object): nodes = curr_node.getElementsByTagName('symbol') if nodes: currency.symbol = self._getText(nodes[0].childNodes) - currency.symbolChoice = \ - nodes[0].getAttribute('choice') == u"true" + currency.symbolChoice = ( + nodes[0].getAttribute('choice') == u"true" + ) nodes = curr_node.getElementsByTagName('displayName') if nodes: @@ -1242,7 +1261,6 @@ class LocaleFactory(object): return currencies - def _extractNumbers(self): """Extract all number information from the DOM tree""" numbers_nodes = self._data.getElementsByTagName('numbers') @@ -1259,7 +1277,6 @@ class LocaleFactory(object): numbers.currencies = currencies return numbers - def _extractDelimiters(self): """Extract all delimiter entries from the DOM tree. @@ -1307,33 +1324,36 @@ class LocaleFactory(object): return delimiters = InheritingDictionary() - for name in (u'quotationStart', u"quotationEnd", - u"alternateQuotationStart", u"alternateQuotationEnd"): + for name in ( + u'quotationStart', + u"quotationEnd", + u"alternateQuotationStart", + u"alternateQuotationEnd", + ): nodes = delimiters_nodes[0].getElementsByTagName(name) if nodes: delimiters[name] = self._getText(nodes[0].childNodes) return delimiters - def _extractOrientation(self): """Extract orientation information. - >>> factory = LocaleFactory(None) - >>> from xml.dom.minidom import parseString - >>> xml = u''' - ... <ldml> - ... <layout> - ... <orientation lines="bottom-to-top" characters="right-to-left" /> - ... </layout> - ... </ldml>''' - >>> dom = parseString(xml) - >>> factory._data = parseString(xml).documentElement - >>> orientation = factory._extractOrientation() - >>> orientation.lines - u'bottom-to-top' - >>> orientation.characters - u'right-to-left' + >>> factory = LocaleFactory(None) + >>> from xml.dom.minidom import parseString + >>> xml = u''' + ... <ldml> + ... <layout> + ... <orientation lines="bottom-to-top" characters="right-to-left" /> + ... </layout> + ... </ldml>''' + >>> dom = parseString(xml) + >>> factory._data = parseString(xml).documentElement + >>> orientation = factory._extractOrientation() + >>> orientation.lines + u'bottom-to-top' + >>> orientation.characters + u'right-to-left' """ orientation_nodes = self._data.getElementsByTagName('orientation') if not orientation_nodes: @@ -1345,7 +1365,6 @@ class LocaleFactory(object): setattr(orientation, name, value) return orientation - def __call__(self): """Create the Locale.""" locale = Locale(self._extractIdentity()) diff --git a/src/zope/i18n/negotiator.py b/src/zope/i18n/negotiator.py index 3c5fa17..8fa0609 100644 --- a/src/zope/i18n/negotiator.py +++ b/src/zope/i18n/negotiator.py @@ -36,7 +36,6 @@ def normalize_langs(langs): @implementer(INegotiator) class Negotiator(object): - def getLanguage(self, langs, env): envadapter = IUserPreferredLanguages(env) userlangs = envadapter.getPreferredLanguages() diff --git a/src/zope/i18n/simpletranslationdomain.py b/src/zope/i18n/simpletranslationdomain.py index b093db8..889d6db 100644 --- a/src/zope/i18n/simpletranslationdomain.py +++ b/src/zope/i18n/simpletranslationdomain.py @@ -25,15 +25,15 @@ text_type = str if bytes is not str else unicode @implementer(ITranslationDomain) class SimpleTranslationDomain(object): """This is the simplest implementation of the ITranslationDomain I - could come up with. + could come up with. - The constructor takes one optional argument 'messages', which will be - used to do the translation. The 'messages' attribute has to have the - following structure: + The constructor takes one optional argument 'messages', which will be + used to do the translation. The 'messages' attribute has to have the + following structure: - {('language', 'msg_id'): 'message', ...} + {('language', 'msg_id'): 'message', ...} - Note: This Translation Domain does not use message catalogs. + Note: This Translation Domain does not use message catalogs. """ # See zope.i18n.interfaces.ITranslationDomain @@ -42,13 +42,22 @@ class SimpleTranslationDomain(object): def __init__(self, domain, messages=None): """Initializes the object. No arguments are needed.""" self.domain = ( - domain.decode("utf-8") if isinstance(domain, bytes) else domain) + domain.decode("utf-8") if isinstance(domain, bytes) else domain + ) self.messages = messages if messages is not None else {} assert self.messages is not None - def translate(self, msgid, mapping=None, context=None, - target_language=None, default=None, msgid_plural=None, - default_plural=None, number=None): + def translate( + self, + msgid, + mapping=None, + context=None, + target_language=None, + default=None, + msgid_plural=None, + default_plural=None, + number=None, + ): '''See interface ITranslationDomain''' # Find out what the target language should be if target_language is None and context is not None: diff --git a/src/zope/i18n/testing.py b/src/zope/i18n/testing.py index 035827c..8fd1c04 100644 --- a/src/zope/i18n/testing.py +++ b/src/zope/i18n/testing.py @@ -34,12 +34,12 @@ def setUp(test=None): import zope.component from zope.publisher.browser import BrowserLanguages from zope.publisher.http import HTTPCharsets + zope.component.provideAdapter(HTTPCharsets) zope.component.provideAdapter(BrowserLanguages) class PlacelessSetup(object): - def setUp(self): """ Install the language and charset negotiators. diff --git a/src/zope/i18n/testmessagecatalog.py b/src/zope/i18n/testmessagecatalog.py index 8420c58..a06b15a 100644 --- a/src/zope/i18n/testmessagecatalog.py +++ b/src/zope/i18n/testmessagecatalog.py @@ -18,6 +18,7 @@ from zope import interface import zope.i18n.interfaces from zope.i18n.translationdomain import TranslationDomain + @interface.implementer(zope.i18n.interfaces.IGlobalMessageCatalog) class TestMessageCatalog(object): @@ -43,13 +44,15 @@ class TestMessageCatalog(object): def reload(self): pass + @interface.implementer(zope.i18n.interfaces.ITranslationDomain) def TestMessageFallbackDomain(domain_id=u""): domain = TranslationDomain(domain_id) domain.addCatalog(TestMessageCatalog(domain_id)) return domain + interface.directlyProvides( TestMessageFallbackDomain, zope.i18n.interfaces.IFallbackTranslationDomainFactory, - ) +) diff --git a/src/zope/i18n/tests/test.py b/src/zope/i18n/tests/test.py index 4fb4f4c..64fdbeb 100644 --- a/src/zope/i18n/tests/test.py +++ b/src/zope/i18n/tests/test.py @@ -22,18 +22,23 @@ from zope.i18n.testing import unicode_checker def test_suite(): options = doctest.NORMALIZE_WHITESPACE | doctest.ELLIPSIS + def suite(name): return doctest.DocTestSuite( name, - setUp=setUp, tearDown=tearDown, + setUp=setUp, + tearDown=tearDown, optionflags=options, - checker=unicode_checker) + checker=unicode_checker, + ) - return unittest.TestSuite([ - suite('zope.i18n'), - suite("zope.i18n.config"), - suite("zope.i18n.testing"), - ]) + return unittest.TestSuite( + [ + suite('zope.i18n'), + suite("zope.i18n.config"), + suite("zope.i18n.testing"), + ] + ) if __name__ == '__main__': diff --git a/src/zope/i18n/tests/test_compile.py b/src/zope/i18n/tests/test_compile.py index d82c3e7..214c240 100644 --- a/src/zope/i18n/tests/test_compile.py +++ b/src/zope/i18n/tests/test_compile.py @@ -19,10 +19,8 @@ from zope.testing.loggingsupport import InstalledHandler from zope.i18n import compile -@unittest.skipUnless(compile.HAS_PYTHON_GETTEXT, - "Need python-gettext") +@unittest.skipUnless(compile.HAS_PYTHON_GETTEXT, "Need python-gettext") class TestCompile(unittest.TestCase): - def setUp(self): self.handler = InstalledHandler('zope.i18n') self.addCleanup(self.handler.uninstall) @@ -43,8 +41,7 @@ class TestCompile(unittest.TestCase): compile.compile_mo_file('foo', td) - self.assertIn("Syntax error while compiling", - str(self.handler)) + self.assertIn("Syntax error while compiling", str(self.handler)) def test_po_exists_cannot_write_mo(self): import tempfile @@ -68,5 +65,4 @@ class TestCompile(unittest.TestCase): compile.compile_mo_file('foo', td) - self.assertIn("Error while compiling", - str(self.handler)) + self.assertIn("Error while compiling", str(self.handler)) diff --git a/src/zope/i18n/tests/test_formats.py b/src/zope/i18n/tests/test_formats.py index 83c9129..29de8c9 100644 --- a/src/zope/i18n/tests/test_formats.py +++ b/src/zope/i18n/tests/test_formats.py @@ -34,6 +34,7 @@ from zope.i18n.format import NumberPatternParseError class LocaleStub(object): pass + class LocaleCalendarStub(object): type = u"gregorian" @@ -50,7 +51,7 @@ class LocaleCalendarStub(object): 9: ('September', 'Sep'), 10: ('Oktober', 'Okt'), 11: ('November', 'Nov'), - 12: ('Dezember', 'Dez') + 12: ('Dezember', 'Dez'), } days = { @@ -60,7 +61,7 @@ class LocaleCalendarStub(object): 4: ('Donnerstag', 'Do'), 5: ('Freitag', 'Fr'), 6: ('Samstag', 'Sa'), - 7: ('Sonntag', 'So') + 7: ('Sonntag', 'So'), } am = 'vorm.' @@ -71,7 +72,9 @@ class LocaleCalendarStub(object): week = {'firstDay': 1, 'minDays': 1} def getMonthNames(self): - return [self.months.get(type, (None, None))[0] for type in range(1, 13)] + return [ + self.months.get(type, (None, None))[0] for type in range(1, 13) + ] def getMonthTypeFromName(self, name): for item in self.months.items(): @@ -79,7 +82,9 @@ class LocaleCalendarStub(object): return item[0] def getMonthAbbreviations(self): - return [self.months.get(type, (None, None))[1] for type in range(1, 13)] + return [ + self.months.get(type, (None, None))[1] for type in range(1, 13) + ] def getMonthTypeFromAbbreviation(self, abbr): for item in self.months.items(): @@ -102,94 +107,170 @@ class LocaleCalendarStub(object): class _TestCase(TestCase): # Avoid deprecation warnings in Python 3 by making the preferred # method name available for Python 2. - assertRaisesRegex = getattr(TestCase, 'assertRaisesRegex', TestCase.assertRaisesRegexp) + assertRaisesRegex = getattr( + TestCase, 'assertRaisesRegex', TestCase.assertRaisesRegexp + ) class TestDateTimePatternParser(_TestCase): """Extensive tests for the ICU-based-syntax datetime pattern parser.""" - def testParseSimpleTimePattern(self): - self.assertEqual(parseDateTimePattern('HH'), - [('H', 2)]) - self.assertEqual(parseDateTimePattern('HH:mm'), - [('H', 2), ':', ('m', 2)]) - self.assertEqual(parseDateTimePattern('HH:mm:ss'), - [('H', 2), ':', ('m', 2), ':', ('s', 2)]) - self.assertEqual(parseDateTimePattern('mm:ss'), - [('m', 2), ':', ('s', 2)]) - self.assertEqual(parseDateTimePattern('H:m:s'), - [('H', 1), ':', ('m', 1), ':', ('s', 1)]) - self.assertEqual(parseDateTimePattern('HHH:mmmm:sssss'), - [('H', 3), ':', ('m', 4), ':', ('s', 5)]) + self.assertEqual(parseDateTimePattern('HH'), [('H', 2)]) + self.assertEqual( + parseDateTimePattern('HH:mm'), [('H', 2), ':', ('m', 2)] + ) + self.assertEqual( + parseDateTimePattern('HH:mm:ss'), + [('H', 2), ':', ('m', 2), ':', ('s', 2)], + ) + self.assertEqual( + parseDateTimePattern('mm:ss'), [('m', 2), ':', ('s', 2)] + ) + self.assertEqual( + parseDateTimePattern('H:m:s'), + [('H', 1), ':', ('m', 1), ':', ('s', 1)], + ) + self.assertEqual( + parseDateTimePattern('HHH:mmmm:sssss'), + [('H', 3), ':', ('m', 4), ':', ('s', 5)], + ) def testParseGermanTimePattern(self): # German full - self.assertEqual(parseDateTimePattern("H:mm' Uhr 'z"), - [('H', 1), ':', ('m', 2), ' Uhr ', ('z', 1)]) + self.assertEqual( + parseDateTimePattern("H:mm' Uhr 'z"), + [('H', 1), ':', ('m', 2), ' Uhr ', ('z', 1)], + ) # German long - self.assertEqual(parseDateTimePattern("HH:mm:ss z"), - [('H', 2), ':', ('m', 2), ':', ('s', 2), ' ', - ('z', 1)]) + self.assertEqual( + parseDateTimePattern("HH:mm:ss z"), + [('H', 2), ':', ('m', 2), ':', ('s', 2), ' ', ('z', 1)], + ) # German medium - self.assertEqual(parseDateTimePattern("HH:mm:ss"), - [('H', 2), ':', ('m', 2), ':', ('s', 2)]) + self.assertEqual( + parseDateTimePattern("HH:mm:ss"), + [('H', 2), ':', ('m', 2), ':', ('s', 2)], + ) # German short - self.assertEqual(parseDateTimePattern("HH:mm"), - [('H', 2), ':', ('m', 2)]) + self.assertEqual( + parseDateTimePattern("HH:mm"), [('H', 2), ':', ('m', 2)] + ) def testParseRealDate(self): # German full - self.assertEqual(parseDateTimePattern("EEEE, d. MMMM yyyy"), - [('E', 4), ', ', ('d', 1), '. ', ('M', 4), - ' ', ('y', 4)]) + self.assertEqual( + parseDateTimePattern("EEEE, d. MMMM yyyy"), + [('E', 4), ', ', ('d', 1), '. ', ('M', 4), ' ', ('y', 4)], + ) # German long - self.assertEqual(parseDateTimePattern("d. MMMM yyyy"), - [('d', 1), '. ', ('M', 4), ' ', ('y', 4)]) + self.assertEqual( + parseDateTimePattern("d. MMMM yyyy"), + [('d', 1), '. ', ('M', 4), ' ', ('y', 4)], + ) # German medium - self.assertEqual(parseDateTimePattern("dd.MM.yyyy"), - [('d', 2), '.', ('M', 2), '.', ('y', 4)]) + self.assertEqual( + parseDateTimePattern("dd.MM.yyyy"), + [('d', 2), '.', ('M', 2), '.', ('y', 4)], + ) # German short - self.assertEqual(parseDateTimePattern("dd.MM.yy"), - [('d', 2), '.', ('M', 2), '.', ('y', 2)]) + self.assertEqual( + parseDateTimePattern("dd.MM.yy"), + [('d', 2), '.', ('M', 2), '.', ('y', 2)], + ) def testParseRealDateTime(self): # German full self.assertEqual( parseDateTimePattern("EEEE, d. MMMM yyyy H:mm' Uhr 'z"), - [('E', 4), ', ', ('d', 1), '. ', ('M', 4), ' ', ('y', 4), - ' ', ('H', 1), ':', ('m', 2), ' Uhr ', ('z', 1)]) + [ + ('E', 4), + ', ', + ('d', 1), + '. ', + ('M', 4), + ' ', + ('y', 4), + ' ', + ('H', 1), + ':', + ('m', 2), + ' Uhr ', + ('z', 1), + ], + ) # German long self.assertEqual( parseDateTimePattern("d. MMMM yyyy HH:mm:ss z"), - [('d', 1), '. ', ('M', 4), ' ', ('y', 4), - ' ', ('H', 2), ':', ('m', 2), ':', ('s', 2), ' ', ('z', 1)]) + [ + ('d', 1), + '. ', + ('M', 4), + ' ', + ('y', 4), + ' ', + ('H', 2), + ':', + ('m', 2), + ':', + ('s', 2), + ' ', + ('z', 1), + ], + ) # German medium self.assertEqual( parseDateTimePattern("dd.MM.yyyy HH:mm:ss"), - [('d', 2), '.', ('M', 2), '.', ('y', 4), - ' ', ('H', 2), ':', ('m', 2), ':', ('s', 2)]) + [ + ('d', 2), + '.', + ('M', 2), + '.', + ('y', 4), + ' ', + ('H', 2), + ':', + ('m', 2), + ':', + ('s', 2), + ], + ) # German short self.assertEqual( parseDateTimePattern("dd.MM.yy HH:mm"), - [('d', 2), '.', ('M', 2), '.', ('y', 2), - ' ', ('H', 2), ':', ('m', 2)]) + [ + ('d', 2), + '.', + ('M', 2), + '.', + ('y', 2), + ' ', + ('H', 2), + ':', + ('m', 2), + ], + ) def testParseQuotesInPattern(self): - self.assertEqual(parseDateTimePattern("HH''mm"), - [('H', 2), "'", ('m', 2)]) - self.assertEqual(parseDateTimePattern("HH'HHmm'mm"), - [('H', 2), 'HHmm', ('m', 2)]) - self.assertEqual(parseDateTimePattern("HH':'''':'mm"), - [('H', 2), ":':", ('m', 2)]) - self.assertEqual(parseDateTimePattern("HH':' ':'mm"), - [('H', 2), ": :", ('m', 2)]) + self.assertEqual( + parseDateTimePattern("HH''mm"), [('H', 2), "'", ('m', 2)] + ) + self.assertEqual( + parseDateTimePattern("HH'HHmm'mm"), [('H', 2), 'HHmm', ('m', 2)] + ) + self.assertEqual( + parseDateTimePattern("HH':'''':'mm"), [('H', 2), ":':", ('m', 2)] + ) + self.assertEqual( + parseDateTimePattern("HH':' ':'mm"), [('H', 2), ": :", ('m', 2)] + ) def testParseDateTimePatternError(self): # Quote not closed with self.assertRaisesRegex( - DateTimePatternParseError, - 'The quote starting at character 2 is not closed.'): + DateTimePatternParseError, + 'The quote starting at character 2 is not closed.', + ): parseDateTimePattern("HH' Uhr") # Test correct length of characters in datetime fields @@ -198,10 +279,7 @@ class TestDateTimePatternParser(_TestCase): def testParseDateTimePatternRepeatDateTimeChars(self): result = parseDateTimePattern('aG') - self.assertEqual( - result, - [('a', 1), ('G', 1)] - ) + self.assertEqual(result, [('a', 1), ('G', 1)]) class TestBuildDateTimeParseInfo(_TestCase): @@ -216,8 +294,10 @@ class TestBuildDateTimeParseInfo(_TestCase): def testGenericNumbers(self): for char in 'dDFkKhHmsSwW': for length in range(1, 6): - self.assertEqual(self.info((char, length)), - '([0-9]{%i,1000})' %length) + self.assertEqual( + self.info((char, length)), '([0-9]{%i,1000})' % length + ) + def testYear(self): self.assertEqual(self.info(('y', 2)), '([0-9]{2})') self.assertEqual(self.info(('y', 4)), '([0-9]{4})') @@ -228,7 +308,9 @@ class TestBuildDateTimeParseInfo(_TestCase): def testAMPMMarker(self): names = ['vorm.', 'nachm.'] for length in range(1, 6): - self.assertEqual(self.info(('a', length)), '('+'|'.join(names)+')') + self.assertEqual( + self.info(('a', length)), '(' + '|'.join(names) + ')' + ) def testEra(self): self.assertEqual(self.info(('G', 1)), '(v. Chr.|n. Chr.)') @@ -245,30 +327,60 @@ class TestBuildDateTimeParseInfo(_TestCase): self.assertEqual(self.info(('M', 2)), '([0-9]{2})') def testMonthNames(self): - names = [u"Januar", u"Februar", u"Maerz", u"April", - u"Mai", u"Juni", u"Juli", u"August", u"September", u"Oktober", - u"November", u"Dezember"] - self.assertEqual(self.info(('M', 4)), '('+'|'.join(names)+')') + names = [ + u"Januar", + u"Februar", + u"Maerz", + u"April", + u"Mai", + u"Juni", + u"Juli", + u"August", + u"September", + u"Oktober", + u"November", + u"Dezember", + ] + self.assertEqual(self.info(('M', 4)), '(' + '|'.join(names) + ')') def testMonthAbbr(self): - names = ['Jan', 'Feb', 'Mrz', 'Apr', 'Mai', 'Jun', 'Jul', 'Aug', - 'Sep', 'Okt', 'Nov', 'Dez'] - self.assertEqual(self.info(('M', 3)), '('+'|'.join(names)+')') + names = [ + 'Jan', + 'Feb', + 'Mrz', + 'Apr', + 'Mai', + 'Jun', + 'Jul', + 'Aug', + 'Sep', + 'Okt', + 'Nov', + 'Dez', + ] + self.assertEqual(self.info(('M', 3)), '(' + '|'.join(names) + ')') def testWeekdayNumber(self): self.assertEqual(self.info(('E', 1)), '([0-9])') self.assertEqual(self.info(('E', 2)), '([0-9]{2})') def testWeekdayNames(self): - names = ['Montag', 'Dienstag', 'Mittwoch', 'Donnerstag', - 'Freitag', 'Samstag', 'Sonntag'] - self.assertEqual(self.info(('E', 4)), '('+'|'.join(names)+')') - self.assertEqual(self.info(('E', 5)), '('+'|'.join(names)+')') - self.assertEqual(self.info(('E', 10)), '('+'|'.join(names)+')') + names = [ + 'Montag', + 'Dienstag', + 'Mittwoch', + 'Donnerstag', + 'Freitag', + 'Samstag', + 'Sonntag', + ] + self.assertEqual(self.info(('E', 4)), '(' + '|'.join(names) + ')') + self.assertEqual(self.info(('E', 5)), '(' + '|'.join(names) + ')') + self.assertEqual(self.info(('E', 10)), '(' + '|'.join(names) + ')') def testWeekdayAbbr(self): names = ['Mo', 'Di', 'Mi', 'Do', 'Fr', 'Sa', 'So'] - self.assertEqual(self.info(('E', 3)), '('+'|'.join(names)+')') + self.assertEqual(self.info(('E', 3)), '(' + '|'.join(names) + ')') class TestDateTimeFormat(_TestCase): @@ -284,36 +396,44 @@ class TestDateTimeFormat(_TestCase): # German short self.assertEqual( self.format.parse('02.01.03 21:48', 'dd.MM.yy HH:mm'), - datetime.datetime(2003, 1, 2, 21, 48)) + datetime.datetime(2003, 1, 2, 21, 48), + ) def testParseRealDateTime(self): # German medium self.assertEqual( self.format.parse('02.01.2003 21:48:01', 'dd.MM.yyyy HH:mm:ss'), - datetime.datetime(2003, 1, 2, 21, 48, 1)) + datetime.datetime(2003, 1, 2, 21, 48, 1), + ) # German long # TODO: The parser does not support timezones yet. self.assertEqual( self.format.parse( - '2. Januar 2003 21:48:01 +100', - 'd. MMMM yyyy HH:mm:ss z'), + '2. Januar 2003 21:48:01 +100', 'd. MMMM yyyy HH:mm:ss z' + ), pytz.timezone('Europe/Berlin').localize( - datetime.datetime(2003, 1, 2, 21, 48, 1))) + datetime.datetime(2003, 1, 2, 21, 48, 1) + ), + ) # German full # TODO: The parser does not support timezones yet. self.assertEqual( self.format.parse( 'Donnerstag, 2. Januar 2003 21:48 Uhr +100', - "EEEE, d. MMMM yyyy H:mm' Uhr 'z"), + "EEEE, d. MMMM yyyy H:mm' Uhr 'z", + ), pytz.timezone('Europe/Berlin').localize( - datetime.datetime(2003, 1, 2, 21, 48))) + datetime.datetime(2003, 1, 2, 21, 48) + ), + ) def testParseAMPMDateTime(self): self.assertEqual( self.format.parse('02.01.03 09:48 nachm.', 'dd.MM.yy hh:mm a'), - datetime.datetime(2003, 1, 2, 21, 48)) + datetime.datetime(2003, 1, 2, 21, 48), + ) def testParseTimeZone(self): dt = self.format.parse('09:48 -600', 'HH:mm z') @@ -338,15 +458,17 @@ class TestDateTimeFormat(_TestCase): self.assertEqual(dt.tzinfo.zone, 'EST') self.assertEqual(dt.tzinfo.tzname(dt), 'EST') - dt = self.format.parse('01.01.2003 09:48 US/Eastern', - 'dd.MM.yyyy HH:mm zzzz') + dt = self.format.parse( + '01.01.2003 09:48 US/Eastern', 'dd.MM.yyyy HH:mm zzzz' + ) self.assertEqual(pickle.loads(pickle.dumps(dt)), dt) self.assertEqual(dt.tzinfo.utcoffset(dt), datetime.timedelta(hours=-5)) self.assertEqual(dt.tzinfo.zone, 'US/Eastern') self.assertEqual(dt.tzinfo.tzname(dt), 'EST') - dt = self.format.parse('01.01.2003 09:48 Canada/Eastern', - 'dd.MM.yyyy HH:mm zzzz') + dt = self.format.parse( + '01.01.2003 09:48 Canada/Eastern', 'dd.MM.yyyy HH:mm zzzz' + ) self.assertEqual(dt.tzinfo.utcoffset(dt), datetime.timedelta(hours=-5)) self.assertEqual(dt.tzinfo.zone, 'Canada/Eastern') self.assertEqual(dt.tzinfo.tzname(dt), 'EST') @@ -354,13 +476,15 @@ class TestDateTimeFormat(_TestCase): # Note that historical and future (as far as known) # timezones are handled happily using the pytz timezone database # US DST transition points are changing in 2007 - dt = self.format.parse('01.04.2006 09:48 US/Eastern', - 'dd.MM.yyyy HH:mm zzzz') + dt = self.format.parse( + '01.04.2006 09:48 US/Eastern', 'dd.MM.yyyy HH:mm zzzz' + ) self.assertEqual(dt.tzinfo.zone, 'US/Eastern') self.assertEqual(dt.tzinfo.tzname(dt), 'EST') self.assertEqual(dt.tzinfo.utcoffset(dt), datetime.timedelta(hours=-5)) - dt = self.format.parse('01.04.2007 09:48 US/Eastern', - 'dd.MM.yyyy HH:mm zzzz') + dt = self.format.parse( + '01.04.2007 09:48 US/Eastern', 'dd.MM.yyyy HH:mm zzzz' + ) self.assertEqual(dt.tzinfo.zone, 'US/Eastern') self.assertEqual(dt.tzinfo.tzname(dt), 'EDT') self.assertEqual(dt.tzinfo.utcoffset(dt), datetime.timedelta(hours=-4)) @@ -376,65 +500,75 @@ class TestDateTimeFormat(_TestCase): def testParse12PM(self): self.assertEqual( self.format.parse('01.01.03 12:00 nachm.', 'dd.MM.yy hh:mm a'), - datetime.datetime(2003, 1, 1, 12, 00, 00, 00)) + datetime.datetime(2003, 1, 1, 12, 00, 00, 00), + ) def testParseUnusualFormats(self): self.assertEqual( - self.format.parse('001. Januar 03 0012:00', - 'ddd. MMMMM yy HHHH:mm'), - datetime.datetime(2003, 1, 1, 12, 00, 00, 00)) + self.format.parse( + '001. Januar 03 0012:00', 'ddd. MMMMM yy HHHH:mm' + ), + datetime.datetime(2003, 1, 1, 12, 00, 00, 00), + ) self.assertEqual( - self.format.parse('0001. Jan 2003 0012:00 vorm.', - 'dddd. MMM yyyy hhhh:mm a'), - datetime.datetime(2003, 1, 1, 00, 00, 00, 00)) + self.format.parse( + '0001. Jan 2003 0012:00 vorm.', 'dddd. MMM yyyy hhhh:mm a' + ), + datetime.datetime(2003, 1, 1, 00, 00, 00, 00), + ) def testParseNotObject(self): self.assertEqual( ('2017', '01', '01'), - self.format.parse('2017-01-01', 'yyyy-MM-dd', asObject=False)) + self.format.parse('2017-01-01', 'yyyy-MM-dd', asObject=False), + ) def testParseTwoDigitYearIs20thCentury(self): self.assertEqual( datetime.date(1952, 1, 1), - self.format.parse('52-01-01', 'yy-MM-dd')) + self.format.parse('52-01-01', 'yy-MM-dd'), + ) # 30 is the cut off self.assertEqual( datetime.date(1931, 1, 1), - self.format.parse('31-01-01', 'yy-MM-dd')) + self.format.parse('31-01-01', 'yy-MM-dd'), + ) self.assertEqual( datetime.date(2030, 1, 1), - self.format.parse('30-01-01', 'yy-MM-dd')) + self.format.parse('30-01-01', 'yy-MM-dd'), + ) def testParseAMPMMissing(self): with self.assertRaisesRegex( - DateTimeParseError, - 'Cannot handle 12-hour format without am/pm marker.'): + DateTimeParseError, + 'Cannot handle 12-hour format without am/pm marker.', + ): self.format.parse('02.01.03 09:48', 'dd.MM.yy hh:mm') def testParseBadTimezone(self): # Produces an object without pytz info self.assertEqual( datetime.time(21, 48, 1), - self.format.parse( - '21:48:01 Bad/Timezone', - 'HH:mm:ss zzzz')) + self.format.parse('21:48:01 Bad/Timezone', 'HH:mm:ss zzzz'), + ) def testParsePyTzTimezone(self): tzinfo = pytz.timezone("US/Central") self.assertEqual( datetime.time(21, 48, 1, tzinfo=tzinfo), - self.format.parse( - '21:48:01 US/Central', - 'HH:mm:ss zzzz')) + self.format.parse('21:48:01 US/Central', 'HH:mm:ss zzzz'), + ) def testFormatSimpleDateTime(self): # German short self.assertEqual( - self.format.format(datetime.datetime(2003, 1, 2, 21, 48), - 'dd.MM.yy HH:mm'), - '02.01.03 21:48') + self.format.format( + datetime.datetime(2003, 1, 2, 21, 48), 'dd.MM.yy HH:mm' + ), + '02.01.03 21:48', + ) def testFormatRealDateTime(self): tz = pytz.timezone('Europe/Berlin') @@ -442,80 +576,89 @@ class TestDateTimeFormat(_TestCase): # German medium self.assertEqual( self.format.format(dt, 'dd.MM.yyyy HH:mm:ss'), - '02.01.2003 21:48:01') + '02.01.2003 21:48:01', + ) # German long self.assertEqual( self.format.format(dt, 'd. MMMM yyyy HH:mm:ss z'), - '2. Januar 2003 21:48:01 +100') + '2. Januar 2003 21:48:01 +100', + ) # German full self.assertEqual( - self.format.format( - dt, "EEEE, d. MMMM yyyy H:mm' Uhr 'z"), - 'Donnerstag, 2. Januar 2003 21:48 Uhr +100') + self.format.format(dt, "EEEE, d. MMMM yyyy H:mm' Uhr 'z"), + 'Donnerstag, 2. Januar 2003 21:48 Uhr +100', + ) def testFormatAMPMDateTime(self): self.assertEqual( self.format.format( - datetime.datetime(2003, 1, 2, 21, 48), - 'dd.MM.yy hh:mm a'), - '02.01.03 09:48 nachm.') + datetime.datetime(2003, 1, 2, 21, 48), 'dd.MM.yy hh:mm a' + ), + '02.01.03 09:48 nachm.', + ) def testFormatAllWeekdays(self): for day in range(1, 8): self.assertEqual( self.format.format( - datetime.datetime(2003, 1, day+5, 21, 48), - "EEEE, d. MMMM yyyy H:mm' Uhr 'z"), - '%s, %i. Januar 2003 21:48 Uhr +000' % ( - self.format.calendar.days[day][0], day+5)) + datetime.datetime(2003, 1, day + 5, 21, 48), + "EEEE, d. MMMM yyyy H:mm' Uhr 'z", + ), + '%s, %i. Januar 2003 21:48 Uhr +000' + % (self.format.calendar.days[day][0], day + 5), + ) def testFormatTimeZone(self): self.assertEqual( - self.format.format( - datetime.datetime(2003, 1, 2, 12, 00), 'z'), - '+000') + self.format.format(datetime.datetime(2003, 1, 2, 12, 00), 'z'), + '+000', + ) self.assertEqual( - self.format.format( - datetime.datetime(2003, 1, 2, 12, 00), 'zz'), - '+00:00') + self.format.format(datetime.datetime(2003, 1, 2, 12, 00), 'zz'), + '+00:00', + ) self.assertEqual( - self.format.format( - datetime.datetime(2003, 1, 2, 12, 00), 'zzz'), - 'UTC') + self.format.format(datetime.datetime(2003, 1, 2, 12, 00), 'zzz'), + 'UTC', + ) self.assertEqual( - self.format.format( - datetime.datetime(2003, 1, 2, 12, 00), 'zzzz'), - 'UTC') + self.format.format(datetime.datetime(2003, 1, 2, 12, 00), 'zzzz'), + 'UTC', + ) tz = pytz.timezone('US/Eastern') self.assertEqual( self.format.format( - tz.localize(datetime.datetime(2003, 1, 2, 12)), 'z'), - '-500') + tz.localize(datetime.datetime(2003, 1, 2, 12)), 'z' + ), + '-500', + ) self.assertEqual( self.format.format( - tz.localize(datetime.datetime(2003, 1, 2, 12)), 'zz'), - '-05:00') + tz.localize(datetime.datetime(2003, 1, 2, 12)), 'zz' + ), + '-05:00', + ) self.assertEqual( self.format.format( - tz.localize(datetime.datetime(2003, 1, 2, 12)), 'zzz'), - 'EST') + tz.localize(datetime.datetime(2003, 1, 2, 12)), 'zzz' + ), + 'EST', + ) self.assertEqual( self.format.format( - tz.localize(datetime.datetime(2003, 1, 2, 12)), 'zzzz'), - 'US/Eastern') + tz.localize(datetime.datetime(2003, 1, 2, 12)), 'zzzz' + ), + 'US/Eastern', + ) def testFormatWeekDay(self): date = datetime.date(2003, 1, 2) - self.assertEqual(self.format.format(date, "E"), - '4') - self.assertEqual(self.format.format(date, "EE"), - '04') - self.assertEqual(self.format.format(date, "EEE"), - 'Do') - self.assertEqual(self.format.format(date, "EEEE"), - 'Donnerstag') + self.assertEqual(self.format.format(date, "E"), '4') + self.assertEqual(self.format.format(date, "EE"), '04') + self.assertEqual(self.format.format(date, "EEE"), 'Do') + self.assertEqual(self.format.format(date, "EEEE"), 'Donnerstag') # Create custom calendar, which has Sunday as the first day of the # week. I am assigning a totally new dict here, since dicts are @@ -525,166 +668,146 @@ class TestDateTimeFormat(_TestCase): calendar.week = {'firstDay': 7, 'minDays': 1} format = DateTimeFormat(calendar=calendar) - self.assertEqual(format.format(date, "E"), - '5') - self.assertEqual(format.format(date, "EE"), - '05') + self.assertEqual(format.format(date, "E"), '5') + self.assertEqual(format.format(date, "EE"), '05') def testFormatDayOfWeekInMonth(self): date = datetime.date(2003, 1, 2) - self.assertEqual(self.format.format(date, "F"), - '1') - self.assertEqual(self.format.format(date, "FF"), - '01') + self.assertEqual(self.format.format(date, "F"), '1') + self.assertEqual(self.format.format(date, "FF"), '01') self.assertEqual( - self.format.format(datetime.date(2003, 1, 9), "F"), - '2') + self.format.format(datetime.date(2003, 1, 9), "F"), '2' + ) self.assertEqual( - self.format.format(datetime.date(2003, 1, 16), "F"), - '3') + self.format.format(datetime.date(2003, 1, 16), "F"), '3' + ) self.assertEqual( - self.format.format(datetime.date(2003, 1, 23), "F"), - '4') + self.format.format(datetime.date(2003, 1, 23), "F"), '4' + ) def testFormatWeekInMonth(self): self.assertEqual( - self.format.format(datetime.date(2003, 1, 3), "W"), - '1') + self.format.format(datetime.date(2003, 1, 3), "W"), '1' + ) self.assertEqual( - self.format.format(datetime.date(2003, 1, 3), "WW"), - '01') + self.format.format(datetime.date(2003, 1, 3), "WW"), '01' + ) self.assertEqual( - self.format.format(datetime.date(2003, 1, 8), "W"), - '2') + self.format.format(datetime.date(2003, 1, 8), "W"), '2' + ) self.assertEqual( - self.format.format(datetime.date(2003, 1, 19), "W"), - '3') + self.format.format(datetime.date(2003, 1, 19), "W"), '3' + ) self.assertEqual( - self.format.format(datetime.date(2003, 1, 20), "W"), - '4') + self.format.format(datetime.date(2003, 1, 20), "W"), '4' + ) self.assertEqual( - self.format.format(datetime.date(2003, 1, 31), "W"), - '5') + self.format.format(datetime.date(2003, 1, 31), "W"), '5' + ) def testFormatHourInDayOneTo24(self): - self.assertEqual( - self.format.format(datetime.time(5, 0), "k"), - '5') - self.assertEqual( - self.format.format(datetime.time(5, 0), "kk"), - '05') - self.assertEqual( - self.format.format(datetime.time(0, 0), "k"), - '24') - self.assertEqual( - self.format.format(datetime.time(1, 0), "k"), - '1') + self.assertEqual(self.format.format(datetime.time(5, 0), "k"), '5') + self.assertEqual(self.format.format(datetime.time(5, 0), "kk"), '05') + self.assertEqual(self.format.format(datetime.time(0, 0), "k"), '24') + self.assertEqual(self.format.format(datetime.time(1, 0), "k"), '1') def testFormatHourInDayZeroToEleven(self): - self.assertEqual( - self.format.format(datetime.time(5, 0), "K"), - '5') - self.assertEqual( - self.format.format(datetime.time(5, 0), "KK"), - '05') - self.assertEqual( - self.format.format(datetime.time(0, 0), "K"), - '0') - self.assertEqual( - self.format.format(datetime.time(12, 0), "K"), - '0') - self.assertEqual( - self.format.format(datetime.time(11, 0), "K"), - '11') - self.assertEqual( - self.format.format(datetime.time(23, 0), "K"), - '11') + self.assertEqual(self.format.format(datetime.time(5, 0), "K"), '5') + self.assertEqual(self.format.format(datetime.time(5, 0), "KK"), '05') + self.assertEqual(self.format.format(datetime.time(0, 0), "K"), '0') + self.assertEqual(self.format.format(datetime.time(12, 0), "K"), '0') + self.assertEqual(self.format.format(datetime.time(11, 0), "K"), '11') + self.assertEqual(self.format.format(datetime.time(23, 0), "K"), '11') def testFormatSimpleHourRepresentation(self): self.assertEqual( - self.format.format(datetime.datetime(2003, 1, 2, 23, 00), - 'dd.MM.yy h:mm:ss a'), - '02.01.03 11:00:00 nachm.') + self.format.format( + datetime.datetime(2003, 1, 2, 23, 00), 'dd.MM.yy h:mm:ss a' + ), + '02.01.03 11:00:00 nachm.', + ) self.assertEqual( - self.format.format(datetime.datetime(2003, 1, 2, 2, 00), - 'dd.MM.yy h:mm:ss a'), - '02.01.03 2:00:00 vorm.') + self.format.format( + datetime.datetime(2003, 1, 2, 2, 00), 'dd.MM.yy h:mm:ss a' + ), + '02.01.03 2:00:00 vorm.', + ) self.assertEqual( - self.format.format(datetime.time(0, 15), 'h:mm a'), - '12:15 vorm.') + self.format.format(datetime.time(0, 15), 'h:mm a'), '12:15 vorm.' + ) self.assertEqual( - self.format.format(datetime.time(1, 15), 'h:mm a'), - '1:15 vorm.') + self.format.format(datetime.time(1, 15), 'h:mm a'), '1:15 vorm.' + ) self.assertEqual( - self.format.format(datetime.time(12, 15), 'h:mm a'), - '12:15 nachm.') + self.format.format(datetime.time(12, 15), 'h:mm a'), '12:15 nachm.' + ) self.assertEqual( - self.format.format(datetime.time(13, 15), 'h:mm a'), - '1:15 nachm.') + self.format.format(datetime.time(13, 15), 'h:mm a'), '1:15 nachm.' + ) def testFormatDayInYear(self): self.assertEqual( - self.format.format(datetime.date(2003, 1, 3), 'D'), - u"3") + self.format.format(datetime.date(2003, 1, 3), 'D'), u"3" + ) self.assertEqual( - self.format.format(datetime.date(2003, 1, 3), 'DD'), - u"03") + self.format.format(datetime.date(2003, 1, 3), 'DD'), u"03" + ) self.assertEqual( - self.format.format(datetime.date(2003, 1, 3), 'DDD'), - u"003") + self.format.format(datetime.date(2003, 1, 3), 'DDD'), u"003" + ) self.assertEqual( - self.format.format(datetime.date(2003, 12, 31), 'D'), - u"365") + self.format.format(datetime.date(2003, 12, 31), 'D'), u"365" + ) self.assertEqual( - self.format.format(datetime.date(2003, 12, 31), 'DD'), - u"365") + self.format.format(datetime.date(2003, 12, 31), 'DD'), u"365" + ) self.assertEqual( - self.format.format(datetime.date(2003, 12, 31), 'DDD'), - u"365") + self.format.format(datetime.date(2003, 12, 31), 'DDD'), u"365" + ) self.assertEqual( - self.format.format(datetime.date(2004, 12, 31), 'DDD'), - u"366") + self.format.format(datetime.date(2004, 12, 31), 'DDD'), u"366" + ) def testFormatDayOfWeekInMOnth(self): self.assertEqual( - self.format.format(datetime.date(2003, 1, 3), 'F'), - u"1") + self.format.format(datetime.date(2003, 1, 3), 'F'), u"1" + ) self.assertEqual( - self.format.format(datetime.date(2003, 1, 10), 'F'), - u"2") + self.format.format(datetime.date(2003, 1, 10), 'F'), u"2" + ) self.assertEqual( - self.format.format(datetime.date(2003, 1, 17), 'F'), - u"3") + self.format.format(datetime.date(2003, 1, 17), 'F'), u"3" + ) self.assertEqual( - self.format.format(datetime.date(2003, 1, 24), 'F'), - u"4") + self.format.format(datetime.date(2003, 1, 24), 'F'), u"4" + ) self.assertEqual( - self.format.format(datetime.date(2003, 1, 31), 'F'), - u"5") + self.format.format(datetime.date(2003, 1, 31), 'F'), u"5" + ) self.assertEqual( - self.format.format(datetime.date(2003, 1, 6), 'F'), - u"1") + self.format.format(datetime.date(2003, 1, 6), 'F'), u"1" + ) def testFormatUnusualFormats(self): self.assertEqual( self.format.format(datetime.date(2003, 1, 3), 'DDD-yyyy'), - u"003-2003") + u"003-2003", + ) self.assertEqual( - self.format.format(datetime.date(2003, 1, 10), - "F. EEEE 'im' MMMM, yyyy"), - u"2. Freitag im Januar, 2003") - + self.format.format( + datetime.date(2003, 1, 10), "F. EEEE 'im' MMMM, yyyy" + ), + u"2. Freitag im Januar, 2003", + ) def testFormatGregorianEra(self): self.assertEqual( - self.format.format(datetime.date(2017, 12, 17), 'G'), - u'n. Chr.' + self.format.format(datetime.date(2017, 12, 17), 'G'), u'n. Chr.' ) def testFormateMonthLengthOne(self): self.assertEqual( - self.format.format(datetime.date(2017, 12, 17), 'M'), - u'12' + self.format.format(datetime.date(2017, 12, 17), 'M'), u'12' ) @@ -694,515 +817,757 @@ class TestNumberPatternParser(_TestCase): def testParseSimpleIntegerPattern(self): self.assertEqual( parseNumberPattern('###0'), - ((None, '', None, '###0', '', '', None, '', None, ()), - (None, '', None, '###0', '', '', None, '', None, ()))) + ( + (None, '', None, '###0', '', '', None, '', None, ()), + (None, '', None, '###0', '', '', None, '', None, ()), + ), + ) def testParseScientificIntegerPattern(self): self.assertEqual( parseNumberPattern('###0E#0'), - ((None, '', None, '###0', '', '#0', None, '', None, ()), - (None, '', None, '###0', '', '#0', None, '', None, ()))) + ( + (None, '', None, '###0', '', '#0', None, '', None, ()), + (None, '', None, '###0', '', '#0', None, '', None, ()), + ), + ) self.assertEqual( parseNumberPattern('###0E+#0'), - ((None, '', None, '###0', '', '+#0', None, '', None, ()), - (None, '', None, '###0', '', '+#0', None, '', None, ()))) + ( + (None, '', None, '###0', '', '+#0', None, '', None, ()), + (None, '', None, '###0', '', '+#0', None, '', None, ()), + ), + ) def testParsePosNegAlternativeIntegerPattern(self): self.assertEqual( parseNumberPattern('###0;#0'), - ((None, '', None, '###0', '', '', None, '', None, ()), - (None, '', None, '#0', '', '', None, '', None, ()))) + ( + (None, '', None, '###0', '', '', None, '', None, ()), + (None, '', None, '#0', '', '', None, '', None, ()), + ), + ) def testParsePrefixedIntegerPattern(self): self.assertEqual( parseNumberPattern('+###0'), - ((None, '+', None, '###0', '', '', None, '', None, ()), - (None, '+', None, '###0', '', '', None, '', None, ()))) + ( + (None, '+', None, '###0', '', '', None, '', None, ()), + (None, '+', None, '###0', '', '', None, '', None, ()), + ), + ) def testParsePosNegIntegerPattern(self): self.assertEqual( parseNumberPattern('+###0;-###0'), - ((None, '+', None, '###0', '', '', None, '', None, ()), - (None, '-', None, '###0', '', '', None, '', None, ()))) + ( + (None, '+', None, '###0', '', '', None, '', None, ()), + (None, '-', None, '###0', '', '', None, '', None, ()), + ), + ) def testParseScientificPosNegIntegerPattern(self): self.assertEqual( parseNumberPattern('+###0E0;-###0E#0'), - ((None, '+', None, '###0', '', '0', None, '', None, ()), - (None, '-', None, '###0', '', '#0', None, '', None, ()))) + ( + (None, '+', None, '###0', '', '0', None, '', None, ()), + (None, '-', None, '###0', '', '#0', None, '', None, ()), + ), + ) def testParseThousandSeparatorIntegerPattern(self): self.assertEqual( parseNumberPattern('#,##0'), - ((None, '', None, '###0', '', '', None, '', None, (3, 0)), - (None, '', None, '###0', '', '', None, '', None, (3, 0)))) + ( + (None, '', None, '###0', '', '', None, '', None, (3, 0)), + (None, '', None, '###0', '', '', None, '', None, (3, 0)), + ), + ) def testParseSimpleDecimalPattern(self): self.assertEqual( parseNumberPattern('###0.00#'), - ((None, '', None, '###0', '00#', '', None, '', None, ()), - (None, '', None, '###0', '00#', '', None, '', None, ()))) + ( + (None, '', None, '###0', '00#', '', None, '', None, ()), + (None, '', None, '###0', '00#', '', None, '', None, ()), + ), + ) def testParseScientificDecimalPattern(self): self.assertEqual( parseNumberPattern('###0.00#E#0'), - ((None, '', None, '###0', '00#', '#0', None, '', None, ()), - (None, '', None, '###0', '00#', '#0', None, '', None, ()))) + ( + (None, '', None, '###0', '00#', '#0', None, '', None, ()), + (None, '', None, '###0', '00#', '#0', None, '', None, ()), + ), + ) def testParsePosNegAlternativeFractionPattern(self): self.assertEqual( parseNumberPattern('###0.00#;#0.0#'), - ((None, '', None, '###0', '00#', '', None, '', None, ()), - (None, '', None, '#0', '0#', '', None, '', None, ()))) + ( + (None, '', None, '###0', '00#', '', None, '', None, ()), + (None, '', None, '#0', '0#', '', None, '', None, ()), + ), + ) def testParsePosNegFractionPattern(self): self.assertEqual( parseNumberPattern('+###0.0##;-###0.0##'), - ((None, '+', None, '###0', '0##', '', None, '', None, ()), - (None, '-', None, '###0', '0##', '', None, '', None, ()))) + ( + (None, '+', None, '###0', '0##', '', None, '', None, ()), + (None, '-', None, '###0', '0##', '', None, '', None, ()), + ), + ) def testParseScientificPosNegFractionPattern(self): self.assertEqual( parseNumberPattern('+###0.0##E#0;-###0.0##E0'), - ((None, '+', None, '###0', '0##', '#0', None, '', None, ()), - (None, '-', None, '###0', '0##', '0', None, '', None, ()))) + ( + (None, '+', None, '###0', '0##', '#0', None, '', None, ()), + (None, '-', None, '###0', '0##', '0', None, '', None, ()), + ), + ) def testParseThousandSeparatorFractionPattern(self): self.assertEqual( parseNumberPattern('#,##0.0#'), - ((None, '', None, '###0', '0#', '', None, '', None, (3, 0)), - (None, '', None, '###0', '0#', '', None, '', None, (3, 0)))) + ( + (None, '', None, '###0', '0#', '', None, '', None, (3, 0)), + (None, '', None, '###0', '0#', '', None, '', None, (3, 0)), + ), + ) def testParseThousandSeparatorPatterns(self): # the following patterns are present in the ICU XMLs: self.assertEqual( parseNumberPattern('#,##0.00;-#,##0.00'), - ((None, '', None, '###0', '00', '', None, '', None, (3, 0)), - (None, '-', None, '###0', '00', '', None, '', None, (3, 0)))) + ( + (None, '', None, '###0', '00', '', None, '', None, (3, 0)), + (None, '-', None, '###0', '00', '', None, '', None, (3, 0)), + ), + ) self.assertEqual( parseNumberPattern('#,##,##0.###;-#,##,##0.###'), - ((None, '', None, '#####0', '###', '', None, '', None, (3, 2, 0)), - (None, '-', None, '#####0', '###', '', None, '', None, (3, 2, 0)))) + ( + ( + None, + '', + None, + '#####0', + '###', + '', + None, + '', + None, + (3, 2, 0), + ), + ( + None, + '-', + None, + '#####0', + '###', + '', + None, + '', + None, + (3, 2, 0), + ), + ), + ) self.assertEqual( parseNumberPattern('#,##0.##;-#,##0.##'), - ((None, '', None, '###0', '##', '', None, '', None, (3, 0)), - (None, '-', None, '###0', '##', '', None, '', None, (3, 0)))) + ( + (None, '', None, '###0', '##', '', None, '', None, (3, 0)), + (None, '-', None, '###0', '##', '', None, '', None, (3, 0)), + ), + ) self.assertEqual( parseNumberPattern('#,##0.###;-#,##0.###'), - ((None, '', None, '###0', '###', '', None, '', None, (3, 0)), - (None, '-', None, '###0', '###', '', None, '', None, (3, 0)))) + ( + (None, '', None, '###0', '###', '', None, '', None, (3, 0)), + (None, '-', None, '###0', '###', '', None, '', None, (3, 0)), + ), + ) self.assertEqual( parseNumberPattern('#,##0.###;(#,##0.###)'), - ((None, '', None, '###0', '###', '', None, '', None, (3, 0)), - (None, '(', None, '###0', '###', '', None, ')', None, (3, 0)))) + ( + (None, '', None, '###0', '###', '', None, '', None, (3, 0)), + (None, '(', None, '###0', '###', '', None, ')', None, (3, 0)), + ), + ) self.assertEqual( parseNumberPattern('#,##0.###;#,##0.###-'), - ((None, '', None, '###0', '###', '', None, '', None, (3, 0)), - (None, '', None, '###0', '###', '', None, '-', None, (3, 0)))) + ( + (None, '', None, '###0', '###', '', None, '', None, (3, 0)), + (None, '', None, '###0', '###', '', None, '-', None, (3, 0)), + ), + ) self.assertEqual( parseNumberPattern('##,##,##0.###;-##,##,##0.###'), - ((None, '', None, '######0', '###', '', None, '', None, (3, 2, 0)), - (None, '-', None, '######0', '###', '', None, '', None, (3, 2, 0)))) + ( + ( + None, + '', + None, + '######0', + '###', + '', + None, + '', + None, + (3, 2, 0), + ), + ( + None, + '-', + None, + '######0', + '###', + '', + None, + '', + None, + (3, 2, 0), + ), + ), + ) self.assertEqual( parseNumberPattern('##,##0.##;-##,##0.##'), - ((None, '', None, '####0', '##', '', None, '', None, (3, 0)), - (None, '-', None, '####0', '##', '', None, '', None, (3, 0)))) + ( + (None, '', None, '####0', '##', '', None, '', None, (3, 0)), + (None, '-', None, '####0', '##', '', None, '', None, (3, 0)), + ), + ) self.assertEqual( parseNumberPattern('###0.###;-###0.###'), - ((None, '', None, '###0', '###', '', None, '', None, ()), - (None, '-', None, '###0', '###', '', None, '', None, ()))) + ( + (None, '', None, '###0', '###', '', None, '', None, ()), + (None, '-', None, '###0', '###', '', None, '', None, ()), + ), + ) self.assertEqual( parseNumberPattern('###0.###;###0.###-'), - ((None, '', None, '###0', '###', '', None, '', None, ()), - (None, '', None, '###0', '###', '', None, '-', None, ()))) + ( + (None, '', None, '###0', '###', '', None, '', None, ()), + (None, '', None, '###0', '###', '', None, '-', None, ()), + ), + ) self.assertEqual( parseNumberPattern('#0.###;-#0.###'), - ((None, '', None, '#0', '###', '', None, '', None, ()), - (None, '-', None, '#0', '###', '', None, '', None, ()))) + ( + (None, '', None, '#0', '###', '', None, '', None, ()), + (None, '-', None, '#0', '###', '', None, '', None, ()), + ), + ) self.assertEqual( parseNumberPattern('#,##0.###;#,##0.###'), - ((None, '', None, '###0', '###', '', None, '', None, (3, 0)), - (None, '', None, '###0', '###', '', None, '', None, (3, 0)))) + ( + (None, '', None, '###0', '###', '', None, '', None, (3, 0)), + (None, '', None, '###0', '###', '', None, '', None, (3, 0)), + ), + ) self.assertEqual( parseNumberPattern('###0.###;-###0.###'), - ((None, '', None, '###0', '###', '', None, '', None, ()), - (None, '-', None, '###0', '###', '', None, '', None, ()))) + ( + (None, '', None, '###0', '###', '', None, '', None, ()), + (None, '-', None, '###0', '###', '', None, '', None, ()), + ), + ) self.assertEqual( parseNumberPattern('#,##0.00;-#,##0.00'), - ((None, '', None, '###0', '00', '', None, '', None, (3, 0)), - (None, '-', None, '###0', '00', '', None, '', None, (3, 0)))) + ( + (None, '', None, '###0', '00', '', None, '', None, (3, 0)), + (None, '-', None, '###0', '00', '', None, '', None, (3, 0)), + ), + ) self.assertEqual( parseNumberPattern('#,##0.00;(#,##0.00)'), - ((None, '', None, '###0', '00', '', None, '', None, (3, 0)), - (None, '(', None, '###0', '00', '', None, ')', None, (3, 0)))) + ( + (None, '', None, '###0', '00', '', None, '', None, (3, 0)), + (None, '(', None, '###0', '00', '', None, ')', None, (3, 0)), + ), + ) self.assertEqual( parseNumberPattern('#,##0.00;-#,##0.00'), - ((None, '', None, '###0', '00', '', None, '', None, (3, 0)), - (None, '-', None, '###0', '00', '', None, '', None, (3, 0)))) + ( + (None, '', None, '###0', '00', '', None, '', None, (3, 0)), + (None, '-', None, '###0', '00', '', None, '', None, (3, 0)), + ), + ) self.assertEqual( parseNumberPattern('##,##,##0.00;-##,##,##0.00'), - ((None, '', None, '######0', '00', '', None, '', None, (3, 2, 0)), - (None, '-', None, '######0', '00', '', None, '', None, (3, 2, 0)))) + ( + ( + None, + '', + None, + '######0', + '00', + '', + None, + '', + None, + (3, 2, 0), + ), + ( + None, + '-', + None, + '######0', + '00', + '', + None, + '', + None, + (3, 2, 0), + ), + ), + ) self.assertEqual( parseNumberPattern('###0.00;-###0.00'), - ((None, '', None, '###0', '00', '', None, '', None, ()), - (None, '-', None, '###0', '00', '', None, '', None, ()))) + ( + (None, '', None, '###0', '00', '', None, '', None, ()), + (None, '-', None, '###0', '00', '', None, '', None, ()), + ), + ) self.assertEqual( parseNumberPattern('#,##0;-#,##0'), - ((None, '', None, '###0', '', '', None, '', None, (3, 0)), - (None, '-', None, '###0', '', '', None, '', None, (3, 0)))) + ( + (None, '', None, '###0', '', '', None, '', None, (3, 0)), + (None, '-', None, '###0', '', '', None, '', None, (3, 0)), + ), + ) self.assertEqual( parseNumberPattern('###0.00;-###0.00'), - ((None, '', None, '###0', '00', '', None, '', None, ()), - (None, '-', None, '###0', '00', '', None, '', None, ()))) + ( + (None, '', None, '###0', '00', '', None, '', None, ()), + (None, '-', None, '###0', '00', '', None, '', None, ()), + ), + ) def testParsePadding1WithoutPrefixPattern(self): self.assertEqual( parseNumberPattern('* ###0'), - ((' ', '', None, '###0', '', '', None, '', None, ()), - (' ', '', None, '###0', '', '', None, '', None, ()))) + ( + (' ', '', None, '###0', '', '', None, '', None, ()), + (' ', '', None, '###0', '', '', None, '', None, ()), + ), + ) self.assertEqual( parseNumberPattern('* ###0.0##'), - ((' ', '', None, '###0', '0##', '', None, '', None, ()), - (' ', '', None, '###0', '0##', '', None, '', None, ()))) + ( + (' ', '', None, '###0', '0##', '', None, '', None, ()), + (' ', '', None, '###0', '0##', '', None, '', None, ()), + ), + ) self.assertEqual( parseNumberPattern('* ###0.0##;*_###0.0##'), - ((' ', '', None, '###0', '0##', '', None, '', None, ()), - ('_', '', None, '###0', '0##', '', None, '', None, ()))) + ( + (' ', '', None, '###0', '0##', '', None, '', None, ()), + ('_', '', None, '###0', '0##', '', None, '', None, ()), + ), + ) def testParsePadding1WithPrefixPattern(self): self.assertEqual( parseNumberPattern('* +###0'), - ((' ', '+', None, '###0', '', '', None, '', None, ()), - (' ', '+', None, '###0', '', '', None, '', None, ()))) + ( + (' ', '+', None, '###0', '', '', None, '', None, ()), + (' ', '+', None, '###0', '', '', None, '', None, ()), + ), + ) self.assertEqual( parseNumberPattern('* +###0.0##'), - ((' ', '+', None, '###0', '0##', '', None, '', None, ()), - (' ', '+', None, '###0', '0##', '', None, '', None, ()))) + ( + (' ', '+', None, '###0', '0##', '', None, '', None, ()), + (' ', '+', None, '###0', '0##', '', None, '', None, ()), + ), + ) self.assertEqual( parseNumberPattern('* +###0.0##;*_-###0.0##'), - ((' ', '+', None, '###0', '0##', '', None, '', None, ()), - ('_', '-', None, '###0', '0##', '', None, '', None, ()))) + ( + (' ', '+', None, '###0', '0##', '', None, '', None, ()), + ('_', '-', None, '###0', '0##', '', None, '', None, ()), + ), + ) def testParsePadding1Padding2WithPrefixPattern(self): self.assertEqual( parseNumberPattern('* +* ###0'), - ((' ', '+', ' ', '###0', '', '', None, '', None, ()), - (' ', '+', ' ', '###0', '', '', None, '', None, ()))) + ( + (' ', '+', ' ', '###0', '', '', None, '', None, ()), + (' ', '+', ' ', '###0', '', '', None, '', None, ()), + ), + ) self.assertEqual( parseNumberPattern('* +* ###0.0##'), - ((' ', '+', ' ', '###0', '0##', '', None, '', None, ()), - (' ', '+', ' ', '###0', '0##', '', None, '', None, ()))) + ( + (' ', '+', ' ', '###0', '0##', '', None, '', None, ()), + (' ', '+', ' ', '###0', '0##', '', None, '', None, ()), + ), + ) self.assertEqual( parseNumberPattern('* +* ###0.0##;*_-*_###0.0##'), - ((' ', '+', ' ', '###0', '0##', '', None, '', None, ()), - ('_', '-', '_', '###0', '0##', '', None, '', None, ()))) + ( + (' ', '+', ' ', '###0', '0##', '', None, '', None, ()), + ('_', '-', '_', '###0', '0##', '', None, '', None, ()), + ), + ) def testParsePadding3WithoutSuffixPattern(self): self.assertEqual( parseNumberPattern('###0* '), - ((None, '', None, '###0', '', '', ' ', '', None, ()), - (None, '', None, '###0', '', '', ' ', '', None, ()))) + ( + (None, '', None, '###0', '', '', ' ', '', None, ()), + (None, '', None, '###0', '', '', ' ', '', None, ()), + ), + ) self.assertEqual( parseNumberPattern('###0.0##* '), - ((None, '', None, '###0', '0##', '', ' ', '', None, ()), - (None, '', None, '###0', '0##', '', ' ', '', None, ()))) + ( + (None, '', None, '###0', '0##', '', ' ', '', None, ()), + (None, '', None, '###0', '0##', '', ' ', '', None, ()), + ), + ) self.assertEqual( parseNumberPattern('###0.0##* ;###0.0##*_'), - ((None, '', None, '###0', '0##', '', ' ', '', None, ()), - (None, '', None, '###0', '0##', '', '_', '', None, ()))) + ( + (None, '', None, '###0', '0##', '', ' ', '', None, ()), + (None, '', None, '###0', '0##', '', '_', '', None, ()), + ), + ) def testParsePadding3InScientificPattern(self): self.assertEqual( parseNumberPattern('###0E#0* '), - ((None, '', None, '###0', '', '#0', ' ', '', None, ()), - (None, '', None, '###0', '', '#0', ' ', '', None, ()))) + ( + (None, '', None, '###0', '', '#0', ' ', '', None, ()), + (None, '', None, '###0', '', '#0', ' ', '', None, ()), + ), + ) self.assertEqual( parseNumberPattern('###0.0##E0* '), - ((None, '', None, '###0', '0##', '0', ' ', '', None, ()), - (None, '', None, '###0', '0##', '0', ' ', '', None, ()))) + ( + (None, '', None, '###0', '0##', '0', ' ', '', None, ()), + (None, '', None, '###0', '0##', '0', ' ', '', None, ()), + ), + ) self.assertEqual( parseNumberPattern('###0.0##E#0* ;###0.0##E0*_'), - ((None, '', None, '###0', '0##', '#0', ' ', '', None, ()), - (None, '', None, '###0', '0##', '0', '_', '', None, ()))) + ( + (None, '', None, '###0', '0##', '#0', ' ', '', None, ()), + (None, '', None, '###0', '0##', '0', '_', '', None, ()), + ), + ) def testParsePadding3WithSufffixPattern(self): self.assertEqual( parseNumberPattern('###0* /'), - ((None, '', None, '###0', '', '', ' ', '/', None, ()), - (None, '', None, '###0', '', '', ' ', '/', None, ()))) + ( + (None, '', None, '###0', '', '', ' ', '/', None, ()), + (None, '', None, '###0', '', '', ' ', '/', None, ()), + ), + ) self.assertEqual( parseNumberPattern('###0.0#* /'), - ((None, '', None, '###0', '0#', '', ' ', '/', None, ()), - (None, '', None, '###0', '0#', '', ' ', '/', None, ()))) + ( + (None, '', None, '###0', '0#', '', ' ', '/', None, ()), + (None, '', None, '###0', '0#', '', ' ', '/', None, ()), + ), + ) self.assertEqual( parseNumberPattern('###0.0#* /;###0.0#*_/'), - ((None, '', None, '###0', '0#', '', ' ', '/', None, ()), - (None, '', None, '###0', '0#', '', '_', '/', None, ()))) + ( + (None, '', None, '###0', '0#', '', ' ', '/', None, ()), + (None, '', None, '###0', '0#', '', '_', '/', None, ()), + ), + ) def testParsePadding3And4WithSuffixPattern(self): self.assertEqual( parseNumberPattern('###0* /* '), - ((None, '', None, '###0', '', '', ' ', '/', ' ', ()), - (None, '', None, '###0', '', '', ' ', '/', ' ', ()))) + ( + (None, '', None, '###0', '', '', ' ', '/', ' ', ()), + (None, '', None, '###0', '', '', ' ', '/', ' ', ()), + ), + ) self.assertEqual( parseNumberPattern('###0* /* ;###0*_/*_'), - ((None, '', None, '###0', '', '', ' ', '/', ' ', ()), - (None, '', None, '###0', '', '', '_', '/', '_', ()))) + ( + (None, '', None, '###0', '', '', ' ', '/', ' ', ()), + (None, '', None, '###0', '', '', '_', '/', '_', ()), + ), + ) def testParseMultipleCharacterPrefix(self): self.assertEqual( parseNumberPattern('DM###0'), - ((None, 'DM', None, '###0', '', '', None, '', None, ()), - (None, 'DM', None, '###0', '', '', None, '', None, ()))) + ( + (None, 'DM', None, '###0', '', '', None, '', None, ()), + (None, 'DM', None, '###0', '', '', None, '', None, ()), + ), + ) self.assertEqual( parseNumberPattern('DM* ###0'), - ((None, 'DM', ' ', '###0', '', '', None, '', None, ()), - (None, 'DM', ' ', '###0', '', '', None, '', None, ()))) + ( + (None, 'DM', ' ', '###0', '', '', None, '', None, ()), + (None, 'DM', ' ', '###0', '', '', None, '', None, ()), + ), + ) def testParseStringEscapedPrefix(self): self.assertEqual( parseNumberPattern("'DEM'###0"), - ((None, 'DEM', None, '###0', '', '', None, '', None, ()), - (None, 'DEM', None, '###0', '', '', None, '', None, ()))) + ( + (None, 'DEM', None, '###0', '', '', None, '', None, ()), + (None, 'DEM', None, '###0', '', '', None, '', None, ()), + ), + ) self.assertEqual( parseNumberPattern("D'EM'###0"), - ((None, 'DEM', None, '###0', '', '', None, '', None, ()), - (None, 'DEM', None, '###0', '', '', None, '', None, ()))) + ( + (None, 'DEM', None, '###0', '', '', None, '', None, ()), + (None, 'DEM', None, '###0', '', '', None, '', None, ()), + ), + ) self.assertEqual( parseNumberPattern("D'E'M###0"), - ((None, 'DEM', None, '###0', '', '', None, '', None, ()), - (None, 'DEM', None, '###0', '', '', None, '', None, ()))) + ( + (None, 'DEM', None, '###0', '', '', None, '', None, ()), + (None, 'DEM', None, '###0', '', '', None, '', None, ()), + ), + ) def testParseStringEscapedSuffix(self): self.assertEqual( parseNumberPattern("###0'DEM'"), - ((None, '', None, '###0', '', '', None, 'DEM', None, ()), - (None, '', None, '###0', '', '', None, 'DEM', None, ()))) + ( + (None, '', None, '###0', '', '', None, 'DEM', None, ()), + (None, '', None, '###0', '', '', None, 'DEM', None, ()), + ), + ) self.assertEqual( parseNumberPattern("###0D'EM'"), - ((None, '', None, '###0', '', '', None, 'DEM', None, ()), - (None, '', None, '###0', '', '', None, 'DEM', None, ()))) + ( + (None, '', None, '###0', '', '', None, 'DEM', None, ()), + (None, '', None, '###0', '', '', None, 'DEM', None, ()), + ), + ) self.assertEqual( parseNumberPattern("###0D'E'M"), - ((None, '', None, '###0', '', '', None, 'DEM', None, ()), - (None, '', None, '###0', '', '', None, 'DEM', None, ()))) + ( + (None, '', None, '###0', '', '', None, 'DEM', None, ()), + (None, '', None, '###0', '', '', None, 'DEM', None, ()), + ), + ) def testParseInvalidBegin(self): - with self.assertRaisesRegex(NumberPatternParseError, - "Wrong syntax at beginning"): + with self.assertRaisesRegex( + NumberPatternParseError, "Wrong syntax at beginning" + ): parseNumberPattern(".") def testParseFractionQuote(self): pattern, neg_pattern = parseNumberPattern("0.'") self.assertEqual( - (None, '', None, '0', '', '', None, '', None, ()), - pattern) + (None, '', None, '0', '', '', None, '', None, ()), pattern + ) self.assertEqual( - (None, '', None, '0', '', '', None, '', None, ()), - neg_pattern) + (None, '', None, '0', '', '', None, '', None, ()), neg_pattern + ) def testParseExponentialQuote(self): pattern, neg_pattern = parseNumberPattern("0E'") self.assertEqual( - (None, '', None, '0', '', '', None, '', None, ()), - pattern) + (None, '', None, '0', '', '', None, '', None, ()), pattern + ) self.assertEqual( - (None, '', None, '0', '', '', None, '', None, ()), - neg_pattern) + (None, '', None, '0', '', '', None, '', None, ()), neg_pattern + ) def testParseExponentialNumber(self): pattern, neg_pattern = parseNumberPattern("0E1") self.assertEqual( - (None, '', None, '0', '', '', None, '1', None, ()), - pattern) + (None, '', None, '0', '', '', None, '1', None, ()), pattern + ) self.assertEqual( - (None, '', None, '0', '', '', None, '1', None, ()), - neg_pattern) + (None, '', None, '0', '', '', None, '1', None, ()), neg_pattern + ) class TestNumberFormat(_TestCase): """Test the functionality of an implmentation of the NumberFormat.""" - format = NumberFormat(symbols={ - 'decimal': '.', 'group': ',', 'list': ';', 'percentSign': '%', - 'nativeZeroDigit': '0', 'patternDigit': '#', 'plusSign': '+', - 'minusSign': '-', 'exponential': 'E', 'perMille': 'o/oo', - 'infinity': 'oo', 'nan': 'N/A'}) + format = NumberFormat( + symbols={ + 'decimal': '.', + 'group': ',', + 'list': ';', + 'percentSign': '%', + 'nativeZeroDigit': '0', + 'patternDigit': '#', + 'plusSign': '+', + 'minusSign': '-', + 'exponential': 'E', + 'perMille': 'o/oo', + 'infinity': 'oo', + 'nan': 'N/A', + } + ) def testInterfaceConformity(self): self.assertTrue(INumberFormat.providedBy(self.format)) def testParseSimpleInteger(self): - self.assertEqual(self.format.parse('23341', '###0'), - 23341) - self.assertEqual(self.format.parse('041', '#000'), - 41) + self.assertEqual(self.format.parse('23341', '###0'), 23341) + self.assertEqual(self.format.parse('041', '#000'), 41) def testParseScientificInteger(self): - self.assertEqual(self.format.parse('2.3341E4', '0.0###E0'), - 23341) - self.assertEqual(self.format.parse('4.100E01', '0.000##E00'), - 41) - self.assertEqual(self.format.parse('1E0', '0E0'), - 1) - self.assertEqual(self.format.parse('0E0', '0E0'), - 0) + self.assertEqual(self.format.parse('2.3341E4', '0.0###E0'), 23341) + self.assertEqual(self.format.parse('4.100E01', '0.000##E00'), 41) + self.assertEqual(self.format.parse('1E0', '0E0'), 1) + self.assertEqual(self.format.parse('0E0', '0E0'), 0) # This is a special case I found not working, but is used frequently # in the new LDML Locale files. - self.assertEqual(self.format.parse('2.3341E+04', '0.000###E+00'), - 23341) + self.assertEqual( + self.format.parse('2.3341E+04', '0.000###E+00'), 23341 + ) def testParsePosNegAlternativeInteger(self): - self.assertEqual(self.format.parse('23341', '#000;#00'), - 23341) - self.assertEqual(self.format.parse('041', '#000;#00'), - 41) - self.assertEqual(self.format.parse('41', '#000;#00'), - -41) - self.assertEqual(self.format.parse('01', '#000;#00'), - -1) + self.assertEqual(self.format.parse('23341', '#000;#00'), 23341) + self.assertEqual(self.format.parse('041', '#000;#00'), 41) + self.assertEqual(self.format.parse('41', '#000;#00'), -41) + self.assertEqual(self.format.parse('01', '#000;#00'), -1) def testParsePrefixedInteger(self): - self.assertEqual(self.format.parse('+23341', '+###0'), - 23341) - self.assertEqual(self.format.parse('+041', '+#000'), - 41) + self.assertEqual(self.format.parse('+23341', '+###0'), 23341) + self.assertEqual(self.format.parse('+041', '+#000'), 41) def testParsePosNegInteger(self): - self.assertEqual(self.format.parse('+23341', '+###0;-###0'), - 23341) - self.assertEqual(self.format.parse('+041', '+#000;-#000'), - 41) - self.assertEqual(self.format.parse('-23341', '+###0;-###0'), - -23341) - self.assertEqual(self.format.parse('-041', '+#000;-#000'), - -41) + self.assertEqual(self.format.parse('+23341', '+###0;-###0'), 23341) + self.assertEqual(self.format.parse('+041', '+#000;-#000'), 41) + self.assertEqual(self.format.parse('-23341', '+###0;-###0'), -23341) + self.assertEqual(self.format.parse('-041', '+#000;-#000'), -41) def testParseThousandSeparatorInteger(self): - self.assertEqual(self.format.parse('+23,341', '+#,##0;-#,##0'), - 23341) - self.assertEqual(self.format.parse('-23,341', '+#,##0;-#,##0'), - -23341) - self.assertEqual(self.format.parse('+0,041', '+#0,000;-#0,000'), - 41) - self.assertEqual(self.format.parse('-0,041', '+#0,000;-#0,000'), - -41) + self.assertEqual(self.format.parse('+23,341', '+#,##0;-#,##0'), 23341) + self.assertEqual(self.format.parse('-23,341', '+#,##0;-#,##0'), -23341) + self.assertEqual(self.format.parse('+0,041', '+#0,000;-#0,000'), 41) + self.assertEqual(self.format.parse('-0,041', '+#0,000;-#0,000'), -41) def testParseDecimal(self): - self.assertEqual(self.format.parse('23341.02', '###0.0#'), - 23341.02) - self.assertEqual(self.format.parse('23341.1', '###0.0#'), - 23341.1) - self.assertEqual(self.format.parse('23341.020', '###0.000#'), - 23341.02) + self.assertEqual(self.format.parse('23341.02', '###0.0#'), 23341.02) + self.assertEqual(self.format.parse('23341.1', '###0.0#'), 23341.1) + self.assertEqual(self.format.parse('23341.020', '###0.000#'), 23341.02) def testParseDecimalWithOptionalDecimalDigits(self): - self.assertEqual(self.format.parse('23341.02', '###0.##'), - 23341.02) - self.assertEqual(self.format.parse('23341', '###0.#'), - 23341.0) - self.assertEqual(self.format.parse('23341.', '###0.#'), - 23341.0) + self.assertEqual(self.format.parse('23341.02', '###0.##'), 23341.02) + self.assertEqual(self.format.parse('23341', '###0.#'), 23341.0) + self.assertEqual(self.format.parse('23341.', '###0.#'), 23341.0) def testParseScientificDecimal(self): - self.assertEqual(self.format.parse('2.334102E04', '0.00####E00'), - 23341.02) - self.assertEqual(self.format.parse('2.3341020E004', '0.0000000E000'), - 23341.02) - self.assertEqual(self.format.parse('0.0E0', '0.0#E0'), - 0.0) + self.assertEqual( + self.format.parse('2.334102E04', '0.00####E00'), 23341.02 + ) + self.assertEqual( + self.format.parse('2.3341020E004', '0.0000000E000'), 23341.02 + ) + self.assertEqual(self.format.parse('0.0E0', '0.0#E0'), 0.0) def testParseScientificDecimalSmallerOne(self): - self.assertEqual(self.format.parse('2.357E-02', '0.00####E00'), - 0.02357) - self.assertEqual(self.format.parse('2.0000E-02', '0.0000E00'), - 0.02) + self.assertEqual( + self.format.parse('2.357E-02', '0.00####E00'), 0.02357 + ) + self.assertEqual(self.format.parse('2.0000E-02', '0.0000E00'), 0.02) def testParsePadding1WithoutPrefix(self): - self.assertEqual(self.format.parse(' 41', '* ##0;*_##0'), - 41) - self.assertEqual(self.format.parse('_41', '* ##0;*_##0'), - -41) + self.assertEqual(self.format.parse(' 41', '* ##0;*_##0'), 41) + self.assertEqual(self.format.parse('_41', '* ##0;*_##0'), -41) def testParsePadding1WithPrefix(self): - self.assertEqual(self.format.parse(' +41', '* +##0;*_-##0'), - 41) - self.assertEqual(self.format.parse('_-41', '* +##0;*_-##0'), - -41) + self.assertEqual(self.format.parse(' +41', '* +##0;*_-##0'), 41) + self.assertEqual(self.format.parse('_-41', '* +##0;*_-##0'), -41) def testParsePadding1Padding2WithPrefix(self): - self.assertEqual(self.format.parse(' + 41', '* +* ###0;*_-*_###0'), - +41) - self.assertEqual(self.format.parse('__-_41', '* +* ###0;*_-*_###0'), - -41) + self.assertEqual( + self.format.parse(' + 41', '* +* ###0;*_-*_###0'), +41 + ) + self.assertEqual( + self.format.parse('__-_41', '* +* ###0;*_-*_###0'), -41 + ) def testParsePadding1Scientific(self): - self.assertEqual(self.format.parse(' 4.102E1', - '* 0.0####E0;*_0.0####E0'), - 41.02) - self.assertEqual(self.format.parse('__4.102E1', - '* 0.0####E0;*_0.0####E0'), - -41.02) - self.assertEqual(self.format.parse(' +4.102E1', - '* +0.0###E0;*_-0.0###E0'), - 41.02) - self.assertEqual(self.format.parse('_-4.102E1', - '* +0.0###E0;*_-0.0###E0'), - -41.02) + self.assertEqual( + self.format.parse(' 4.102E1', '* 0.0####E0;*_0.0####E0'), 41.02 + ) + self.assertEqual( + self.format.parse('__4.102E1', '* 0.0####E0;*_0.0####E0'), -41.02 + ) + self.assertEqual( + self.format.parse(' +4.102E1', '* +0.0###E0;*_-0.0###E0'), 41.02 + ) + self.assertEqual( + self.format.parse('_-4.102E1', '* +0.0###E0;*_-0.0###E0'), -41.02 + ) def testParsePadding3WithoutSufffix(self): - self.assertEqual(self.format.parse('41.02 ', '#0.0###* ;#0.0###*_'), - 41.02) - self.assertEqual(self.format.parse('41.02__', '#0.0###* ;#0.0###*_'), - -41.02) + self.assertEqual( + self.format.parse('41.02 ', '#0.0###* ;#0.0###*_'), 41.02 + ) + self.assertEqual( + self.format.parse('41.02__', '#0.0###* ;#0.0###*_'), -41.02 + ) def testParsePadding3WithSufffix(self): self.assertEqual( - self.format.parse('[41.02 ]', '[#0.0###* ];(#0.0###*_)'), - 41.02) + self.format.parse('[41.02 ]', '[#0.0###* ];(#0.0###*_)'), 41.02 + ) self.assertEqual( - self.format.parse('(41.02__)', '[#0.0###* ];(#0.0###*_)'), - -41.02) + self.format.parse('(41.02__)', '[#0.0###* ];(#0.0###*_)'), -41.02 + ) def testParsePadding3Scientific(self): - self.assertEqual(self.format.parse('4.102E1 ', - '0.0##E0##* ;0.0##E0##*_'), - 41.02) - self.assertEqual(self.format.parse('4.102E1__', - '0.0##E0##* ;0.0##E0##*_'), - -41.02) - self.assertEqual(self.format.parse('(4.102E1 )', - '(0.0##E0##* );0.0E0'), - 41.02) - self.assertEqual(self.format.parse('[4.102E1__]', - '0.0E0;[0.0##E0##*_]'), - -41.02) + self.assertEqual( + self.format.parse('4.102E1 ', '0.0##E0##* ;0.0##E0##*_'), 41.02 + ) + self.assertEqual( + self.format.parse('4.102E1__', '0.0##E0##* ;0.0##E0##*_'), -41.02 + ) + self.assertEqual( + self.format.parse('(4.102E1 )', '(0.0##E0##* );0.0E0'), 41.02 + ) + self.assertEqual( + self.format.parse('[4.102E1__]', '0.0E0;[0.0##E0##*_]'), -41.02 + ) def testParsePadding3Padding4WithSuffix(self): - self.assertEqual(self.format.parse('(41.02 ) ', '(#0.0###* )* '), - 41.02) - self.assertEqual(self.format.parse('(4.102E1 ) ', '(0.0##E0##* )* '), - 41.02) + self.assertEqual( + self.format.parse('(41.02 ) ', '(#0.0###* )* '), 41.02 + ) + self.assertEqual( + self.format.parse('(4.102E1 ) ', '(0.0##E0##* )* '), 41.02 + ) def testParseDecimalWithGermanDecimalSeparator(self): format = NumberFormat(symbols={'decimal': ',', 'group': '.'}) @@ -1210,7 +1575,8 @@ class TestNumberFormat(_TestCase): def testParseWithAlternativeExponentialSymbol(self): format = NumberFormat( - symbols={'decimal': '.', 'group': ',', 'exponential': 'X'}) + symbols={'decimal': '.', 'group': ',', 'exponential': 'X'} + ) self.assertEqual(format.parse('1.2X11', '#.#E0'), 1.2e11) def testParseFailWithInvalidCharacters(self): @@ -1232,112 +1598,94 @@ class TestNumberFormat(_TestCase): def testChangeOutputType(self): format = NumberFormat() format.type = decimal.Decimal - self.assertEqual(format.parse('23341', '###0'), - decimal.Decimal('23341')) - self.assertEqual(format.parse('233.41', '###0.00'), - decimal.Decimal('233.41')) + self.assertEqual( + format.parse('23341', '###0'), decimal.Decimal('23341') + ) + self.assertEqual( + format.parse('233.41', '###0.00'), decimal.Decimal('233.41') + ) def testFormatSimpleInteger(self): - self.assertEqual(self.format.format(23341, '###0'), - '23341') - self.assertEqual(self.format.format(41, '#000'), - '041') + self.assertEqual(self.format.format(23341, '###0'), '23341') + self.assertEqual(self.format.format(41, '#000'), '041') def testFormatScientificInteger(self): - self.assertEqual(self.format.format(23341, '0.000#E0'), - '2.3341E4') - self.assertEqual(self.format.format(23341, '0.000#E00'), - '2.3341E04') - self.assertEqual(self.format.format(1, '0.##E0'), - '1E0') - self.assertEqual(self.format.format(1, '0.00E00'), - '1.00E00') + self.assertEqual(self.format.format(23341, '0.000#E0'), '2.3341E4') + self.assertEqual(self.format.format(23341, '0.000#E00'), '2.3341E04') + self.assertEqual(self.format.format(1, '0.##E0'), '1E0') + self.assertEqual(self.format.format(1, '0.00E00'), '1.00E00') # This is a special case I found not working, but is used frequently # in the new LDML Locale files. - self.assertEqual(self.format.format(23341, '0.000###E+00'), - '2.3341E+04') + self.assertEqual( + self.format.format(23341, '0.000###E+00'), '2.3341E+04' + ) def testFormatScientificZero(self): - self.assertEqual(self.format.format(0, '0.00E00'), - '0.00E00') - self.assertEqual(self.format.format(0, '0E0'), - '0E0') + self.assertEqual(self.format.format(0, '0.00E00'), '0.00E00') + self.assertEqual(self.format.format(0, '0E0'), '0E0') def testFormatPosNegAlternativeInteger(self): - self.assertEqual(self.format.format(23341, '#000;#00'), - '23341') - self.assertEqual(self.format.format(41, '#000;#00'), - '041') - self.assertEqual(self.format.format(-23341, '#000;#00'), - '23341') - self.assertEqual(self.format.format(-41, '#000;#00'), - '41') - self.assertEqual(self.format.format(-1, '#000;#00'), - '01') + self.assertEqual(self.format.format(23341, '#000;#00'), '23341') + self.assertEqual(self.format.format(41, '#000;#00'), '041') + self.assertEqual(self.format.format(-23341, '#000;#00'), '23341') + self.assertEqual(self.format.format(-41, '#000;#00'), '41') + self.assertEqual(self.format.format(-1, '#000;#00'), '01') def testFormatPrefixedInteger(self): - self.assertEqual(self.format.format(23341, '+###0'), - '+23341') - self.assertEqual(self.format.format(41, '+#000'), - '+041') - self.assertEqual(self.format.format(-23341, '+###0'), - '+23341') - self.assertEqual(self.format.format(-41, '+#000'), - '+041') + self.assertEqual(self.format.format(23341, '+###0'), '+23341') + self.assertEqual(self.format.format(41, '+#000'), '+041') + self.assertEqual(self.format.format(-23341, '+###0'), '+23341') + self.assertEqual(self.format.format(-41, '+#000'), '+041') def testFormatPosNegInteger(self): - self.assertEqual(self.format.format(23341, '+###0;-###0'), - '+23341') - self.assertEqual(self.format.format(41, '+#000;-#000'), - '+041') - self.assertEqual(self.format.format(-23341, '+###0;-###0'), - '-23341') - self.assertEqual(self.format.format(-41, '+#000;-#000'), - '-041') + self.assertEqual(self.format.format(23341, '+###0;-###0'), '+23341') + self.assertEqual(self.format.format(41, '+#000;-#000'), '+041') + self.assertEqual(self.format.format(-23341, '+###0;-###0'), '-23341') + self.assertEqual(self.format.format(-41, '+#000;-#000'), '-041') def testFormatPosNegScientificInteger(self): - self.assertEqual(self.format.format(23341, '+0.00###E00;-0.00###E00'), - '+2.3341E04') - self.assertEqual(self.format.format(23341, '-0.00###E00;-0.00###E00'), - '-2.3341E04') - - def testFormatThousandSeparatorInteger(self): - self.assertEqual( - self.format.format(23341, '+#,##0;-#,##0'), - '+23,341') self.assertEqual( - self.format.format(-23341, '+#,##0;-#,##0'), - '-23,341') + self.format.format(23341, '+0.00###E00;-0.00###E00'), '+2.3341E04' + ) self.assertEqual( - self.format.format(41, '+#0,000;-#0,000'), - '+0,041') + self.format.format(23341, '-0.00###E00;-0.00###E00'), '-2.3341E04' + ) + + def testFormatThousandSeparatorInteger(self): + self.assertEqual(self.format.format(23341, '+#,##0;-#,##0'), '+23,341') self.assertEqual( - self.format.format(-41, '+#0,000;-#0,000'), - '-0,041') + self.format.format(-23341, '+#,##0;-#,##0'), '-23,341' + ) + self.assertEqual(self.format.format(41, '+#0,000;-#0,000'), '+0,041') + self.assertEqual(self.format.format(-41, '+#0,000;-#0,000'), '-0,041') self.assertEqual( - self.format.format(987654321, '+#,##0;-#,##0'), - '+987,654,321') + self.format.format(987654321, '+#,##0;-#,##0'), '+987,654,321' + ) self.assertEqual( - self.format.format(-987654321, '+#,##0;-#,##0'), - '-987,654,321') + self.format.format(-987654321, '+#,##0;-#,##0'), '-987,654,321' + ) self.assertEqual( self.format.format(987654321, '+#,##,#0,000;-#,##,#0,000'), - '+98,76,54,321') + '+98,76,54,321', + ) self.assertEqual( self.format.format(-987654321, '+#,##,#0,000;-#,##,#0,000'), - '-98,76,54,321') + '-98,76,54,321', + ) def testFormatBadThousandSeparator(self): - self.assertRaises(ValueError, - self.format.format, 23341, '0,') + self.assertRaises(ValueError, self.format.format, 23341, '0,') def testFormatDecimal(self): - self.assertEqual(self.format.format(23341.02357, '###0.0#'), - '23341.02') - self.assertEqual(self.format.format(23341.02357, '###0.000#'), - '23341.0236') - self.assertEqual(self.format.format(23341.02, '###0.000#'), - '23341.020') + self.assertEqual( + self.format.format(23341.02357, '###0.0#'), '23341.02' + ) + self.assertEqual( + self.format.format(23341.02357, '###0.000#'), '23341.0236' + ) + self.assertEqual( + self.format.format(23341.02, '###0.000#'), '23341.020' + ) def testRounding(self): self.assertEqual(self.format.format(0.5, '#'), '1') @@ -1348,110 +1696,126 @@ class TestNumberFormat(_TestCase): self.assertEqual(self.format.format(1.9999, '0.000'), '2.000') self.assertEqual(self.format.format(1.9999, '0.0000'), '1.9999') - def testFormatScientificDecimal(self): - self.assertEqual(self.format.format(23341.02357, '0.00####E00'), - '2.334102E04') - self.assertEqual(self.format.format(23341.02, '0.0000000E000'), - '2.3341020E004') + self.assertEqual( + self.format.format(23341.02357, '0.00####E00'), '2.334102E04' + ) + self.assertEqual( + self.format.format(23341.02, '0.0000000E000'), '2.3341020E004' + ) def testFormatScientificDecimalSmallerOne(self): - self.assertEqual(self.format.format(0.02357, '0.00####E00'), - '2.357E-02') - self.assertEqual(self.format.format(0.02, '0.0000E00'), - '2.0000E-02') + self.assertEqual( + self.format.format(0.02357, '0.00####E00'), '2.357E-02' + ) + self.assertEqual(self.format.format(0.02, '0.0000E00'), '2.0000E-02') def testFormatPadding1WithoutPrefix(self): - self.assertEqual(self.format.format(41, '* ##0;*_##0'), - ' 41') - self.assertEqual(self.format.format(-41, '* ##0;*_##0'), - '_41') + self.assertEqual(self.format.format(41, '* ##0;*_##0'), ' 41') + self.assertEqual(self.format.format(-41, '* ##0;*_##0'), '_41') def testFormatPadding1WithPrefix(self): - self.assertEqual(self.format.format(41, '* +##0;*_-##0'), - ' +41') - self.assertEqual(self.format.format(-41, '* +##0;*_-##0'), - '_-41') + self.assertEqual(self.format.format(41, '* +##0;*_-##0'), ' +41') + self.assertEqual(self.format.format(-41, '* +##0;*_-##0'), '_-41') def testFormatPadding1Scientific(self): - self.assertEqual(self.format.format(41.02, '* 0.0####E0;*_0.0####E0'), - ' 4.102E1') - self.assertEqual(self.format.format(-41.02, '* 0.0####E0;*_0.0####E0'), - '__4.102E1') - self.assertEqual(self.format.format(41.02, '* +0.0###E0;*_-0.0###E0'), - ' +4.102E1') - self.assertEqual(self.format.format(-41.02, '* +0.0###E0;*_-0.0###E0'), - '_-4.102E1') + self.assertEqual( + self.format.format(41.02, '* 0.0####E0;*_0.0####E0'), ' 4.102E1' + ) + self.assertEqual( + self.format.format(-41.02, '* 0.0####E0;*_0.0####E0'), '__4.102E1' + ) + self.assertEqual( + self.format.format(41.02, '* +0.0###E0;*_-0.0###E0'), ' +4.102E1' + ) + self.assertEqual( + self.format.format(-41.02, '* +0.0###E0;*_-0.0###E0'), '_-4.102E1' + ) def testFormatPadding1Padding2WithPrefix(self): - self.assertEqual(self.format.format(41, '* +* ###0;*_-*_###0'), - ' + 41') - self.assertEqual(self.format.format(-41, '* +* ###0;*_-*_###0'), - '__-_41') + self.assertEqual( + self.format.format(41, '* +* ###0;*_-*_###0'), ' + 41' + ) + self.assertEqual( + self.format.format(-41, '* +* ###0;*_-*_###0'), '__-_41' + ) def testFormatPadding3WithoutSufffix(self): - self.assertEqual(self.format.format(41.02, '#0.0###* ;#0.0###*_'), - '41.02 ') - self.assertEqual(self.format.format(-41.02, '#0.0###* ;#0.0###*_'), - '41.02__') + self.assertEqual( + self.format.format(41.02, '#0.0###* ;#0.0###*_'), '41.02 ' + ) + self.assertEqual( + self.format.format(-41.02, '#0.0###* ;#0.0###*_'), '41.02__' + ) def testFormatPadding3WithSufffix(self): - self.assertEqual(self.format.format(41.02, '[#0.0###* ];(#0.0###*_)'), - '[41.02 ]') - self.assertEqual(self.format.format(-41.02, '[#0.0###* ];(#0.0###*_)'), - '(41.02__)') + self.assertEqual( + self.format.format(41.02, '[#0.0###* ];(#0.0###*_)'), '[41.02 ]' + ) + self.assertEqual( + self.format.format(-41.02, '[#0.0###* ];(#0.0###*_)'), '(41.02__)' + ) def testFormatPadding3Scientific(self): - self.assertEqual(self.format.format(41.02, '0.0##E0##* ;0.0##E0##*_'), - '4.102E1 ') - self.assertEqual(self.format.format(-41.02, '0.0##E0##* ;0.0##E0##*_'), - '4.102E1__') - self.assertEqual(self.format.format(41.02, '(0.0##E0##* );0.0E0'), - '(4.102E1 )') - self.assertEqual(self.format.format(-41.02, '0.0E0;[0.0##E0##*_]'), - '[4.102E1__]') + self.assertEqual( + self.format.format(41.02, '0.0##E0##* ;0.0##E0##*_'), '4.102E1 ' + ) + self.assertEqual( + self.format.format(-41.02, '0.0##E0##* ;0.0##E0##*_'), '4.102E1__' + ) + self.assertEqual( + self.format.format(41.02, '(0.0##E0##* );0.0E0'), '(4.102E1 )' + ) + self.assertEqual( + self.format.format(-41.02, '0.0E0;[0.0##E0##*_]'), '[4.102E1__]' + ) def testFormatPadding3Padding4WithSuffix(self): - self.assertEqual(self.format.format(41.02, '(#0.0###* )* '), - '(41.02 ) ') - self.assertEqual(self.format.format(41.02, '(0.0##E0##* )* '), - '(4.102E1 ) ') + self.assertEqual( + self.format.format(41.02, '(#0.0###* )* '), '(41.02 ) ' + ) + self.assertEqual( + self.format.format(41.02, '(0.0##E0##* )* '), '(4.102E1 ) ' + ) def testFormatSmallNumbers(self): - self.assertEqual(self.format.format( - -1e-7, '(#0.00#####);(-#0.00#####)'), '(-0.0000001)') + self.assertEqual( + self.format.format(-1e-7, '(#0.00#####);(-#0.00#####)'), + '(-0.0000001)', + ) self.assertEqual(self.format.format(1e-9, '(#0.00###)'), '(0.00)') self.assertEqual(self.format.format(1e-9, '(#0.00###)'), '(0.00)') def testFormatHighPrecisionNumbers(self): self.assertEqual( - self.format.format( - 1+1e-7, '(#0.00#####);(-#0.00#####)'), - '(1.0000001)') + self.format.format(1 + 1e-7, '(#0.00#####);(-#0.00#####)'), + '(1.0000001)', + ) self.assertEqual( - self.format.format( - 1+1e-7, '(#0.00###)'), - '(1.00000)') + self.format.format(1 + 1e-7, '(#0.00###)'), '(1.00000)' + ) self.assertEqual( - self.format.format( - 1+1e-9, '(#0.00#######);(-#0.00#######)'), - '(1.000000001)') + self.format.format(1 + 1e-9, '(#0.00#######);(-#0.00#######)'), + '(1.000000001)', + ) self.assertEqual( - self.format.format( - 1+1e-9, '(#0.00###)'), - '(1.00000)') + self.format.format(1 + 1e-9, '(#0.00###)'), '(1.00000)' + ) self.assertEqual( self.format.format( - 1+1e-12, '(#0.00##########);(-#0.00##########)'), - '(1.000000000001)') + 1 + 1e-12, '(#0.00##########);(-#0.00##########)' + ), + '(1.000000000001)', + ) self.assertEqual( - self.format.format( - 1+1e-12, '(#0.00###)'), - '(1.00000)') + self.format.format(1 + 1e-12, '(#0.00###)'), '(1.00000)' + ) def testNoRounding(self): # Witout Rounding self.assertEqual( self.format.format( - decimal.Decimal('0.99999'), '0.###', rounding=False), - '0.99999') + decimal.Decimal('0.99999'), '0.###', rounding=False + ), + '0.99999', + ) diff --git a/src/zope/i18n/tests/test_gettextmessagecatalog.py b/src/zope/i18n/tests/test_gettextmessagecatalog.py index 07a0932..3160c97 100644 --- a/src/zope/i18n/tests/test_gettextmessagecatalog.py +++ b/src/zope/i18n/tests/test_gettextmessagecatalog.py @@ -19,14 +19,13 @@ from zope.i18n.tests import test_imessagecatalog class GettextMessageCatalogTest(test_imessagecatalog.TestIMessageCatalog): - def _getMessageCatalog(self): from zope.i18n import tests + path = os.path.dirname(tests.__file__) self._path = os.path.join(path, 'en-default.mo') catalog = GettextMessageCatalog('en', 'default', self._path) return catalog - def _getUniqueIndentifier(self): return self._path diff --git a/src/zope/i18n/tests/test_imessagecatalog.py b/src/zope/i18n/tests/test_imessagecatalog.py index 803d4f0..36fb43c 100644 --- a/src/zope/i18n/tests/test_imessagecatalog.py +++ b/src/zope/i18n/tests/test_imessagecatalog.py @@ -21,7 +21,6 @@ from zope.schema import getValidationErrors class TestIMessageCatalog(unittest.TestCase): - # This should be overridden by every class that inherits this test def _getMessageCatalog(self): raise NotImplementedError() @@ -29,7 +28,6 @@ class TestIMessageCatalog(unittest.TestCase): def _getUniqueIndentifier(self): raise NotImplementedError() - def setUp(self): self._catalog = self._getMessageCatalog() @@ -63,4 +61,4 @@ class TestIMessageCatalog(unittest.TestCase): def test_suite(): - return unittest.TestSuite() # Deliberately empty + return unittest.TestSuite() # Deliberately empty diff --git a/src/zope/i18n/tests/test_itranslationdomain.py b/src/zope/i18n/tests/test_itranslationdomain.py index 79c385f..e061d87 100644 --- a/src/zope/i18n/tests/test_itranslationdomain.py +++ b/src/zope/i18n/tests/test_itranslationdomain.py @@ -28,16 +28,16 @@ from zope.i18n.interfaces import ITranslationDomain text_type = str if bytes is not str else unicode + @implementer(IUserPreferredLanguages) class Environment(object): - - def __init__(self, langs=()): self.langs = langs def getPreferredLanguages(self): return self.langs + class TestITranslationDomain(PlacelessSetup): # This should be overwritten by every class that inherits this test @@ -70,25 +70,40 @@ class TestITranslationDomain(PlacelessSetup): translate = self._domain.translate eq = self.assertEqual # Testing both translation and interpolation - eq(translate('greeting', mapping={'name': 'Stephan'}, - target_language='de'), - 'Hallo Stephan, wie geht es Dir?') + eq( + translate( + 'greeting', mapping={'name': 'Stephan'}, target_language='de' + ), + 'Hallo Stephan, wie geht es Dir?', + ) # Testing default value interpolation - eq(translate('greeting', mapping={'name': 'Philipp'}, - target_language='fr', - default="Hello $name, how are you?"), - 'Hello Philipp, how are you?') + eq( + translate( + 'greeting', + mapping={'name': 'Philipp'}, + target_language='fr', + default="Hello $name, how are you?", + ), + 'Hello Philipp, how are you?', + ) def testNoTranslation(self): translate = self._domain.translate eq = self.assertEqual # Verify that an unknown message id will end up not being translated - eq(translate('glorp_smurf_hmpf', target_language='en'), - 'glorp_smurf_hmpf') + eq( + translate('glorp_smurf_hmpf', target_language='en'), + 'glorp_smurf_hmpf', + ) # Test default value behaviour - eq(translate('glorp_smurf_hmpf', target_language='en', - default='Glorp Smurf Hmpf'), - 'Glorp Smurf Hmpf') + eq( + translate( + 'glorp_smurf_hmpf', + target_language='en', + default='Glorp Smurf Hmpf', + ), + 'Glorp Smurf Hmpf', + ) def testUnicodeDefaultValue(self): translate = self._domain.translate @@ -100,7 +115,7 @@ class TestITranslationDomain(PlacelessSetup): translate = self._domain.translate eq = self.assertEqual # Test that default is returned when no language can be negotiated - context = Environment(('xx', )) + context = Environment(('xx',)) eq(translate('short_greeting', context=context, default=42), 42) # Test that default is returned when there's no destination language @@ -108,4 +123,4 @@ class TestITranslationDomain(PlacelessSetup): def test_suite(): - return unittest.TestSuite() # Deliberately empty + return unittest.TestSuite() # Deliberately empty diff --git a/src/zope/i18n/tests/test_negotiator.py b/src/zope/i18n/tests/test_negotiator.py index f8a0336..d78218e 100644 --- a/src/zope/i18n/tests/test_negotiator.py +++ b/src/zope/i18n/tests/test_negotiator.py @@ -20,9 +20,9 @@ from zope.i18n.interfaces import IUserPreferredLanguages from zope.component.testing import PlacelessSetup from zope.interface import implementer + @implementer(IUserPreferredLanguages) class Env(object): - def __init__(self, langs=()): self.langs = langs @@ -31,7 +31,6 @@ class Env(object): class NegotiatorTest(PlacelessSetup, unittest.TestCase): - def setUp(self): super(NegotiatorTest, self).setUp() self.negotiator = Negotiator() @@ -39,23 +38,23 @@ class NegotiatorTest(PlacelessSetup, unittest.TestCase): def test_findLanguages(self): _cases = ( - (('en','de'), ('en','de','fr'), 'en'), - (('en'), ('it','de','fr'), None), - (('pt-br','de'), ('pt_BR','de','fr'), 'pt_BR'), - (('pt-br','en'), ('pt', 'en', 'fr'), 'pt'), - (('pt-br','en-us', 'de'), ('de', 'en', 'fr'), 'en'), - ) + (('en', 'de'), ('en', 'de', 'fr'), 'en'), + (('en'), ('it', 'de', 'fr'), None), + (('pt-br', 'de'), ('pt_BR', 'de', 'fr'), 'pt_BR'), + (('pt-br', 'en'), ('pt', 'en', 'fr'), 'pt'), + (('pt-br', 'en-us', 'de'), ('de', 'en', 'fr'), 'en'), + ) for user_pref_langs, obj_langs, expected in _cases: env = Env(user_pref_langs) - self.assertEqual(self.negotiator.getLanguage(obj_langs, env), - expected) + self.assertEqual( + self.negotiator.getLanguage(obj_langs, env), expected + ) def test_suite(): - return unittest.TestSuite(( - unittest.makeSuite(NegotiatorTest), - )) + return unittest.TestSuite((unittest.makeSuite(NegotiatorTest),)) + if __name__ == '__main__': unittest.main(defaultTest='test_suite') diff --git a/src/zope/i18n/tests/test_plurals.py b/src/zope/i18n/tests/test_plurals.py index d4f57c7..76f1823 100644 --- a/src/zope/i18n/tests/test_plurals.py +++ b/src/zope/i18n/tests/test_plurals.py @@ -26,7 +26,6 @@ from zope.i18n.interfaces import ITranslationDomain class TestPlurals(unittest.TestCase): - def _getMessageCatalog(self, locale, variant="default"): path = os.path.dirname(tests.__file__) self._path = os.path.join(path, '%s-%s.mo' % (locale, variant)) @@ -47,21 +46,32 @@ class TestPlurals(unittest.TestCase): self.assertEqual( catalog.queryPluralMessage( - 'One apple', '%d apples', 0, - dft1='One fruit', dft2='%d fruits'), - '0 fruits') + 'One apple', '%d apples', 0, dft1='One fruit', dft2='%d fruits' + ), + '0 fruits', + ) self.assertEqual( catalog.queryPluralMessage( - 'One apple.', '%d apples.', 1, - dft1='One fruit', dft2='%d fruits'), - 'One fruit') + 'One apple.', + '%d apples.', + 1, + dft1='One fruit', + dft2='%d fruits', + ), + 'One fruit', + ) self.assertEqual( catalog.queryPluralMessage( - 'One apple.', '%d apples.', 2, - dft1='One fruit', dft2='%d fruits'), - '2 fruits') + 'One apple.', + '%d apples.', + 2, + dft1='One fruit', + dft2='%d fruits', + ), + '2 fruits', + ) def test_missing_getPluralMessage(self): catalog = self._getMessageCatalog('en') @@ -83,37 +93,65 @@ class TestPlurals(unittest.TestCase): catalog = self._getMessageCatalog('de') self.assertEqual(catalog.language, 'de') - self.assertEqual(catalog.getPluralMessage( - 'There is one file.', 'There are %d files.', 1), - 'Es gibt eine Datei.') - self.assertEqual(catalog.getPluralMessage( - 'There is one file.', 'There are %d files.', 3), - 'Es gibt 3 Dateien.') - self.assertEqual(catalog.getPluralMessage( - 'There is one file.', 'There are %d files.', 0), - 'Es gibt 0 Dateien.') + self.assertEqual( + catalog.getPluralMessage( + 'There is one file.', 'There are %d files.', 1 + ), + 'Es gibt eine Datei.', + ) + self.assertEqual( + catalog.getPluralMessage( + 'There is one file.', 'There are %d files.', 3 + ), + 'Es gibt 3 Dateien.', + ) + self.assertEqual( + catalog.getPluralMessage( + 'There is one file.', 'There are %d files.', 0 + ), + 'Es gibt 0 Dateien.', + ) # Unknown id - self.assertRaises(KeyError, catalog.getPluralMessage, - 'There are %d files.', 'bar', 6) + self.assertRaises( + KeyError, catalog.getPluralMessage, 'There are %d files.', 'bar', 6 + ) # Query without default values - self.assertEqual(catalog.queryPluralMessage( - 'There is one file.', 'There are %d files.', 1), - 'Es gibt eine Datei.') - self.assertEqual(catalog.queryPluralMessage( - 'There is one file.', 'There are %d files.', 3), - 'Es gibt 3 Dateien.') + self.assertEqual( + catalog.queryPluralMessage( + 'There is one file.', 'There are %d files.', 1 + ), + 'Es gibt eine Datei.', + ) + self.assertEqual( + catalog.queryPluralMessage( + 'There is one file.', 'There are %d files.', 3 + ), + 'Es gibt 3 Dateien.', + ) # Query with default values - self.assertEqual(catalog.queryPluralMessage( - 'There are %d files.', 'There is one file.', 1, - 'Es gibt 1 Datei.', 'Es gibt %d Dateien !', ), - 'Es gibt 1 Datei.') - self.assertEqual(catalog.queryPluralMessage( - 'There are %d files.', 'There is one file.', 3, - 'Es gibt 1 Datei.', 'Es gibt %d Dateien !', ), - 'Es gibt 3 Dateien !') + self.assertEqual( + catalog.queryPluralMessage( + 'There are %d files.', + 'There is one file.', + 1, + 'Es gibt 1 Datei.', + 'Es gibt %d Dateien !', + ), + 'Es gibt 1 Datei.', + ) + self.assertEqual( + catalog.queryPluralMessage( + 'There are %d files.', + 'There is one file.', + 3, + 'Es gibt 1 Datei.', + 'Es gibt %d Dateien !', + ), + 'Es gibt 3 Dateien !', + ) def test_PolishPlurals(self): """Polish has a complex rule for plurals. It makes for a good @@ -122,24 +160,42 @@ class TestPlurals(unittest.TestCase): catalog = self._getMessageCatalog('pl') self.assertEqual(catalog.language, 'pl') - self.assertEqual(catalog.getPluralMessage( - 'There is one file.', 'There are %d files.', 0), - u"Istnieją 0 plików.") - self.assertEqual(catalog.getPluralMessage( - 'There is one file.', 'There are %d files.', 1), - u"Istnieje 1 plik.") - self.assertEqual(catalog.getPluralMessage( - 'There is one file.', 'There are %d files.', 3), - u"Istnieją 3 pliki.") - self.assertEqual(catalog.getPluralMessage( - 'There is one file.', 'There are %d files.', 17), - u"Istnieją 17 plików.") - self.assertEqual(catalog.getPluralMessage( - 'There is one file.', 'There are %d files.', 23), - u"Istnieją 23 pliki.") - self.assertEqual(catalog.getPluralMessage( - 'There is one file.', 'There are %d files.', 28), - u"Istnieją 28 plików.") + self.assertEqual( + catalog.getPluralMessage( + 'There is one file.', 'There are %d files.', 0 + ), + u"Istnieją 0 plików.", + ) + self.assertEqual( + catalog.getPluralMessage( + 'There is one file.', 'There are %d files.', 1 + ), + u"Istnieje 1 plik.", + ) + self.assertEqual( + catalog.getPluralMessage( + 'There is one file.', 'There are %d files.', 3 + ), + u"Istnieją 3 pliki.", + ) + self.assertEqual( + catalog.getPluralMessage( + 'There is one file.', 'There are %d files.', 17 + ), + u"Istnieją 17 plików.", + ) + self.assertEqual( + catalog.getPluralMessage( + 'There is one file.', 'There are %d files.', 23 + ), + u"Istnieją 23 pliki.", + ) + self.assertEqual( + catalog.getPluralMessage( + 'There is one file.', 'There are %d files.', 28 + ), + u"Istnieją 28 plików.", + ) def test_floater(self): """Test with the number being a float. @@ -149,101 +205,175 @@ class TestPlurals(unittest.TestCase): self.assertEqual(catalog.language, 'en') # It's cast to integer because of the %d in the translation string. - self.assertEqual(catalog.getPluralMessage( - 'There is one file.', 'There are %d files.', 1.0), - 'There is one file.') - self.assertEqual(catalog.getPluralMessage( - 'There is one file.', 'There are %d files.', 3.5), - 'There are 3 files.') + self.assertEqual( + catalog.getPluralMessage( + 'There is one file.', 'There are %d files.', 1.0 + ), + 'There is one file.', + ) + self.assertEqual( + catalog.getPluralMessage( + 'There is one file.', 'There are %d files.', 3.5 + ), + 'There are 3 files.', + ) # It's cast to a string because of the %s in the translation string. - self.assertEqual(catalog.getPluralMessage( - 'The item is rated 1/5 star.', - 'The item is rated %s/5 stars.', 3.5), - 'The item is rated 3.5/5 stars.') + self.assertEqual( + catalog.getPluralMessage( + 'The item is rated 1/5 star.', + 'The item is rated %s/5 stars.', + 3.5, + ), + 'The item is rated 3.5/5 stars.', + ) # It's cast either to an int or a float because of the %s in # the translation string. - self.assertEqual(catalog.getPluralMessage( - 'There is %d chance.', - 'There are %f chances.', 1.5), - 'There are 1.500000 chances.') - self.assertEqual(catalog.getPluralMessage( - 'There is %d chance.', - 'There are %f chances.', 3.5), - 'There are 3.500000 chances.') + self.assertEqual( + catalog.getPluralMessage( + 'There is %d chance.', 'There are %f chances.', 1.5 + ), + 'There are 1.500000 chances.', + ) + self.assertEqual( + catalog.getPluralMessage( + 'There is %d chance.', 'There are %f chances.', 3.5 + ), + 'There are 3.500000 chances.', + ) def test_translate_without_defaults(self): domain = self._getTranslationDomain('en') zope.component.provideUtility(domain, ITranslationDomain, 'default') self.assertEqual( - translate('One apple', domain='default', - msgid_plural='%d apples', number=0), - '0 apples') + translate( + 'One apple', + domain='default', + msgid_plural='%d apples', + number=0, + ), + '0 apples', + ) self.assertEqual( - translate('One apple', domain='default', - msgid_plural='%d apples', number=1), - 'One apple') + translate( + 'One apple', + domain='default', + msgid_plural='%d apples', + number=1, + ), + 'One apple', + ) self.assertEqual( - translate('One apple', domain='default', - msgid_plural='%d apples', number=2), - '2 apples') + translate( + 'One apple', + domain='default', + msgid_plural='%d apples', + number=2, + ), + '2 apples', + ) def test_translate_with_defaults(self): domain = self._getTranslationDomain('en') zope.component.provideUtility(domain, ITranslationDomain, 'default') self.assertEqual( - translate('One apple', domain='default', - msgid_plural='%d apples', number=0, - default='One fruit', default_plural='%d fruits'), - '0 fruits') + translate( + 'One apple', + domain='default', + msgid_plural='%d apples', + number=0, + default='One fruit', + default_plural='%d fruits', + ), + '0 fruits', + ) self.assertEqual( - translate('One apple', domain='default', - msgid_plural='%d apples', number=1, - default='One fruit', default_plural='%d fruits'), - 'One fruit') + translate( + 'One apple', + domain='default', + msgid_plural='%d apples', + number=1, + default='One fruit', + default_plural='%d fruits', + ), + 'One fruit', + ) self.assertEqual( - translate('One apple', domain='default', - msgid_plural='%d apples', number=2, - default='One fruit', default_plural='%d fruits'), - '2 fruits') + translate( + 'One apple', + domain='default', + msgid_plural='%d apples', + number=2, + default='One fruit', + default_plural='%d fruits', + ), + '2 fruits', + ) def test_translate_message_without_defaults(self): domain = self._getTranslationDomain('en') factory = MessageFactory('default') zope.component.provideUtility(domain, ITranslationDomain, 'default') self.assertEqual( - translate(factory('One apple', msgid_plural='%d apples', - number=0)), - '0 apples') + translate( + factory('One apple', msgid_plural='%d apples', number=0) + ), + '0 apples', + ) self.assertEqual( - translate(factory('One apple', msgid_plural='%d apples', - number=1)), - 'One apple') + translate( + factory('One apple', msgid_plural='%d apples', number=1) + ), + 'One apple', + ) self.assertEqual( - translate(factory('One apple', msgid_plural='%d apples', - number=2)), - '2 apples') + translate( + factory('One apple', msgid_plural='%d apples', number=2) + ), + '2 apples', + ) def test_translate_message_with_defaults(self): domain = self._getTranslationDomain('en') factory = MessageFactory('default') zope.component.provideUtility(domain, ITranslationDomain, 'default') self.assertEqual( - translate(factory('One apple', msgid_plural='%d apples', number=0, - default='One fruit', - default_plural='%d fruits')), - '0 fruits') + translate( + factory( + 'One apple', + msgid_plural='%d apples', + number=0, + default='One fruit', + default_plural='%d fruits', + ) + ), + '0 fruits', + ) self.assertEqual( - translate(factory('One apple', msgid_plural='%d apples', number=1, - default='One fruit', - default_plural='%d fruits')), - 'One fruit') + translate( + factory( + 'One apple', + msgid_plural='%d apples', + number=1, + default='One fruit', + default_plural='%d fruits', + ) + ), + 'One fruit', + ) self.assertEqual( - translate(factory('One apple', msgid_plural='%d apples', number=2, - default='One fruit', - default_plural='%d fruits')), - '2 fruits') + translate( + factory( + 'One apple', + msgid_plural='%d apples', + number=2, + default='One fruit', + default_plural='%d fruits', + ) + ), + '2 fruits', + ) def test_translate_recursive(self): domain = self._getTranslationDomain('en') @@ -251,30 +381,42 @@ class TestPlurals(unittest.TestCase): # Singular banana = factory('banana', msgid_plural='bananas', number=1) - phrase = factory('There is %d ${type}.', - msgid_plural='There are %d ${type}.', - number=1, mapping={'type': banana}) + phrase = factory( + 'There is %d ${type}.', + msgid_plural='There are %d ${type}.', + number=1, + mapping={'type': banana}, + ) self.assertEqual( domain.translate(phrase, target_language="en"), - 'There is 1 banana.') + 'There is 1 banana.', + ) # Plural apple = factory('apple', msgid_plural='apples', number=10) - phrase = factory('There is %d ${type}.', - msgid_plural='There are %d ${type}.', - number=10, mapping={'type': apple}) + phrase = factory( + 'There is %d ${type}.', + msgid_plural='There are %d ${type}.', + number=10, + mapping={'type': apple}, + ) self.assertEqual( domain.translate(phrase, target_language="en"), - 'There are 10 apples.') + 'There are 10 apples.', + ) # Straight translation with translatable mapping apple = factory('apple', msgid_plural='apples', number=75) self.assertEqual( - domain.translate(msgid='There is %d ${type}.', - msgid_plural='There are %d ${type}.', - mapping={'type': apple}, - target_language="en", number=75), - 'There are 75 apples.') + domain.translate( + msgid='There is %d ${type}.', + msgid_plural='There are %d ${type}.', + mapping={'type': apple}, + target_language="en", + number=75, + ), + 'There are 75 apples.', + ) # Add another catalog, to test the domain's catalogs iteration # We add this catalog in first position, to resolve the translations @@ -285,8 +427,12 @@ class TestPlurals(unittest.TestCase): apple = factory('apple', msgid_plural='apples', number=42) self.assertEqual( - domain.translate(msgid='There is %d ${type}.', - msgid_plural='There are %d ${type}.', - mapping={'type': apple}, - target_language="de", number=42), - 'There are 42 oranges.') + domain.translate( + msgid='There is %d ${type}.', + msgid_plural='There are %d ${type}.', + mapping={'type': apple}, + target_language="de", + number=42, + ), + 'There are 42 oranges.', + ) diff --git a/src/zope/i18n/tests/test_simpletranslationdomain.py b/src/zope/i18n/tests/test_simpletranslationdomain.py index 7c87c40..eabf2a1 100644 --- a/src/zope/i18n/tests/test_simpletranslationdomain.py +++ b/src/zope/i18n/tests/test_simpletranslationdomain.py @@ -22,11 +22,11 @@ data = { ('en', 'short_greeting'): 'Hello!', ('de', 'short_greeting'): 'Hallo!', ('en', 'greeting'): 'Hello $name, how are you?', - ('de', 'greeting'): 'Hallo $name, wie geht es Dir?'} + ('de', 'greeting'): 'Hallo $name, wie geht es Dir?', +} class TestSimpleTranslationDomain(unittest.TestCase, TestITranslationDomain): - def setUp(self): TestITranslationDomain.setUp(self) diff --git a/src/zope/i18n/tests/test_testmessagecatalog.py b/src/zope/i18n/tests/test_testmessagecatalog.py index ce5adc6..e90ac1a 100644 --- a/src/zope/i18n/tests/test_testmessagecatalog.py +++ b/src/zope/i18n/tests/test_testmessagecatalog.py @@ -15,10 +15,12 @@ import unittest import doctest + def test_suite(): - return unittest.TestSuite(( - doctest.DocFileSuite('../testmessagecatalog.rst') - )) + return unittest.TestSuite( + (doctest.DocFileSuite('../testmessagecatalog.rst')) + ) + if __name__ == '__main__': unittest.main(defaultTest='test_suite') diff --git a/src/zope/i18n/tests/test_translationdomain.py b/src/zope/i18n/tests/test_translationdomain.py index f8efbd9..4687107 100644 --- a/src/zope/i18n/tests/test_translationdomain.py +++ b/src/zope/i18n/tests/test_translationdomain.py @@ -17,8 +17,10 @@ import unittest import os from zope.i18n.translationdomain import TranslationDomain from zope.i18n.gettextmessagecatalog import GettextMessageCatalog -from zope.i18n.tests.test_itranslationdomain import \ - TestITranslationDomain, Environment +from zope.i18n.tests.test_itranslationdomain import ( + TestITranslationDomain, + Environment, +) from zope.i18nmessageid import MessageFactory from zope.i18n.interfaces import ITranslationDomain @@ -31,13 +33,10 @@ de_file = os.path.join(testdir, 'de-default.mo') class TestGlobalTranslationDomain(TestITranslationDomain, unittest.TestCase): - def _getTranslationDomain(self): domain = TranslationDomain('default') - en_catalog = GettextMessageCatalog('en', 'default', - en_file) - de_catalog = GettextMessageCatalog('de', 'default', - de_file) + en_catalog = GettextMessageCatalog('en', 'default', en_file) + de_catalog = GettextMessageCatalog('de', 'default', de_file) domain.addCatalog(en_catalog) domain.addCatalog(de_catalog) return domain @@ -56,14 +55,24 @@ class TestGlobalTranslationDomain(TestITranslationDomain, unittest.TestCase): # Test that a translation in an unsupported language returns the # default, if there is no fallback language eq(translate('short_greeting', target_language='es'), 'short_greeting') - eq(translate('short_greeting', target_language='es', - default='short_greeting'), 'short_greeting') + eq( + translate( + 'short_greeting', + target_language='es', + default='short_greeting', + ), + 'short_greeting', + ) # Same test, but use the context argument instead of target_language context = Environment() eq(translate('short_greeting', context=context), 'short_greeting') - eq(translate('short_greeting', context=context, - default='short_greeting'), 'short_greeting') + eq( + translate( + 'short_greeting', context=context, default='short_greeting' + ), + 'short_greeting', + ) def testEmptyStringTranslate(self): translate = self._domain.translate @@ -73,7 +82,8 @@ class TestGlobalTranslationDomain(TestITranslationDomain, unittest.TestCase): def testStringTranslate(self): self.assertEqual( self._domain.translate(u"short_greeting", target_language='en'), - u"Hello!") + u"Hello!", + ) def testMessageIDTranslate(self): factory = MessageFactory('default') @@ -81,44 +91,58 @@ class TestGlobalTranslationDomain(TestITranslationDomain, unittest.TestCase): msgid = factory(u"short_greeting", 'default') self.assertEqual(translate(msgid, target_language='en'), u"Hello!") # MessageID attributes override arguments - msgid = factory('43-not-there', 'this ${that} the other', - mapping={'that': 'THAT'}) + msgid = factory( + '43-not-there', 'this ${that} the other', mapping={'that': 'THAT'} + ) self.assertEqual( - translate(msgid, target_language='en', default="default", - mapping={"that": "that"}), "this THAT the other") + translate( + msgid, + target_language='en', + default="default", + mapping={"that": "that"}, + ), + "this THAT the other", + ) def testMessageIDRecursiveTranslate(self): factory = MessageFactory('default') translate = self._domain.translate - msgid_sub1 = factory(u"44-not-there", '${blue}', - mapping={'blue': 'BLUE'}) - msgid_sub2 = factory(u"45-not-there", '${yellow}', - mapping={'yellow': 'YELLOW'}) - mapping = {'color1': msgid_sub1, - 'color2': msgid_sub2} - msgid = factory(u"46-not-there", 'Color: ${color1}/${color2}', - mapping=mapping) + msgid_sub1 = factory( + u"44-not-there", '${blue}', mapping={'blue': 'BLUE'} + ) + msgid_sub2 = factory( + u"45-not-there", '${yellow}', mapping={'yellow': 'YELLOW'} + ) + mapping = {'color1': msgid_sub1, 'color2': msgid_sub2} + msgid = factory( + u"46-not-there", 'Color: ${color1}/${color2}', mapping=mapping + ) self.assertEqual( translate(msgid, target_language='en', default="default"), - "Color: BLUE/YELLOW") + "Color: BLUE/YELLOW", + ) # The recursive translation must not change the mappings - self.assertEqual(msgid.mapping, {'color1': msgid_sub1, - 'color2': msgid_sub2}) + self.assertEqual( + msgid.mapping, {'color1': msgid_sub1, 'color2': msgid_sub2} + ) # A circular reference should not lead to crashes - msgid1 = factory(u"47-not-there", 'Message 1 and $msg2', - mapping={}) - msgid2 = factory(u"48-not-there", 'Message 2 and $msg1', - mapping={}) + msgid1 = factory(u"47-not-there", 'Message 1 and $msg2', mapping={}) + msgid2 = factory(u"48-not-there", 'Message 2 and $msg1', mapping={}) msgid1.mapping['msg2'] = msgid2 msgid2.mapping['msg1'] = msgid1 - self.assertRaises(ValueError, - translate, msgid1, None, None, 'en', "default") + self.assertRaises( + ValueError, translate, msgid1, None, None, 'en', "default" + ) # Recursive translations also work if the original message id wasn't a # message id but a Unicode with a directly passed mapping self.assertEqual( "Color: BLUE/YELLOW", - translate(u"Color: ${color1}/${color2}", mapping=mapping, - target_language='en')) + translate( + u"Color: ${color1}/${color2}", + mapping=mapping, + target_language='en', + ), + ) # If we have mapping with a message id from a different # domain, make sure we use that domain, not ours. If the @@ -127,29 +151,34 @@ class TestGlobalTranslationDomain(TestITranslationDomain, unittest.TestCase): alt_factory = MessageFactory('alt') msgid_sub = alt_factory(u"special", default=u"oohhh") mapping = {'message': msgid_sub} - msgid = factory(u"46-not-there", 'Message: ${message}', - mapping=mapping) + msgid = factory( + u"46-not-there", 'Message: ${message}', mapping=mapping + ) # test we get a default with no domain registered self.assertEqual( translate(msgid, target_language='en', default="default"), - "Message: oohhh") + "Message: oohhh", + ) # provide the domain domain = TranslationDomain('alt') path = testdir - en_catalog = GettextMessageCatalog('en', 'alt', - os.path.join(path, 'en-alt.mo')) + en_catalog = GettextMessageCatalog( + 'en', 'alt', os.path.join(path, 'en-alt.mo') + ) domain.addCatalog(en_catalog) # test that we get the right translation zope.component.provideUtility(domain, ITranslationDomain, 'alt') self.assertEqual( translate(msgid, target_language='en', default="default"), - "Message: Wow") + "Message: Wow", + ) def testMessageIDTranslateForDifferentDomain(self): domain = TranslationDomain('alt') path = testdir - en_catalog = GettextMessageCatalog('en', 'alt', - os.path.join(path, 'en-alt.mo')) + en_catalog = GettextMessageCatalog( + 'en', 'alt', os.path.join(path, 'en-alt.mo') + ) domain.addCatalog(en_catalog) zope.component.provideUtility(domain, ITranslationDomain, 'alt') @@ -157,34 +186,34 @@ class TestGlobalTranslationDomain(TestITranslationDomain, unittest.TestCase): factory = MessageFactory('alt') msgid = factory(u"special", 'default') self.assertEqual( - self._domain.translate(msgid, target_language='en'), u"Wow") + self._domain.translate(msgid, target_language='en'), u"Wow" + ) def testSimpleFallbackTranslation(self): translate = self._domain.translate eq = self.assertEqual # Test that a translation in an unsupported language returns a # translation in the fallback language (by default, English) - eq(translate('short_greeting', target_language='es'), - u"Hello!") + eq(translate('short_greeting', target_language='es'), u"Hello!") # Same test, but use the context argument instead of target_language context = Environment() - eq(translate('short_greeting', context=context), - u"Hello!") + eq(translate('short_greeting', context=context), u"Hello!") def testInterpolationWithoutTranslation(self): translate = self._domain.translate self.assertEqual( - translate('42-not-there', target_language="en", - default="this ${that} the other", - mapping={"that": "THAT"}), - "this THAT the other") + translate( + '42-not-there', + target_language="en", + default="this ${that} the other", + mapping={"that": "THAT"}, + ), + "this THAT the other", + ) def test_getCatalogInfos(self): cats = self._domain.getCatalogsInfo() - self.assertEqual( - cats, - {'en': [en_file], - 'de': [de_file]}) + self.assertEqual(cats, {'en': [en_file], 'de': [de_file]}) def test_releoadCatalogs(self): # It uses the keys we pass diff --git a/src/zope/i18n/tests/test_zcml.py b/src/zope/i18n/tests/test_zcml.py index 52ee437..89bff33 100644 --- a/src/zope/i18n/tests/test_zcml.py +++ b/src/zope/i18n/tests/test_zcml.py @@ -37,6 +37,7 @@ template = """\ %s </configure>""" + class DirectivesTest(PlacelessSetup, unittest.TestCase): # This test suite needs the [zcml] and [compile] extra dependencies @@ -54,13 +55,21 @@ class DirectivesTest(PlacelessSetup, unittest.TestCase): def testRegisterTranslations(self): self.assertTrue(queryUtility(ITranslationDomain) is None) xmlconfig.string( - template % ''' + template + % ''' <configure package="zope.i18n.tests"> <i18n:registerTranslations directory="locale" /> </configure> - ''', self.context) - path = os.path.join(os.path.dirname(zope.i18n.tests.__file__), - 'locale', 'en', 'LC_MESSAGES', 'zope-i18n.mo') + ''', + self.context, + ) + path = os.path.join( + os.path.dirname(zope.i18n.tests.__file__), + 'locale', + 'en', + 'LC_MESSAGES', + 'zope-i18n.mo', + ) util = getUtility(ITranslationDomain, 'zope-i18n') self.assertEqual(util._catalogs.get('test'), ['test']) self.assertEqual(util._catalogs.get('en'), [text_type(path)]) @@ -69,39 +78,65 @@ class DirectivesTest(PlacelessSetup, unittest.TestCase): self.assertTrue(queryUtility(ITranslationDomain) is None) config.ALLOWED_LANGUAGES = ('de', 'fr') xmlconfig.string( - template % ''' + template + % ''' <configure package="zope.i18n.tests"> <i18n:registerTranslations directory="locale" /> </configure> - ''', self.context) - path = os.path.join(os.path.dirname(zope.i18n.tests.__file__), - 'locale', 'de', 'LC_MESSAGES', 'zope-i18n.mo') + ''', + self.context, + ) + path = os.path.join( + os.path.dirname(zope.i18n.tests.__file__), + 'locale', + 'de', + 'LC_MESSAGES', + 'zope-i18n.mo', + ) util = getUtility(ITranslationDomain, 'zope-i18n') - self.assertEqual(util._catalogs, - {'test': ['test'], 'de': [text_type(path)]}) + self.assertEqual( + util._catalogs, {'test': ['test'], 'de': [text_type(path)]} + ) def testRegisterDistributedTranslations(self): self.assertTrue(queryUtility(ITranslationDomain, 'zope-i18n') is None) xmlconfig.string( - template % ''' + template + % ''' <configure package="zope.i18n.tests"> <i18n:registerTranslations directory="locale" /> </configure> - ''', self.context) + ''', + self.context, + ) xmlconfig.string( - template % ''' + template + % ''' <configure package="zope.i18n.tests"> <i18n:registerTranslations directory="locale2" /> </configure> - ''', self.context) - path1 = os.path.join(os.path.dirname(zope.i18n.tests.__file__), - 'locale', 'en', 'LC_MESSAGES', 'zope-i18n.mo') - path2 = os.path.join(os.path.dirname(zope.i18n.tests.__file__), - 'locale2', 'en', 'LC_MESSAGES', 'zope-i18n.mo') + ''', + self.context, + ) + path1 = os.path.join( + os.path.dirname(zope.i18n.tests.__file__), + 'locale', + 'en', + 'LC_MESSAGES', + 'zope-i18n.mo', + ) + path2 = os.path.join( + os.path.dirname(zope.i18n.tests.__file__), + 'locale2', + 'en', + 'LC_MESSAGES', + 'zope-i18n.mo', + ) util = getUtility(ITranslationDomain, 'zope-i18n') self.assertEqual(util._catalogs.get('test'), ['test', 'test']) - self.assertEqual(util._catalogs.get('en'), - [text_type(path1), text_type(path2)]) + self.assertEqual( + util._catalogs.get('en'), [text_type(path1), text_type(path2)] + ) msg = util.translate(u"Additional message", target_language='en') self.assertEqual(msg, u"Additional message translated") @@ -131,14 +166,18 @@ class DirectivesTest(PlacelessSetup, unittest.TestCase): os.utime(path, (path_atime, path_mtime - 6000)) xmlconfig.string( - template % ''' + template + % ''' <configure package="zope.i18n.tests"> <i18n:registerTranslations directory="locale3" /> </configure> - ''', self.context) + ''', + self.context, + ) util = getUtility(ITranslationDomain, 'zope-i18n') - self.assertEqual(util._catalogs, - {'test': ['test'], 'en': [text_type(path)]}) + self.assertEqual( + util._catalogs, {'test': ['test'], 'en': [text_type(path)]} + ) msg = util.translate(u"I'm a newer file", target_language='en') self.assertEqual(msg, u"I'm a newer file translated") @@ -154,22 +193,33 @@ class DirectivesTest(PlacelessSetup, unittest.TestCase): self.assertTrue(queryUtility(ITranslationDomain, 'zope-i18n') is None) self.assertTrue(queryUtility(ITranslationDomain, 'zope-i18n2') is None) xmlconfig.string( - template % ''' + template + % ''' <configure package="zope.i18n.tests"> <i18n:registerTranslations directory="locale3" domain="zope-i18n" /> </configure> - ''', self.context) - path = os.path.join(os.path.dirname(zope.i18n.tests.__file__), - 'locale3', 'en', 'LC_MESSAGES', 'zope-i18n.mo') + ''', + self.context, + ) + path = os.path.join( + os.path.dirname(zope.i18n.tests.__file__), + 'locale3', + 'en', + 'LC_MESSAGES', + 'zope-i18n.mo', + ) util = getUtility(ITranslationDomain, 'zope-i18n') - self.assertEqual(util._catalogs, - {'test': ['test'], 'en': [text_type(path)]}) + self.assertEqual( + util._catalogs, {'test': ['test'], 'en': [text_type(path)]} + ) self.assertTrue(queryUtility(ITranslationDomain, 'zope-i18n2') is None) def test_suite(): - return unittest.TestSuite(( - unittest.defaultTestLoader.loadTestsFromName(__name__), - doctest.DocFileSuite('configure.txt'), - )) + return unittest.TestSuite( + ( + unittest.defaultTestLoader.loadTestsFromName(__name__), + doctest.DocFileSuite('configure.txt'), + ) + ) diff --git a/src/zope/i18n/tests/testi18nawareobject.py b/src/zope/i18n/tests/testi18nawareobject.py index 21a61f4..b1a175e 100644 --- a/src/zope/i18n/tests/testi18nawareobject.py +++ b/src/zope/i18n/tests/testi18nawareobject.py @@ -21,7 +21,6 @@ from zope.interface import implementer @implementer(II18nAware) class I18nAwareContentObject(object): - def __init__(self): self.content = {} self.defaultLanguage = 'en' @@ -54,8 +53,8 @@ class I18nAwareContentObject(object): # ############################################################ -class AbstractTestII18nAwareMixin(object): +class AbstractTestII18nAwareMixin(object): def setUp(self): self.object = self._createObject() self.object.setDefaultLanguage('fr') @@ -73,11 +72,12 @@ class AbstractTestII18nAwareMixin(object): self.assertEqual(self.object.getDefaultLanguage(), 'lt') def testGetAvailableLanguages(self): - self.assertEqual(sorted(self.object.getAvailableLanguages()), ['en', 'fr', 'lt']) + self.assertEqual( + sorted(self.object.getAvailableLanguages()), ['en', 'fr', 'lt'] + ) class TestI18nAwareObject(AbstractTestII18nAwareMixin, unittest.TestCase): - def _createObject(self): object = I18nAwareContentObject() object.setContent('English', 'en') diff --git a/src/zope/i18n/translationdomain.py b/src/zope/i18n/translationdomain.py index b9287f0..af8eee2 100644 --- a/src/zope/i18n/translationdomain.py +++ b/src/zope/i18n/translationdomain.py @@ -37,10 +37,10 @@ text_type = str if bytes is not str else unicode @zope.interface.implementer(ITranslationDomain) class TranslationDomain(object): - def __init__(self, domain, fallbacks=None): self.domain = ( - domain.decode("utf-8") if isinstance(domain, bytes) else domain) + domain.decode("utf-8") if isinstance(domain, bytes) else domain + ) # _catalogs maps (language, domain) to IMessageCatalog instances self._catalogs = {} # _data maps IMessageCatalog.getIdentifier() to IMessageCatalog @@ -56,17 +56,24 @@ class TranslationDomain(object): def addCatalog(self, catalog): self._data[catalog.getIdentifier()] = catalog - self._registerMessageCatalog(catalog.language, - catalog.getIdentifier()) + self._registerMessageCatalog(catalog.language, catalog.getIdentifier()) def setLanguageFallbacks(self, fallbacks=None): if fallbacks is None: fallbacks = LANGUAGE_FALLBACKS self._fallbacks = fallbacks - def translate(self, msgid, mapping=None, context=None, - target_language=None, default=None, - msgid_plural=None, default_plural=None, number=None): + def translate( + self, + msgid, + mapping=None, + context=None, + target_language=None, + default=None, + msgid_plural=None, + default_plural=None, + number=None, + ): """See zope.i18n.interfaces.ITranslationDomain""" # if the msgid is empty, let's save a lot of calculations and return # an empty string. @@ -81,19 +88,43 @@ class TranslationDomain(object): target_language = negotiator.getLanguage(langs, context) return self._recursive_translate( - msgid, mapping, target_language, default, context, - msgid_plural, default_plural, number) - - def _recursive_translate(self, msgid, mapping, target_language, default, - context, msgid_plural, default_plural, number, - seen=None): + msgid, + mapping, + target_language, + default, + context, + msgid_plural, + default_plural, + number, + ) + + def _recursive_translate( + self, + msgid, + mapping, + target_language, + default, + context, + msgid_plural, + default_plural, + number, + seen=None, + ): """Recursively translate msg.""" # MessageID attributes override arguments if isinstance(msgid, Message): if msgid.domain != self.domain: return translate( - msgid, msgid.domain, mapping, context, target_language, - default, msgid_plural, default_plural, number) + msgid, + msgid.domain, + mapping, + context, + target_language, + default, + msgid_plural, + default_plural, + number, + ) default = msgid.default mapping = msgid.mapping msgid_plural = msgid.msgid_plural @@ -101,8 +132,9 @@ class TranslationDomain(object): number = msgid.number # Recursively translate mappings, if they are translatable - if (mapping is not None - and Message in (type(m) for m in mapping.values())): + if mapping is not None and Message in ( + type(m) for m in mapping.values() + ): if seen is None: seen = set() seen.add((msgid, msgid_plural)) @@ -113,11 +145,20 @@ class TranslationDomain(object): # https://bugs.launchpad.net/zope3/+bug/220122 if (value, value.msgid_plural) in seen: raise ValueError( - "Circular reference in mappings detected: %s" % - value) + "Circular reference in mappings detected: %s" + % value + ) mapping[key] = self._recursive_translate( - value, mapping, target_language, default, context, - msgid_plural, default_plural, number, seen) + value, + mapping, + target_language, + default, + context, + msgid_plural, + default_plural, + number, + seen, + ) if default is None: default = text_type(msgid) @@ -142,18 +183,24 @@ class TranslationDomain(object): if msgid_plural is not None: # This is a plural text = self._data[catalog_names[0]].queryPluralMessage( - msgid, msgid_plural, number, default, default_plural) + msgid, msgid_plural, number, default, default_plural + ) else: text = self._data[catalog_names[0]].queryMessage( - msgid, default) + msgid, default + ) else: for name in catalog_names: catalog = self._data[name] if msgid_plural is not None: # This is a plural s = catalog.queryPluralMessage( - msgid, msgid_plural, number, - default, default_plural) + msgid, + msgid_plural, + number, + default, + default_plural, + ) else: s = catalog.queryMessage(msgid) if s is not None: diff --git a/src/zope/i18n/zcml.py b/src/zope/i18n/zcml.py index 4df9ccb..7ae6319 100644 --- a/src/zope/i18n/zcml.py +++ b/src/zope/i18n/zcml.py @@ -1,4 +1,3 @@ - # ############################################################################## # # Copyright (c) 2001, 2002 Zope Foundation and Contributors. @@ -44,15 +43,17 @@ class IRegisterTranslationsDirective(Interface): directory = Path( title=u"Directory", description=u"Directory containing the translations", - required=True - ) + required=True, + ) domain = TextLine( title=u"Domain", - description=(u"Translation domain to register. If not specified, " - u"all domains found in the directory are registered"), - required=False - ) + description=( + u"Translation domain to register. If not specified, " + u"all domains found in the directory are registered" + ), + required=False, + ) def allow_language(lang): @@ -90,13 +91,15 @@ def registerTranslations(_context, directory, domain='*'): if os.path.isdir(lc_messages_path): # Preprocess files and update or compile the mo files if config.COMPILE_MO_FILES: - for domain_path in glob(os.path.join(lc_messages_path, - '%s.po' % domain)): + for domain_path in glob( + os.path.join(lc_messages_path, '%s.po' % domain) + ): domain_file = os.path.basename(domain_path) name = domain_file[:-3] compile_mo_file(name, lc_messages_path) - for domain_path in glob(os.path.join(lc_messages_path, - '%s.mo' % domain)): + for domain_path in glob( + os.path.join(lc_messages_path, '%s.mo' % domain) + ): loaded = True domain_file = os.path.basename(domain_path) name = domain_file[:-3] @@ -115,13 +118,13 @@ def registerTranslations(_context, directory, domain='*'): # `zope.component.zcml.utility`) since we need the actual utilities # in place before the merging can be done... _context.action( - discriminator=None, - callable=handler, - args=(catalogs, name)) + discriminator=None, callable=handler, args=(catalogs, name) + ) # also register the interface for the translation utilities provides = ITranslationDomain _context.action( discriminator=None, callable=provideInterface, - args=(provides.__module__ + '.' + provides.getName(), provides)) + args=(provides.__module__ + '.' + provides.getName(), provides), + ) |