From e6317dada563f309ea8560f3e0681fc3f42cb2d1 Mon Sep 17 00:00:00 2001 From: JackDandy Date: Sat, 12 Sep 2015 11:51:54 +0100 Subject: [PATCH 1/2] Update dateutil library to 2.4.2 (083f666). --- CHANGES.md | 1 + lib/dateutil/parser.py | 315 ++++++++++++++++++------------ lib/dateutil/relativedelta.py | 1 + lib/dateutil/tz.py | 12 +- lib/dateutil/tzwin.py | 11 +- lib/dateutil/zoneinfo/__init__.py | 49 +---- lib/dateutil/zoneinfo/rebuild.py | 43 ++++ 7 files changed, 260 insertions(+), 172 deletions(-) create mode 100644 lib/dateutil/zoneinfo/rebuild.py diff --git a/CHANGES.md b/CHANGES.md index 62118224..c037d267 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -15,6 +15,7 @@ * Remove legacy anime split home option from anime settings tab (new option located in general/interface tab) * Remove "Manage Torrents" * Update Beautiful Soup 4.3.2 to 4.4.0 (r390) +* Update dateutil library to 2.4.2 (083f666) * Update Hachoir library 1.3.3 to 1.3.4 (r1383) * Change configure quiet option in Hachoir to suppress warnings (add ref:hacks.txt) * Add parse media content to determine quality before making final assumptions during re-scan, update, pp diff --git a/lib/dateutil/parser.py b/lib/dateutil/parser.py index 590ac109..762e5db1 100644 --- a/lib/dateutil/parser.py +++ b/lib/dateutil/parser.py @@ -4,28 +4,29 @@ This module offers a generic date/time string parser which is able to parse most known formats to represent a date and/or time. This module attempts to be forgiving with regards to unlikely input formats, -returning a datetime object even for dates which are ambiguous. If an element of -a date/time stamp is omitted, the following rules are applied: +returning a datetime object even for dates which are ambiguous. If an element +of a date/time stamp is omitted, the following rules are applied: - If AM or PM is left unspecified, a 24-hour clock is assumed, however, an hour - on a 12-hour clock (`0 <= hour <= 12`) *must* be specified if AM or PM is + on a 12-hour clock (``0 <= hour <= 12``) *must* be specified if AM or PM is specified. -- If a time zone is omitted, it is assumed to be UTC. +- If a time zone is omitted, a timezone-naive datetime is returned. -If any other elements are missing, they are taken from the `datetime.datetime` -object passed to the parameter `default`. If this results in a day number -exceeding the valid number of days per month, one can fall back to the last -day of the month by setting `fallback_on_invalid_day` parameter to `True`. +If any other elements are missing, they are taken from the +:class:`datetime.datetime` object passed to the parameter ``default``. If this +results in a day number exceeding the valid number of days per month, one can +fall back to the last day of the month by setting ``fallback_on_invalid_day`` +parameter to ``True``. -Also provided is the `smart_defaults` option, which attempts to fill in the +Also provided is the ``smart_defaults`` option, which attempts to fill in the missing elements from context. If specified, the logic is: - If the omitted element is smaller than the largest specified element, select - the *earliest* time matching the specified conditions; so `"June 2010"` is - interpreted as `June 1, 2010 0:00:00`) and the (somewhat strange) - `"Feb 1997 3:15 PM"` is interpreted as `February 1, 1997 15:15:00`. + the *earliest* time matching the specified conditions; so ``"June 2010"`` is + interpreted as ``June 1, 2010 0:00:00``) and the (somewhat strange) + ``"Feb 1997 3:15 PM"`` is interpreted as ``February 1, 1997 15:15:00``. - If the element is larger than the largest specified element, select the - *most recent* time matching the specified conditions (e.g parsing `"May"` + *most recent* time matching the specified conditions (e.g parsing ``"May"`` in June 2015 returns the date May 1st, 2015, whereas parsing it in April 2015 - returns May 1st 2014). If using the `date_in_future` flag, this logic is + returns May 1st 2014). If using the ``date_in_future`` flag, this logic is inverted, and instead the *next* time matching the specified conditions is returned. @@ -46,6 +47,7 @@ import datetime import string import time import collections +import re from io import StringIO from calendar import monthrange, isleap @@ -58,6 +60,9 @@ __all__ = ["parse", "parserinfo"] class _timelex(object): + # Fractional seconds are sometimes split by a comma + _split_decimal = re.compile("([\.,])") + def __init__(self, instream): if isinstance(instream, binary_type): instream = instream.decode() @@ -80,8 +85,8 @@ class _timelex(object): """ This function breaks the time string into lexical units (tokens), which can be parsed by the parser. Lexical units are demarcated by changes in - the character set, so any continuous string of letters is considered one - unit, any continuous string of numbers is considered one unit. + the character set, so any continuous string of letters is considered + one unit, any continuous string of numbers is considered one unit. The main complication arises from the fact that dots ('.') can be used both as separators (e.g. "Sep.20.2009") or decimal points (e.g. @@ -101,9 +106,9 @@ class _timelex(object): whitespace = self.whitespace while not self.eof: - # We only realize that we've reached the end of a token when we find - # a character that's not part of the current token - since that - # character may be part of the next token, it's stored in the + # We only realize that we've reached the end of a token when we + # find a character that's not part of the current token - since + # that character may be part of the next token, it's stored in the # charstack. if self.charstack: nextchar = self.charstack.pop(0) @@ -145,7 +150,7 @@ class _timelex(object): # numbers until we find something that doesn't fit. if nextchar in numchars: token += nextchar - elif nextchar == '.': + elif nextchar == '.' or (nextchar == ',' and len(token) >= 2): token += nextchar state = '0.' else: @@ -176,14 +181,16 @@ class _timelex(object): break # emit token if (state in ('a.', '0.') and (seenletters or token.count('.') > 1 or - token[-1] == '.')): - l = token.split('.') + token[-1] in '.,')): + l = self._split_decimal.split(token) token = l[0] for tok in l[1:]: - self.tokenstack.append('.') if tok: self.tokenstack.append(tok) + if state == '0.' and token.count('.') == 0: + token = token.replace(',', '.') + return token def __iter__(self): @@ -224,20 +231,20 @@ class _resultbase(object): class parserinfo(object): """ - Class which handles what inputs are accepted. Subclass this to customize the - language and acceptable values for each parameter. + Class which handles what inputs are accepted. Subclass this to customize + the language and acceptable values for each parameter. :param dayfirst: Whether to interpret the first value in an ambiguous 3-integer date - (e.g. 01/05/09) as the day (`True`) or month (`False`). If - `yearfirst` is set to `True`, this distinguishes between YDM and - YMD. Default is `False`. + (e.g. 01/05/09) as the day (``True``) or month (``False``). If + ``yearfirst`` is set to ``True``, this distinguishes between YDM + and YMD. Default is ``False``. :param yearfirst: Whether to interpret the first value in an ambiguous 3-integer date - (e.g. 01/05/09) as the year. If `True`, the first number is taken to - be the year, otherwise the last number is taken to be the year. - Default is `False`. + (e.g. 01/05/09) as the year. If ``True``, the first number is taken + to be the year, otherwise the last number is taken to be the year. + Default is ``False``. """ # m from a.m/p.m, t from ISO T separator @@ -287,7 +294,7 @@ class parserinfo(object): self.smart_defaults = smart_defaults self._year = time.localtime().tm_year - self._century = self._year // 100*100 + self._century = self._year // 100 * 100 def _convert(self, lst): dct = {} @@ -313,7 +320,7 @@ class parserinfo(object): def month(self, name): if len(name) >= 3: try: - return self._months[name.lower()]+1 + return self._months[name.lower()] + 1 except KeyError: pass return None @@ -345,7 +352,7 @@ class parserinfo(object): def convertyear(self, year): if year < 100: year += self._century - if abs(year-self._year) >= 50: + if abs(year - self._year) >= 50: if year < self._year: year += 100 else: @@ -373,65 +380,87 @@ class parser(object): smart_defaults=None, date_in_future=False, fallback_on_invalid_day=None, **kwargs): """ - Parse the date/time string into a datetime object. + Parse the date/time string into a :class:`datetime.datetime` object. :param timestr: Any date/time string using the supported formats. :param default: The default datetime object, if this is a datetime object and not - `None`, elements specified in `timestr` replace elements in the - default object, unless `smart_defaults` is set to `True`, in which - case to the extent necessary, timestamps are calculated relative to - this date. + ``None``, elements specified in ``timestr`` replace elements in the + default object, unless ``smart_defaults`` is set to ``True``, in + which case to the extent necessary, timestamps are calculated + relative to this date. :param smart_defaults: - If using smart defaults, the `default` parameter is treated as the - effective parsing date/time, and the context of the datetime string - is determined relative to `default`. If `None`, this parameter is - inherited from the :class:`parserinfo` object. + If using smart defaults, the ``default`` parameter is treated as + the effective parsing date/time, and the context of the datetime + string is determined relative to ``default``. If ``None``, this + parameter is inherited from the :class:`parserinfo` object. :param date_in_future: - If `smart_defaults` is `True`, the parser assumes by default that - the timestamp refers to a date in the past, and will return the - beginning of the most recent timespan which matches the time string - (e.g. if `default` is March 3rd, 2013, "Feb" parses to + If ``smart_defaults`` is ``True``, the parser assumes by default + that the timestamp refers to a date in the past, and will return + the beginning of the most recent timespan which matches the time + string (e.g. if ``default`` is March 3rd, 2013, "Feb" parses to "Feb 1, 2013" and "May 3" parses to May 3rd, 2012). Setting this - parameter to `True` inverts this assumption, and returns the + parameter to ``True`` inverts this assumption, and returns the beginning of the *next* matching timespan. :param fallback_on_invalid_day: - If specified `True`, an otherwise invalid date such as "Feb 30" or - "June 32" falls back to the last day of the month. If specified as - "False", the parser is strict about parsing otherwise valid dates - that would turn up as invalid because of the fallback rules (e.g. - "Feb 2010" run with a default of January 30, 2010 and `smartparser` - set to `False` would would throw an error, rather than falling - back to the end of February). If `None` or unspecified, the date - falls back to the most recent valid date only if the invalid date - is created as a result of an unspecified day in the time string. + If specified ``True``, an otherwise invalid date such as "Feb 30" + or "June 32" falls back to the last day of the month. If specified + as "False", the parser is strict about parsing otherwise valid + dates that would turn up as invalid because of the fallback rules + (e.g. "Feb 2010" run with a default of January 30, 2010 and + ``smartparser`` set to ``False`` would would throw an error, rather + than falling back to the end of February). If ``None`` or + unspecified, the date falls back to the most recent valid date only + if the invalid date is created as a result of an unspecified day in + the time string. :param ignoretz: - Whether or not to ignore the time zone. + If set ``True``, time zones in parsed strings are ignored and a + naive :class:`datetime.datetime` object is returned. :param tzinfos: - A time zone, to be applied to the date, if `ignoretz` is `True`. - This can be either a subclass of `tzinfo`, a time zone string or an - integer offset. + Additional time zone names / aliases which may be present in the + string. This argument maps time zone names (and optionally offsets + from those time zones) to time zones. This parameter can be a + dictionary with timezone aliases mapping time zone names to time + zones or a function taking two parameters (``tzname`` and + ``tzoffset``) and returning a time zone. + + The timezones to which the names are mapped can be an integer + offset from UTC in minutes or a :class:`tzinfo` object. + + .. doctest:: + :options: +NORMALIZE_WHITESPACE + + >>> from dateutil.parser import parse + >>> from dateutil.tz import gettz + >>> tzinfos = {"BRST": -10800, "CST": gettz("America/Chicago")} + >>> parse("2012-01-19 17:21:00 BRST", tzinfos=tzinfos) + datetime.datetime(2012, 1, 19, 17, 21, tzinfo=tzoffset(u'BRST', -10800)) + >>> parse("2012-01-19 17:21:00 CST", tzinfos=tzinfos) + datetime.datetime(2012, 1, 19, 17, 21, + tzinfo=tzfile('/usr/share/zoneinfo/America/Chicago')) + + This parameter is ignored if ``ignoretz`` is set. :param **kwargs: - Keyword arguments as passed to `_parse()`. + Keyword arguments as passed to ``_parse()``. :return: - Returns a `datetime.datetime` object or, if the `fuzzy_with_tokens` - option is `True`, returns a tuple, the first element being a - `datetime.datetime` object, the second a tuple containing the - fuzzy tokens. + Returns a :class:`datetime.datetime` object or, if the + ``fuzzy_with_tokens`` option is ``True``, returns a tuple, the + first element being a :class:`datetime.datetime` object, the second + a tuple containing the fuzzy tokens. :raises ValueError: Raised for invalid or unknown string format, if the provided - `tzinfo` is not in a valid format, or if an invalid date would - be created. + :class:`tzinfo` is not in a valid format, or if an invalid date + would be created. :raises OverFlowError: Raised if the parsed date exceeds the largest valid C integer on @@ -444,14 +473,11 @@ class parser(object): if default is None: effective_dt = datetime.datetime.now() default = datetime.datetime.now().replace(hour=0, minute=0, - second=0, microsecond=0) + second=0, microsecond=0) else: effective_dt = default - if kwargs.get('fuzzy_with_tokens', False): - res, skipped_tokens = self._parse(timestr, **kwargs) - else: - res = self._parse(timestr, **kwargs) + res, skipped_tokens = self._parse(timestr, **kwargs) if res is None: raise ValueError("Unknown string format") @@ -464,7 +490,7 @@ class parser(object): repl[attr] = value # Choose the correct fallback position if requested by the - # `smart_defaults` parameter. + # ``smart_defaults`` parameter. if smart_defaults: # Determine if it refers to this year, last year or next year if res.year is None: @@ -472,7 +498,7 @@ class parser(object): # Explicitly deal with leap year problems if res.month == 2 and (res.day is not None and res.day == 29): - + ly_offset = 4 if date_in_future else -4 next_year = 4 * (default.year // 4) @@ -583,36 +609,42 @@ class parser(object): fuzzy_with_tokens=False): """ Private method which performs the heavy lifting of parsing, called from - `parse()`, which passes on its `kwargs` to this function. + ``parse()``, which passes on its ``kwargs`` to this function. :param timestr: The string to parse. :param dayfirst: Whether to interpret the first value in an ambiguous 3-integer date - (e.g. 01/05/09) as the day (`True`) or month (`False`). If - `yearfirst` is set to `True`, this distinguishes between YDM and - YMD. If set to `None`, this value is retrieved from the current - `parserinfo` object (which itself defaults to `False`). + (e.g. 01/05/09) as the day (``True``) or month (``False``). If + ``yearfirst`` is set to ``True``, this distinguishes between YDM + and YMD. If set to ``None``, this value is retrieved from the + current :class:`parserinfo` object (which itself defaults to + ``False``). :param yearfirst: Whether to interpret the first value in an ambiguous 3-integer date - (e.g. 01/05/09) as the year. If `True`, the first number is taken to - be the year, otherwise the last number is taken to be the year. If - this is set to `None`, the value is retrieved from the current - `parserinfo` object (which itself defaults to `False`). + (e.g. 01/05/09) as the year. If ``True``, the first number is taken + to be the year, otherwise the last number is taken to be the year. + If this is set to ``None``, the value is retrieved from the current + :class:`parserinfo` object (which itself defaults to ``False``). :param fuzzy: Whether to allow fuzzy parsing, allowing for string like "Today is January 1, 2047 at 8:21:00AM". :param fuzzy_with_tokens: - If `True`, `fuzzy` is automatically set to True, and the parser will - return a tuple where the first element is the parsed - `datetime.datetime` datetimestamp and the second element is a tuple - containing the portions of the string which were ignored, e.g. - "Today is January 1, 2047 at 8:21:00AM" should return - `(datetime.datetime(2011, 1, 1, 8, 21), (u'Today is ', u' ', u'at '))` + If ``True``, ``fuzzy`` is automatically set to True, and the parser + will return a tuple where the first element is the parsed + :class:`datetime.datetime` datetimestamp and the second element is + a tuple containing the portions of the string which were ignored: + + .. doctest:: + + >>> from dateutil.parser import parse + >>> parse("Today is January 1, 2047 at 8:21:00AM", fuzzy_with_tokens=True) + (datetime.datetime(2011, 1, 1, 8, 21), (u'Today is ', u' ', u'at ')) + """ if fuzzy_with_tokens: fuzzy = True @@ -796,7 +828,7 @@ class parser(object): assert mstridx == -1 mstridx = len(ymd)-1 else: - return None + return None, None i += 1 @@ -840,7 +872,7 @@ class parser(object): i += 1 elif not fuzzy: - return None + return None, None else: i += 1 continue @@ -969,7 +1001,7 @@ class parser(object): # -[0]3 res.tzoffset = int(l[i][:2])*3600 else: - return None + return None, None i += 1 res.tzoffset *= signal @@ -987,7 +1019,7 @@ class parser(object): # Check jumps if not (info.jump(l[i]) or fuzzy): - return None + return None, None if last_skipped_token_i == i - 1: # recombine the tokens @@ -1002,7 +1034,7 @@ class parser(object): len_ymd = len(ymd) if len_ymd > 3: # More than three members!? - return None + return None, None elif len_ymd == 1 or (mstridx != -1 and len_ymd == 2): # One member, or two members with a month string if mstridx != -1: @@ -1066,72 +1098,113 @@ class parser(object): res.month, res.day, res.year = ymd except (IndexError, ValueError, AssertionError): - return None + return None, None if not info.validate(res): - return None + return None, None if fuzzy_with_tokens: return res, tuple(skipped_tokens) else: - return res + return res, None DEFAULTPARSER = parser() def parse(timestr, parserinfo=None, **kwargs): """ - Parse a string in one of the supported formats, using the `parserinfo` - parameters. + + Parse a string in one of the supported formats, using the + ``parserinfo`` parameters. :param timestr: A string containing a date/time stamp. :param parserinfo: A :class:`parserinfo` object containing parameters for the parser. - If `None`, the default arguments to the `parserinfo` constructor are - used. + If ``None``, the default arguments to the :class:`parserinfo` + constructor are used. - The `**kwargs` parameter takes the following keyword arguments: + The ``**kwargs`` parameter takes the following keyword arguments: :param default: The default datetime object, if this is a datetime object and not - `None`, elements specified in `timestr` replace elements in the + ``None``, elements specified in ``timestr`` replace elements in the default object. :param ignoretz: - Whether or not to ignore the time zone (boolean). + If set ``True``, time zones in parsed strings are ignored and a naive + :class:`datetime` object is returned. :param tzinfos: - A time zone, to be applied to the date, if `ignoretz` is `True`. - This can be either a subclass of `tzinfo`, a time zone string or an - integer offset. + Additional time zone names / aliases which may be present in the + string. This argument maps time zone names (and optionally offsets + from those time zones) to time zones. This parameter can be a + dictionary with timezone aliases mapping time zone names to time + zones or a function taking two parameters (``tzname`` and + ``tzoffset``) and returning a time zone. + + The timezones to which the names are mapped can be an integer + offset from UTC in minutes or a :class:`tzinfo` object. + + .. doctest:: + :options: +NORMALIZE_WHITESPACE + + >>> from dateutil.parser import parse + >>> from dateutil.tz import gettz + >>> tzinfos = {"BRST": -10800, "CST": gettz("America/Chicago")} + >>> parse("2012-01-19 17:21:00 BRST", tzinfos=tzinfos) + datetime.datetime(2012, 1, 19, 17, 21, tzinfo=tzoffset(u'BRST', -10800)) + >>> parse("2012-01-19 17:21:00 CST", tzinfos=tzinfos) + datetime.datetime(2012, 1, 19, 17, 21, + tzinfo=tzfile('/usr/share/zoneinfo/America/Chicago')) + + This parameter is ignored if ``ignoretz`` is set. :param dayfirst: Whether to interpret the first value in an ambiguous 3-integer date - (e.g. 01/05/09) as the day (`True`) or month (`False`). If - `yearfirst` is set to `True`, this distinguishes between YDM and - YMD. If set to `None`, this value is retrieved from the current - :class:`parserinfo` object (which itself defaults to `False`). + (e.g. 01/05/09) as the day (``True``) or month (``False``). If + ``yearfirst`` is set to ``True``, this distinguishes between YDM and + YMD. If set to ``None``, this value is retrieved from the current + :class:`parserinfo` object (which itself defaults to ``False``). :param yearfirst: Whether to interpret the first value in an ambiguous 3-integer date - (e.g. 01/05/09) as the year. If `True`, the first number is taken to + (e.g. 01/05/09) as the year. If ``True``, the first number is taken to be the year, otherwise the last number is taken to be the year. If - this is set to `None`, the value is retrieved from the current - :class:`parserinfo` object (which itself defaults to `False`). + this is set to ``None``, the value is retrieved from the current + :class:`parserinfo` object (which itself defaults to ``False``). :param fuzzy: Whether to allow fuzzy parsing, allowing for string like "Today is January 1, 2047 at 8:21:00AM". :param fuzzy_with_tokens: - If `True`, `fuzzy` is automatically set to True, and the parser will - return a tuple where the first element is the parsed - `datetime.datetime` datetimestamp and the second element is a tuple - containing the portions of the string which were ignored, e.g. - "Today is January 1, 2047 at 8:21:00AM" should return - `(datetime.datetime(2011, 1, 1, 8, 21), (u'Today is ', u' ', u'at '))` + If ``True``, ``fuzzy`` is automatically set to True, and the parser + will return a tuple where the first element is the parsed + :class:`datetime.datetime` datetimestamp and the second element is + a tuple containing the portions of the string which were ignored: + + .. doctest:: + + >>> from dateutil.parser import parse + >>> parse("Today is January 1, 2047 at 8:21:00AM", fuzzy_with_tokens=True) + (datetime.datetime(2011, 1, 1, 8, 21), (u'Today is ', u' ', u'at ')) + + :return: + Returns a :class:`datetime.datetime` object or, if the + ``fuzzy_with_tokens`` option is ``True``, returns a tuple, the + first element being a :class:`datetime.datetime` object, the second + a tuple containing the fuzzy tokens. + + :raises ValueError: + Raised for invalid or unknown string format, if the provided + :class:`tzinfo` is not in a valid format, or if an invalid date + would be created. + + :raises OverFlowError: + Raised if the parsed date exceeds the largest valid C integer on + your system. """ if parserinfo: return parser(parserinfo).parse(timestr, **kwargs) diff --git a/lib/dateutil/relativedelta.py b/lib/dateutil/relativedelta.py index 557d5210..84d5f834 100644 --- a/lib/dateutil/relativedelta.py +++ b/lib/dateutil/relativedelta.py @@ -423,6 +423,7 @@ Here is the behavior of operations with relativedelta: self.hours == other.hours and self.minutes == other.minutes and self.seconds == other.seconds and + self.microseconds == other.microseconds and self.leapdays == other.leapdays and self.year == other.year and self.month == other.month and diff --git a/lib/dateutil/tz.py b/lib/dateutil/tz.py index 444775e7..6625d989 100644 --- a/lib/dateutil/tz.py +++ b/lib/dateutil/tz.py @@ -104,12 +104,12 @@ class tzoffset(datetime.tzinfo): class tzlocal(datetime.tzinfo): - - _std_offset = datetime.timedelta(seconds=-time.timezone) - if time.daylight: - _dst_offset = datetime.timedelta(seconds=-time.altzone) - else: - _dst_offset = _std_offset + def __init__(self): + self._std_offset = datetime.timedelta(seconds=-time.timezone) + if time.daylight: + self._dst_offset = datetime.timedelta(seconds=-time.altzone) + else: + self._dst_offset = self._std_offset def utcoffset(self, dt): if self._isdst(dt): diff --git a/lib/dateutil/tzwin.py b/lib/dateutil/tzwin.py index e8a82d75..f4e0e248 100644 --- a/lib/dateutil/tzwin.py +++ b/lib/dateutil/tzwin.py @@ -4,6 +4,8 @@ import struct from six.moves import winreg +from .tz import tzname_in_python2 + __all__ = ["tzwin", "tzwinlocal"] ONEWEEK = datetime.timedelta(7) @@ -42,6 +44,7 @@ class tzwinbase(datetime.tzinfo): else: return datetime.timedelta(0) + @tzname_in_python2 def tzname(self, dt): if self._isdst(dt): return self._dstname @@ -89,8 +92,8 @@ class tzwin(tzwinbase): "%s\%s" % (TZKEYNAME, name)) as tzkey: keydict = valuestodict(tzkey) - self._stdname = keydict["Std"].encode("iso-8859-1") - self._dstname = keydict["Dlt"].encode("iso-8859-1") + self._stdname = keydict["Std"] + self._dstname = keydict["Dlt"] self._display = keydict["Display"] @@ -129,8 +132,8 @@ class tzwinlocal(tzwinbase): with winreg.OpenKey(handle, TZLOCALKEYNAME) as tzlocalkey: keydict = valuestodict(tzlocalkey) - self._stdname = keydict["StandardName"].encode("iso-8859-1") - self._dstname = keydict["DaylightName"].encode("iso-8859-1") + self._stdname = keydict["StandardName"] + self._dstname = keydict["DaylightName"] try: with winreg.OpenKey( diff --git a/lib/dateutil/zoneinfo/__init__.py b/lib/dateutil/zoneinfo/__init__.py index ff59639c..8156092e 100644 --- a/lib/dateutil/zoneinfo/__init__.py +++ b/lib/dateutil/zoneinfo/__init__.py @@ -16,14 +16,14 @@ from dateutil.tz import tzfile __all__ = ["gettz", "gettz_db_metadata", "rebuild"] -_ZONEFILENAME = "dateutil-zoneinfo.tar.gz" -_METADATA_FN = 'METADATA' +ZONEFILENAME = "dateutil-zoneinfo.tar.gz" +METADATA_FN = 'METADATA' # python2.6 compatability. Note that TarFile.__exit__ != TarFile.close, but # it's close enough for python2.6 -_tar_open = TarFile.open +tar_open = TarFile.open if not hasattr(TarFile, '__exit__'): - def _tar_open(*args, **kwargs): + def tar_open(*args, **kwargs): return closing(TarFile.open(*args, **kwargs)) @@ -34,7 +34,7 @@ class tzfile(tzfile): def getzoneinfofile_stream(): try: - return BytesIO(get_data(__name__, _ZONEFILENAME)) + return BytesIO(get_data(__name__, ZONEFILENAME)) except IOError as e: # TODO switch to FileNotFoundError? warnings.warn("I/O error({0}): {1}".format(e.errno, e.strerror)) return None @@ -43,7 +43,7 @@ def getzoneinfofile_stream(): class ZoneInfoFile(object): def __init__(self, zonefile_stream=None): if zonefile_stream is not None: - with _tar_open(fileobj=zonefile_stream, mode='r') as tf: + with tar_open(fileobj=zonefile_stream, mode='r') as tf: # dict comprehension does not work on python2.6 # TODO: get back to the nicer syntax when we ditch python2.6 # self.zones = {zf.name: tzfile(tf.extractfile(zf), @@ -52,7 +52,7 @@ class ZoneInfoFile(object): self.zones = dict((zf.name, tzfile(tf.extractfile(zf), filename=zf.name)) for zf in tf.getmembers() - if zf.isfile() and zf.name != _METADATA_FN) + if zf.isfile() and zf.name != METADATA_FN) # deal with links: They'll point to their parent object. Less # waste of memory # links = {zl.name: self.zones[zl.linkname] @@ -62,7 +62,7 @@ class ZoneInfoFile(object): zl.islnk() or zl.issym()) self.zones.update(links) try: - metadata_json = tf.extractfile(tf.getmember(_METADATA_FN)) + metadata_json = tf.extractfile(tf.getmember(METADATA_FN)) metadata_str = metadata_json.read().decode('UTF-8') self.metadata = json.loads(metadata_str) except KeyError: @@ -100,36 +100,3 @@ def gettz_db_metadata(): return _CLASS_ZONE_INSTANCE[0].metadata -def rebuild(filename, tag=None, format="gz", zonegroups=[], metadata=None): - """Rebuild the internal timezone info in dateutil/zoneinfo/zoneinfo*tar* - - filename is the timezone tarball from ftp.iana.org/tz. - - """ - tmpdir = tempfile.mkdtemp() - zonedir = os.path.join(tmpdir, "zoneinfo") - moduledir = os.path.dirname(__file__) - try: - with _tar_open(filename) as tf: - for name in zonegroups: - tf.extract(name, tmpdir) - filepaths = [os.path.join(tmpdir, n) for n in zonegroups] - try: - check_call(["zic", "-d", zonedir] + filepaths) - except OSError as e: - if e.errno == 2: - logging.error( - "Could not find zic. Perhaps you need to install " - "libc-bin or some other package that provides it, " - "or it's not in your PATH?") - raise - # write metadata file - with open(os.path.join(zonedir, _METADATA_FN), 'w') as f: - json.dump(metadata, f, indent=4, sort_keys=True) - target = os.path.join(moduledir, _ZONEFILENAME) - with _tar_open(target, "w:%s" % format) as tf: - for entry in os.listdir(zonedir): - entrypath = os.path.join(zonedir, entry) - tf.add(entrypath, entry) - finally: - shutil.rmtree(tmpdir) diff --git a/lib/dateutil/zoneinfo/rebuild.py b/lib/dateutil/zoneinfo/rebuild.py new file mode 100644 index 00000000..e646148c --- /dev/null +++ b/lib/dateutil/zoneinfo/rebuild.py @@ -0,0 +1,43 @@ +import logging +import os +import tempfile +import shutil +import json +from subprocess import check_call + +from dateutil.zoneinfo import tar_open, METADATA_FN, ZONEFILENAME + + +def rebuild(filename, tag=None, format="gz", zonegroups=[], metadata=None): + """Rebuild the internal timezone info in dateutil/zoneinfo/zoneinfo*tar* + + filename is the timezone tarball from ftp.iana.org/tz. + + """ + tmpdir = tempfile.mkdtemp() + zonedir = os.path.join(tmpdir, "zoneinfo") + moduledir = os.path.dirname(__file__) + try: + with tar_open(filename) as tf: + for name in zonegroups: + tf.extract(name, tmpdir) + filepaths = [os.path.join(tmpdir, n) for n in zonegroups] + try: + check_call(["zic", "-d", zonedir] + filepaths) + except OSError as e: + if e.errno == 2: + logging.error( + "Could not find zic. Perhaps you need to install " + "libc-bin or some other package that provides it, " + "or it's not in your PATH?") + raise + # write metadata file + with open(os.path.join(zonedir, METADATA_FN), 'w') as f: + json.dump(metadata, f, indent=4, sort_keys=True) + target = os.path.join(moduledir, ZONEFILENAME) + with tar_open(target, "w:%s" % format) as tf: + for entry in os.listdir(zonedir): + entrypath = os.path.join(zonedir, entry) + tf.add(entrypath, entry) + finally: + shutil.rmtree(tmpdir) From b485106b5b449a5e0c2402c5b3101ce63f326aad Mon Sep 17 00:00:00 2001 From: JackDandy Date: Sat, 12 Sep 2015 12:00:44 +0100 Subject: [PATCH 2/2] Change to use non-private zonefilename variable. --- sickbeard/network_timezones.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/sickbeard/network_timezones.py b/sickbeard/network_timezones.py index bbaa6a5c..61404a96 100644 --- a/sickbeard/network_timezones.py +++ b/sickbeard/network_timezones.py @@ -48,7 +48,7 @@ def _remove_zoneinfo_failed(filename): # helper to remove old unneeded zoneinfo files def _remove_old_zoneinfo(): - zonefilename = zoneinfo._ZONEFILENAME + zonefilename = zoneinfo.ZONEFILENAME if None is zonefilename: return cur_zoneinfo = ek.ek(basename, zonefilename) @@ -83,7 +83,7 @@ def _update_zoneinfo(): logger.WARNING) return - zonefilename = zoneinfo._ZONEFILENAME + zonefilename = zoneinfo.ZONEFILENAME cur_zoneinfo = zonefilename if None is not cur_zoneinfo: cur_zoneinfo = ek.ek(basename, zonefilename) @@ -222,7 +222,7 @@ def get_network_timezone(network, network_dict): return sb_timezone try: - if zoneinfo._ZONEFILENAME is not None: + if zoneinfo.ZONEFILENAME is not None: try: n_t = tz.gettz(network_dict[network]) except: