[PATCH 1/2] A function for resolving date format and order

Anne Mulhern amulhern at redhat.com
Tue Oct 7 21:29:52 UTC 2014





----- Original Message -----
> From: "Vratislav Podzimek" <vpodzime at redhat.com>
> To: anaconda-patches at lists.fedorahosted.org
> Sent: Tuesday, October 7, 2014 12:32:36 PM
> Subject: [PATCH 1/2] A function for resolving date format and order
> 
> It's not that easy to get right order of date fields (year, month, day) and
> their formats so we should better have it in a standalone function with
> tests.
> 
> Related: rhbz#1044233
> Signed-off-by: Vratislav Podzimek <vpodzime at redhat.com>
> ---
>  pyanaconda/localization.py                  | 64
>  +++++++++++++++++++++++++++++
>  tests/pyanaconda_tests/localization_test.py | 21 ++++++++++
>  2 files changed, 85 insertions(+)
> 
> diff --git a/pyanaconda/localization.py b/pyanaconda/localization.py
> index c95fa1d..efcb66f 100644
> --- a/pyanaconda/localization.py
> +++ b/pyanaconda/localization.py
> @@ -26,6 +26,7 @@ import re
>  import langtable
>  import locale as locale_mod
>  import glob
> +from collections import namedtuple
>  
>  from pyanaconda import constants
>  from pyanaconda.iutil import upcase_first_letter
> @@ -469,3 +470,66 @@ def load_firmware_language(lang):
>  
>      log.debug("Using UEFI PlatformLang '%s' ('%s') as our language.", d,
>      locales[0])
>      setup_locale(locales[0], lang)
> +
> +_DateFieldSpec = namedtuple("DateFieldSpec", ["format", "suffix"])
> +
> +def resolve_date_format(year, month, day):
> +    """
> +    Puts the year, month and day objects in the right order according to the
> +    currently set locale and provides format specification for each of the
> +    fields.
> +
> +    :param year: any object or value representing year
> +    :type year: any
> +    :param month: any object or value representing month
> +    :type month: any
> +    :param day: any object or value representing day
> +    :type day: any

Uses of 'any' for type should be changed to 'object'.

> +    :returns: a pair where the first field contains a tuple with the year,
> month
> +              and day objects/values put in the right order and where the
> second
> +              field contains a tuple with three :class:`_DateFieldSpec`
> objects
> +              specifying formats respectively to the first (year, month,
> day)
> +              field
> +    :rtype: tuple
> +    :raise ValueError: in case currently set locale has unsupported date
> format
> +                       (likely a bug in this function)

"this" sounds like it is referring to the function being defined, i.e., this function.
Seems weird to say this function will raise a ValueError if it has a bug.

> +
> +    """
> +
> +    # see date (1), 'O' (not '0') is a mystery, 'E' is Buddhist calendar,
> '(.*)'
> +    # is an arbitrary suffix
> +    field_spec_re = re.compile(r'([-_0OE^#]*)([yYmbBde])(.*)')
> +
> +    order = []
> +    formats = []
> +    fmt_str = locale_mod.nl_langinfo(locale_mod.D_FMT)
> +
> +    # see date (1)
> +    fmt_str = fmt_str.replace("%F", "%Y-%m-%d")

You might want to point out here that while the Python
documentation does not document '%F' as a valid format
string it definitely exists and is in format string returned
by nl_langinfo for some calls.

> +    # e.g. "%d.%m.%Y" -> ['d.', 'm.', 'Y']
> +    fields = fmt_str.split("%")[1:]
> +

       matches = (field_spec_re.match(f) for f in fields)
       fmts = [m.groups() for m in matches if m is not None]

       terms = []
       for _prefix, item, _suffix in fmts:
           if ...:
               terms.append(...)
           ...

       formats = [_DateFieldSpec("%" + prefix + item, suffix.strip()) for prefix, item, suffix in fmts]
       return (tuple(terms), tuple(formats))

I'ld rather do the above than the below, but, in any case,
I think terms, or ordered_terms, or sorted_terms, would be better than order.           
  
> +    for field in fields:
> +        match = field_spec_re.match(field)
> +        if not match:
> +            # ignore fields we are not interested in (like %A for weekday
> name, etc.)
> +            continue
> +
> +        prefix, item, suffix = match.groups()
> +        if item in ("d", "e"):
> +            # "e" is the same as "_d"
> +            order.append(day)
> +        elif item in ("Y", "y"):
> +            # 4-digit year, 2-digit year
> +            order.append(year)
> +        elif item in ("m", "b", "B"):
> +            # month number, short month name, long month name
> +            order.append(month)
> +        else:
> +            raise ValueError("Unsupported date format's '%s' field '%s'" %
> (fmt_str, field))
> +
> +        # "%" + prefix + item gives a format for date/time formatting
> functions
> +        formats.append(_DateFieldSpec("%" + prefix + item, suffix.strip()))
> +
> +    return (tuple(order), tuple(formats))
> diff --git a/tests/pyanaconda_tests/localization_test.py
> b/tests/pyanaconda_tests/localization_test.py
> index 00ff31d..5bf6cba 100644
> --- a/tests/pyanaconda_tests/localization_test.py
> +++ b/tests/pyanaconda_tests/localization_test.py
> @@ -19,6 +19,8 @@
>  #
>  
>  from pyanaconda import localization
> +from pyanaconda.iutil import execReadlines
> +import locale as locale_mod
>  import unittest
>  
>  class ParsingTests(unittest.TestCase):
> @@ -139,3 +141,22 @@ class LangcodeLocaleMatchingTests(unittest.TestCase):
>          # no matches
>          self.assertIsNone(localization.find_best_locale_match("pt_BR",
>          ["en_BR", "en"]))
>          self.assertIsNone(localization.find_best_locale_match("cs_CZ.UTF-8",
>          ["en", "en.UTF-8"]))
> +
> +    def resolve_date_format_test(self):
> +        """All locales' date formats should be properly resolved."""
> +
> +        locales = (line.strip() for line in execReadlines("locale", ["-a"]))
> +        for locale in locales:
> +            try:
> +                locale_mod.setlocale(locale_mod.LC_ALL, locale)
> +            except locale_mod.Error:
> +                # cannot set locale (a bug in the locale module?)
> +                continue
> +
> +            (order, formats) = localization.resolve_date_format(1, 2, 3)
> +            if len(order) != 3:
> +                self.fail("Not all date fields available for %s" % locale)
> +            if len(formats) != 3:
> +                self.fail("Not all date fields' formats available for %s" %
> locale)
> +            for i in (1, 2, 3):
> +                self.assertIn(i, order)

You might want to count the number of times you hit the continue block and if
it's an appreciable proportion, or all, of the number of locales, raise an
exception so that the test has an error.

> --
> 1.9.3
> 
> _______________________________________________
> anaconda-patches mailing list
> anaconda-patches at lists.fedorahosted.org
> https://lists.fedorahosted.org/mailman/listinfo/anaconda-patches
> 

Otherwise, ack.

- mulhern


More information about the anaconda-patches mailing list