Browse Source

Restoring authorship annotation for <dshmatkov@yandex-team.ru>. Commit 2 of 2.

dshmatkov 3 years ago
parent
commit
98201819fe

+ 4 - 4
contrib/python/boto3/ya.make

@@ -6,12 +6,12 @@ VERSION(1.17.112)
 
 LICENSE(Apache-2.0)
 
-PEERDIR( 
-    contrib/python/botocore 
+PEERDIR(
+    contrib/python/botocore
     contrib/python/jmespath
     contrib/python/s3transfer
-) 
- 
+)
+
 NO_LINT()
 
 PY_SRCS(

+ 144 - 144
contrib/python/dateutil/README.rst

@@ -1,168 +1,168 @@
-dateutil - powerful extensions to datetime 
-========================================== 
- 
+dateutil - powerful extensions to datetime
+==========================================
+
 |pypi| |support| |licence|
- 
-|gitter| |readthedocs| 
- 
+
+|gitter| |readthedocs|
+
 |travis| |appveyor| |pipelines| |coverage|
- 
-.. |pypi| image:: https://img.shields.io/pypi/v/python-dateutil.svg?style=flat-square 
-    :target: https://pypi.org/project/python-dateutil/ 
-    :alt: pypi version 
- 
-.. |support| image:: https://img.shields.io/pypi/pyversions/python-dateutil.svg?style=flat-square 
-    :target: https://pypi.org/project/python-dateutil/ 
-    :alt: supported Python version 
- 
-.. |travis| image:: https://img.shields.io/travis/dateutil/dateutil/master.svg?style=flat-square&label=Travis%20Build 
-    :target: https://travis-ci.org/dateutil/dateutil 
-    :alt: travis build status 
- 
-.. |appveyor| image:: https://img.shields.io/appveyor/ci/dateutil/dateutil/master.svg?style=flat-square&logo=appveyor 
-    :target: https://ci.appveyor.com/project/dateutil/dateutil 
-    :alt: appveyor build status 
- 
+
+.. |pypi| image:: https://img.shields.io/pypi/v/python-dateutil.svg?style=flat-square
+    :target: https://pypi.org/project/python-dateutil/
+    :alt: pypi version
+
+.. |support| image:: https://img.shields.io/pypi/pyversions/python-dateutil.svg?style=flat-square
+    :target: https://pypi.org/project/python-dateutil/
+    :alt: supported Python version
+
+.. |travis| image:: https://img.shields.io/travis/dateutil/dateutil/master.svg?style=flat-square&label=Travis%20Build
+    :target: https://travis-ci.org/dateutil/dateutil
+    :alt: travis build status
+
+.. |appveyor| image:: https://img.shields.io/appveyor/ci/dateutil/dateutil/master.svg?style=flat-square&logo=appveyor
+    :target: https://ci.appveyor.com/project/dateutil/dateutil
+    :alt: appveyor build status
+
 .. |pipelines| image:: https://dev.azure.com/pythondateutilazure/dateutil/_apis/build/status/dateutil.dateutil?branchName=master
     :target: https://dev.azure.com/pythondateutilazure/dateutil/_build/latest?definitionId=1&branchName=master
     :alt: azure pipelines build status
 
 .. |coverage| image:: https://codecov.io/gh/dateutil/dateutil/branch/master/graphs/badge.svg?branch=master
     :target: https://codecov.io/gh/dateutil/dateutil?branch=master
-    :alt: Code coverage 
- 
-.. |gitter| image:: https://badges.gitter.im/dateutil/dateutil.svg 
-   :alt: Join the chat at https://gitter.im/dateutil/dateutil 
-   :target: https://gitter.im/dateutil/dateutil 
- 
-.. |licence| image:: https://img.shields.io/pypi/l/python-dateutil.svg?style=flat-square 
-    :target: https://pypi.org/project/python-dateutil/ 
-    :alt: licence 
- 
-.. |readthedocs| image:: https://img.shields.io/readthedocs/dateutil/latest.svg?style=flat-square&label=Read%20the%20Docs 
-   :alt: Read the documentation at https://dateutil.readthedocs.io/en/latest/ 
-   :target: https://dateutil.readthedocs.io/en/latest/ 
- 
-The `dateutil` module provides powerful extensions to 
-the standard `datetime` module, available in Python. 
- 
+    :alt: Code coverage
+
+.. |gitter| image:: https://badges.gitter.im/dateutil/dateutil.svg
+   :alt: Join the chat at https://gitter.im/dateutil/dateutil
+   :target: https://gitter.im/dateutil/dateutil
+
+.. |licence| image:: https://img.shields.io/pypi/l/python-dateutil.svg?style=flat-square
+    :target: https://pypi.org/project/python-dateutil/
+    :alt: licence
+
+.. |readthedocs| image:: https://img.shields.io/readthedocs/dateutil/latest.svg?style=flat-square&label=Read%20the%20Docs
+   :alt: Read the documentation at https://dateutil.readthedocs.io/en/latest/
+   :target: https://dateutil.readthedocs.io/en/latest/
+
+The `dateutil` module provides powerful extensions to
+the standard `datetime` module, available in Python.
+
 Installation
 ============
 `dateutil` can be installed from PyPI using `pip` (note that the package name is
 different from the importable name)::
- 
+
     pip install python-dateutil
 
-Download 
-======== 
-dateutil is available on PyPI 
-https://pypi.org/project/python-dateutil/ 
- 
-The documentation is hosted at: 
-https://dateutil.readthedocs.io/en/stable/ 
- 
-Code 
-==== 
+Download
+========
+dateutil is available on PyPI
+https://pypi.org/project/python-dateutil/
+
+The documentation is hosted at:
+https://dateutil.readthedocs.io/en/stable/
+
+Code
+====
 The code and issue tracker are hosted on GitHub:
-https://github.com/dateutil/dateutil/ 
- 
-Features 
-======== 
- 
-* Computing of relative deltas (next month, next year, 
+https://github.com/dateutil/dateutil/
+
+Features
+========
+
+* Computing of relative deltas (next month, next year,
   next Monday, last week of month, etc);
-* Computing of relative deltas between two given 
-  date and/or datetime objects; 
-* Computing of dates based on very flexible recurrence rules, 
-  using a superset of the `iCalendar <https://www.ietf.org/rfc/rfc2445.txt>`_ 
-  specification. Parsing of RFC strings is supported as well. 
-* Generic parsing of dates in almost any string format; 
-* Timezone (tzinfo) implementations for tzfile(5) format 
-  files (/etc/localtime, /usr/share/zoneinfo, etc), TZ 
-  environment string (in all known formats), iCalendar 
-  format files, given ranges (with help from relative deltas), 
-  local machine timezone, fixed offset timezone, UTC timezone, 
-  and Windows registry-based time zones. 
-* Internal up-to-date world timezone information based on 
-  Olson's database. 
-* Computing of Easter Sunday dates for any given year, 
-  using Western, Orthodox or Julian algorithms; 
-* A comprehensive test suite. 
- 
-Quick example 
-============= 
-Here's a snapshot, just to give an idea about the power of the 
-package. For more examples, look at the documentation. 
- 
-Suppose you want to know how much time is left, in 
-years/months/days/etc, before the next easter happening on a 
-year with a Friday 13th in August, and you want to get today's 
-date out of the "date" unix system command. Here is the code: 
- 
-.. doctest:: readmeexample 
- 
-    >>> from dateutil.relativedelta import * 
-    >>> from dateutil.easter import * 
-    >>> from dateutil.rrule import * 
-    >>> from dateutil.parser import * 
-    >>> from datetime import * 
-    >>> now = parse("Sat Oct 11 17:13:46 UTC 2003") 
-    >>> today = now.date() 
-    >>> year = rrule(YEARLY,dtstart=now,bymonth=8,bymonthday=13,byweekday=FR)[0].year 
-    >>> rdelta = relativedelta(easter(year), today) 
-    >>> print("Today is: %s" % today) 
-    Today is: 2003-10-11 
-    >>> print("Year with next Aug 13th on a Friday is: %s" % year) 
-    Year with next Aug 13th on a Friday is: 2004 
-    >>> print("How far is the Easter of that year: %s" % rdelta) 
-    How far is the Easter of that year: relativedelta(months=+6) 
-    >>> print("And the Easter of that year is: %s" % (today+rdelta)) 
-    And the Easter of that year is: 2004-04-11 
- 
-Being exactly 6 months ahead was **really** a coincidence :) 
- 
-Contributing 
-============ 
- 
-We welcome many types of contributions - bug reports, pull requests (code, infrastructure or documentation fixes). For more information about how to contribute to the project, see the ``CONTRIBUTING.md`` file in the repository. 
- 
- 
-Author 
-====== 
-The dateutil module was written by Gustavo Niemeyer <gustavo@niemeyer.net> 
-in 2003. 
- 
-It is maintained by: 
- 
-* Gustavo Niemeyer <gustavo@niemeyer.net> 2003-2011 
-* Tomi Pieviläinen <tomi.pievilainen@iki.fi> 2012-2014 
-* Yaron de Leeuw <me@jarondl.net> 2014-2016 
-* Paul Ganssle <paul@ganssle.io> 2015- 
- 
+* Computing of relative deltas between two given
+  date and/or datetime objects;
+* Computing of dates based on very flexible recurrence rules,
+  using a superset of the `iCalendar <https://www.ietf.org/rfc/rfc2445.txt>`_
+  specification. Parsing of RFC strings is supported as well.
+* Generic parsing of dates in almost any string format;
+* Timezone (tzinfo) implementations for tzfile(5) format
+  files (/etc/localtime, /usr/share/zoneinfo, etc), TZ
+  environment string (in all known formats), iCalendar
+  format files, given ranges (with help from relative deltas),
+  local machine timezone, fixed offset timezone, UTC timezone,
+  and Windows registry-based time zones.
+* Internal up-to-date world timezone information based on
+  Olson's database.
+* Computing of Easter Sunday dates for any given year,
+  using Western, Orthodox or Julian algorithms;
+* A comprehensive test suite.
+
+Quick example
+=============
+Here's a snapshot, just to give an idea about the power of the
+package. For more examples, look at the documentation.
+
+Suppose you want to know how much time is left, in
+years/months/days/etc, before the next easter happening on a
+year with a Friday 13th in August, and you want to get today's
+date out of the "date" unix system command. Here is the code:
+
+.. doctest:: readmeexample
+
+    >>> from dateutil.relativedelta import *
+    >>> from dateutil.easter import *
+    >>> from dateutil.rrule import *
+    >>> from dateutil.parser import *
+    >>> from datetime import *
+    >>> now = parse("Sat Oct 11 17:13:46 UTC 2003")
+    >>> today = now.date()
+    >>> year = rrule(YEARLY,dtstart=now,bymonth=8,bymonthday=13,byweekday=FR)[0].year
+    >>> rdelta = relativedelta(easter(year), today)
+    >>> print("Today is: %s" % today)
+    Today is: 2003-10-11
+    >>> print("Year with next Aug 13th on a Friday is: %s" % year)
+    Year with next Aug 13th on a Friday is: 2004
+    >>> print("How far is the Easter of that year: %s" % rdelta)
+    How far is the Easter of that year: relativedelta(months=+6)
+    >>> print("And the Easter of that year is: %s" % (today+rdelta))
+    And the Easter of that year is: 2004-04-11
+
+Being exactly 6 months ahead was **really** a coincidence :)
+
+Contributing
+============
+
+We welcome many types of contributions - bug reports, pull requests (code, infrastructure or documentation fixes). For more information about how to contribute to the project, see the ``CONTRIBUTING.md`` file in the repository.
+
+
+Author
+======
+The dateutil module was written by Gustavo Niemeyer <gustavo@niemeyer.net>
+in 2003.
+
+It is maintained by:
+
+* Gustavo Niemeyer <gustavo@niemeyer.net> 2003-2011
+* Tomi Pieviläinen <tomi.pievilainen@iki.fi> 2012-2014
+* Yaron de Leeuw <me@jarondl.net> 2014-2016
+* Paul Ganssle <paul@ganssle.io> 2015-
+
 Starting with version 2.4.1 and running until 2.8.2, all source and binary
 distributions will be signed by a PGP key that has, at the very least, been
 signed by the key which made the previous release. A table of release signing
 keys can be found below:
- 
-===========  ============================ 
-Releases     Signing key fingerprint 
-===========  ============================ 
+
+===========  ============================
+Releases     Signing key fingerprint
+===========  ============================
 2.4.1-2.8.2  `6B49 ACBA DCF6 BD1C A206 67AB CD54 FCE3 D964 BEFB`_ 
-===========  ============================ 
- 
+===========  ============================
+
 New releases *may* have signed tags, but binary and source distributions
 uploaded to PyPI will no longer have GPG signatures attached.
- 
-Contact 
-======= 
-Our mailing list is available at `dateutil@python.org <https://mail.python.org/mailman/listinfo/dateutil>`_. As it is hosted by the PSF, it is subject to the `PSF code of 
+
+Contact
+=======
+Our mailing list is available at `dateutil@python.org <https://mail.python.org/mailman/listinfo/dateutil>`_. As it is hosted by the PSF, it is subject to the `PSF code of
 conduct <https://www.python.org/psf/conduct/>`_.
- 
-License 
-======= 
- 
-All contributions after December 1, 2017 released under dual license - either `Apache 2.0 License <https://www.apache.org/licenses/LICENSE-2.0>`_ or the `BSD 3-Clause License <https://opensource.org/licenses/BSD-3-Clause>`_. Contributions before December 1, 2017 - except those those explicitly relicensed - are released only under the BSD 3-Clause License. 
- 
- 
-.. _6B49 ACBA DCF6 BD1C A206 67AB CD54 FCE3 D964 BEFB: 
-   https://pgp.mit.edu/pks/lookup?op=vindex&search=0xCD54FCE3D964BEFB 
+
+License
+=======
+
+All contributions after December 1, 2017 released under dual license - either `Apache 2.0 License <https://www.apache.org/licenses/LICENSE-2.0>`_ or the `BSD 3-Clause License <https://opensource.org/licenses/BSD-3-Clause>`_. Contributions before December 1, 2017 - except those those explicitly relicensed - are released only under the BSD 3-Clause License.
+
+
+.. _6B49 ACBA DCF6 BD1C A206 67AB CD54 FCE3 D964 BEFB:
+   https://pgp.mit.edu/pks/lookup?op=vindex&search=0xCD54FCE3D964BEFB

+ 7 - 7
contrib/python/dateutil/dateutil/__init__.py

@@ -1,8 +1,8 @@
 # -*- coding: utf-8 -*-
-try: 
-    from ._version import version as __version__ 
-except ImportError: 
-    __version__ = 'unknown' 
- 
-__all__ = ['easter', 'parser', 'relativedelta', 'rrule', 'tz', 
-           'utils', 'zoneinfo'] 
+try:
+    from ._version import version as __version__
+except ImportError:
+    __version__ = 'unknown'
+
+__all__ = ['easter', 'parser', 'relativedelta', 'rrule', 'tz',
+           'utils', 'zoneinfo']

+ 11 - 11
contrib/python/dateutil/dateutil/_common.py

@@ -24,20 +24,20 @@ class weekday(object):
             return False
         return True
 
-    def __hash__(self): 
-        return hash(( 
-          self.weekday, 
-          self.n, 
-        )) 
-
-    def __ne__(self, other): 
-        return not (self == other) 
- 
+    def __hash__(self):
+        return hash((
+          self.weekday,
+          self.n,
+        ))
+
+    def __ne__(self, other):
+        return not (self == other)
+
     def __repr__(self):
         s = ("MO", "TU", "WE", "TH", "FR", "SA", "SU")[self.weekday]
         if not self.n:
             return s
         else:
             return "%s(%+d)" % (s, self.n)
- 
-# vim:ts=4:sw=4:et 
+
+# vim:ts=4:sw=4:et

+ 2 - 2
contrib/python/dateutil/dateutil/easter.py

@@ -41,11 +41,11 @@ def easter(year, method=EASTER_WESTERN):
 
     More about the algorithm may be found at:
 
-    `GM Arts: Easter Algorithms <http://www.gmarts.org/index.php?go=415>`_ 
+    `GM Arts: Easter Algorithms <http://www.gmarts.org/index.php?go=415>`_
 
     and
 
-    `The Calendar FAQ: Easter <https://www.tondering.dk/claus/cal/easter.php>`_ 
+    `The Calendar FAQ: Easter <https://www.tondering.dk/claus/cal/easter.php>`_
 
     """
 

+ 57 - 57
contrib/python/dateutil/dateutil/parser/__init__.py

@@ -1,61 +1,61 @@
-# -*- coding: utf-8 -*- 
+# -*- coding: utf-8 -*-
 from ._parser import parse, parser, parserinfo, ParserError
-from ._parser import DEFAULTPARSER, DEFAULTTZPARSER 
-from ._parser import UnknownTimezoneWarning 
- 
-from ._parser import __doc__ 
- 
-from .isoparser import isoparser, isoparse 
- 
-__all__ = ['parse', 'parser', 'parserinfo', 
-           'isoparse', 'isoparser', 
+from ._parser import DEFAULTPARSER, DEFAULTTZPARSER
+from ._parser import UnknownTimezoneWarning
+
+from ._parser import __doc__
+
+from .isoparser import isoparser, isoparse
+
+__all__ = ['parse', 'parser', 'parserinfo',
+           'isoparse', 'isoparser',
            'ParserError',
-           'UnknownTimezoneWarning'] 
- 
- 
-### 
-# Deprecate portions of the private interface so that downstream code that 
-# is improperly relying on it is given *some* notice. 
- 
- 
-def __deprecated_private_func(f): 
-    from functools import wraps 
-    import warnings 
- 
-    msg = ('{name} is a private function and may break without warning, ' 
-           'it will be moved and or renamed in future versions.') 
-    msg = msg.format(name=f.__name__) 
- 
-    @wraps(f) 
-    def deprecated_func(*args, **kwargs): 
-        warnings.warn(msg, DeprecationWarning) 
-        return f(*args, **kwargs) 
- 
-    return deprecated_func 
- 
-def __deprecate_private_class(c): 
-    import warnings 
- 
-    msg = ('{name} is a private class and may break without warning, ' 
-           'it will be moved and or renamed in future versions.') 
-    msg = msg.format(name=c.__name__) 
- 
-    class private_class(c): 
-        __doc__ = c.__doc__ 
- 
-        def __init__(self, *args, **kwargs): 
-            warnings.warn(msg, DeprecationWarning) 
-            super(private_class, self).__init__(*args, **kwargs) 
- 
-    private_class.__name__ = c.__name__ 
- 
-    return private_class 
- 
- 
+           'UnknownTimezoneWarning']
+
+
+###
+# Deprecate portions of the private interface so that downstream code that
+# is improperly relying on it is given *some* notice.
+
+
+def __deprecated_private_func(f):
+    from functools import wraps
+    import warnings
+
+    msg = ('{name} is a private function and may break without warning, '
+           'it will be moved and or renamed in future versions.')
+    msg = msg.format(name=f.__name__)
+
+    @wraps(f)
+    def deprecated_func(*args, **kwargs):
+        warnings.warn(msg, DeprecationWarning)
+        return f(*args, **kwargs)
+
+    return deprecated_func
+
+def __deprecate_private_class(c):
+    import warnings
+
+    msg = ('{name} is a private class and may break without warning, '
+           'it will be moved and or renamed in future versions.')
+    msg = msg.format(name=c.__name__)
+
+    class private_class(c):
+        __doc__ = c.__doc__
+
+        def __init__(self, *args, **kwargs):
+            warnings.warn(msg, DeprecationWarning)
+            super(private_class, self).__init__(*args, **kwargs)
+
+    private_class.__name__ = c.__name__
+
+    return private_class
+
+
 from ._parser import _timelex, _resultbase
 from ._parser import _tzparser, _parsetz
- 
-_timelex = __deprecate_private_class(_timelex) 
-_tzparser = __deprecate_private_class(_tzparser) 
-_resultbase = __deprecate_private_class(_resultbase) 
-_parsetz = __deprecated_private_func(_parsetz) 
+
+_timelex = __deprecate_private_class(_timelex)
+_tzparser = __deprecate_private_class(_tzparser)
+_resultbase = __deprecate_private_class(_resultbase)
+_parsetz = __deprecated_private_func(_parsetz)

File diff suppressed because it is too large
+ 1106 - 1106
contrib/python/dateutil/dateutil/parser/_parser.py


+ 384 - 384
contrib/python/dateutil/dateutil/parser/isoparser.py

@@ -1,352 +1,352 @@
-# -*- coding: utf-8 -*- 
-""" 
-This module offers a parser for ISO-8601 strings 
- 
-It is intended to support all valid date, time and datetime formats per the 
-ISO-8601 specification. 
- 
-..versionadded:: 2.7.0 
-""" 
-from datetime import datetime, timedelta, time, date 
-import calendar 
-from dateutil import tz 
- 
-from functools import wraps 
- 
-import re 
-import six 
- 
-__all__ = ["isoparse", "isoparser"] 
- 
- 
-def _takes_ascii(f): 
-    @wraps(f) 
-    def func(self, str_in, *args, **kwargs): 
-        # If it's a stream, read the whole thing 
-        str_in = getattr(str_in, 'read', lambda: str_in)() 
- 
-        # If it's unicode, turn it into bytes, since ISO-8601 only covers ASCII 
-        if isinstance(str_in, six.text_type): 
-            # ASCII is the same in UTF-8 
-            try: 
-                str_in = str_in.encode('ascii') 
-            except UnicodeEncodeError as e: 
-                msg = 'ISO-8601 strings should contain only ASCII characters' 
-                six.raise_from(ValueError(msg), e) 
- 
-        return f(self, str_in, *args, **kwargs) 
- 
-    return func 
- 
- 
-class isoparser(object): 
-    def __init__(self, sep=None): 
-        """ 
-        :param sep: 
-            A single character that separates date and time portions. If 
-            ``None``, the parser will accept any single character. 
-            For strict ISO-8601 adherence, pass ``'T'``. 
-        """ 
-        if sep is not None: 
-            if (len(sep) != 1 or ord(sep) >= 128 or sep in '0123456789'): 
-                raise ValueError('Separator must be a single, non-numeric ' + 
-                                 'ASCII character') 
- 
-            sep = sep.encode('ascii') 
- 
-        self._sep = sep 
- 
-    @_takes_ascii 
-    def isoparse(self, dt_str): 
-        """ 
-        Parse an ISO-8601 datetime string into a :class:`datetime.datetime`. 
- 
-        An ISO-8601 datetime string consists of a date portion, followed 
-        optionally by a time portion - the date and time portions are separated 
-        by a single character separator, which is ``T`` in the official 
-        standard. Incomplete date formats (such as ``YYYY-MM``) may *not* be 
-        combined with a time portion. 
- 
-        Supported date formats are: 
- 
-        Common: 
- 
-        - ``YYYY`` 
-        - ``YYYY-MM`` or ``YYYYMM`` 
-        - ``YYYY-MM-DD`` or ``YYYYMMDD`` 
- 
-        Uncommon: 
- 
-        - ``YYYY-Www`` or ``YYYYWww`` - ISO week (day defaults to 0) 
-        - ``YYYY-Www-D`` or ``YYYYWwwD`` - ISO week and day 
- 
-        The ISO week and day numbering follows the same logic as 
-        :func:`datetime.date.isocalendar`. 
- 
-        Supported time formats are: 
- 
-        - ``hh`` 
-        - ``hh:mm`` or ``hhmm`` 
-        - ``hh:mm:ss`` or ``hhmmss`` 
+# -*- coding: utf-8 -*-
+"""
+This module offers a parser for ISO-8601 strings
+
+It is intended to support all valid date, time and datetime formats per the
+ISO-8601 specification.
+
+..versionadded:: 2.7.0
+"""
+from datetime import datetime, timedelta, time, date
+import calendar
+from dateutil import tz
+
+from functools import wraps
+
+import re
+import six
+
+__all__ = ["isoparse", "isoparser"]
+
+
+def _takes_ascii(f):
+    @wraps(f)
+    def func(self, str_in, *args, **kwargs):
+        # If it's a stream, read the whole thing
+        str_in = getattr(str_in, 'read', lambda: str_in)()
+
+        # If it's unicode, turn it into bytes, since ISO-8601 only covers ASCII
+        if isinstance(str_in, six.text_type):
+            # ASCII is the same in UTF-8
+            try:
+                str_in = str_in.encode('ascii')
+            except UnicodeEncodeError as e:
+                msg = 'ISO-8601 strings should contain only ASCII characters'
+                six.raise_from(ValueError(msg), e)
+
+        return f(self, str_in, *args, **kwargs)
+
+    return func
+
+
+class isoparser(object):
+    def __init__(self, sep=None):
+        """
+        :param sep:
+            A single character that separates date and time portions. If
+            ``None``, the parser will accept any single character.
+            For strict ISO-8601 adherence, pass ``'T'``.
+        """
+        if sep is not None:
+            if (len(sep) != 1 or ord(sep) >= 128 or sep in '0123456789'):
+                raise ValueError('Separator must be a single, non-numeric ' +
+                                 'ASCII character')
+
+            sep = sep.encode('ascii')
+
+        self._sep = sep
+
+    @_takes_ascii
+    def isoparse(self, dt_str):
+        """
+        Parse an ISO-8601 datetime string into a :class:`datetime.datetime`.
+
+        An ISO-8601 datetime string consists of a date portion, followed
+        optionally by a time portion - the date and time portions are separated
+        by a single character separator, which is ``T`` in the official
+        standard. Incomplete date formats (such as ``YYYY-MM``) may *not* be
+        combined with a time portion.
+
+        Supported date formats are:
+
+        Common:
+
+        - ``YYYY``
+        - ``YYYY-MM`` or ``YYYYMM``
+        - ``YYYY-MM-DD`` or ``YYYYMMDD``
+
+        Uncommon:
+
+        - ``YYYY-Www`` or ``YYYYWww`` - ISO week (day defaults to 0)
+        - ``YYYY-Www-D`` or ``YYYYWwwD`` - ISO week and day
+
+        The ISO week and day numbering follows the same logic as
+        :func:`datetime.date.isocalendar`.
+
+        Supported time formats are:
+
+        - ``hh``
+        - ``hh:mm`` or ``hhmm``
+        - ``hh:mm:ss`` or ``hhmmss``
         - ``hh:mm:ss.ssssss`` (Up to 6 sub-second digits)
- 
-        Midnight is a special case for `hh`, as the standard supports both 
+
+        Midnight is a special case for `hh`, as the standard supports both
         00:00 and 24:00 as a representation. The decimal separator can be
         either a dot or a comma.
- 
-
-        .. caution:: 
- 
-            Support for fractional components other than seconds is part of the 
-            ISO-8601 standard, but is not currently implemented in this parser. 
- 
-        Supported time zone offset formats are: 
- 
-        - `Z` (UTC) 
-        - `±HH:MM` 
-        - `±HHMM` 
-        - `±HH` 
- 
-        Offsets will be represented as :class:`dateutil.tz.tzoffset` objects, 
-        with the exception of UTC, which will be represented as 
-        :class:`dateutil.tz.tzutc`. Time zone offsets equivalent to UTC (such 
-        as `+00:00`) will also be represented as :class:`dateutil.tz.tzutc`. 
- 
-        :param dt_str: 
-            A string or stream containing only an ISO-8601 datetime string 
- 
-        :return: 
-            Returns a :class:`datetime.datetime` representing the string. 
-            Unspecified components default to their lowest value. 
- 
-        .. warning:: 
- 
-            As of version 2.7.0, the strictness of the parser should not be 
-            considered a stable part of the contract. Any valid ISO-8601 string 
-            that parses correctly with the default settings will continue to 
-            parse correctly in future versions, but invalid strings that 
-            currently fail (e.g. ``2017-01-01T00:00+00:00:00``) are not 
-            guaranteed to continue failing in future versions if they encode 
-            a valid date. 
- 
-        .. versionadded:: 2.7.0 
-        """ 
-        components, pos = self._parse_isodate(dt_str) 
- 
-        if len(dt_str) > pos: 
-            if self._sep is None or dt_str[pos:pos + 1] == self._sep: 
-                components += self._parse_isotime(dt_str[pos + 1:]) 
-            else: 
-                raise ValueError('String contains unknown ISO components') 
- 
+
+
+        .. caution::
+
+            Support for fractional components other than seconds is part of the
+            ISO-8601 standard, but is not currently implemented in this parser.
+
+        Supported time zone offset formats are:
+
+        - `Z` (UTC)
+        - `±HH:MM`
+        - `±HHMM`
+        - `±HH`
+
+        Offsets will be represented as :class:`dateutil.tz.tzoffset` objects,
+        with the exception of UTC, which will be represented as
+        :class:`dateutil.tz.tzutc`. Time zone offsets equivalent to UTC (such
+        as `+00:00`) will also be represented as :class:`dateutil.tz.tzutc`.
+
+        :param dt_str:
+            A string or stream containing only an ISO-8601 datetime string
+
+        :return:
+            Returns a :class:`datetime.datetime` representing the string.
+            Unspecified components default to their lowest value.
+
+        .. warning::
+
+            As of version 2.7.0, the strictness of the parser should not be
+            considered a stable part of the contract. Any valid ISO-8601 string
+            that parses correctly with the default settings will continue to
+            parse correctly in future versions, but invalid strings that
+            currently fail (e.g. ``2017-01-01T00:00+00:00:00``) are not
+            guaranteed to continue failing in future versions if they encode
+            a valid date.
+
+        .. versionadded:: 2.7.0
+        """
+        components, pos = self._parse_isodate(dt_str)
+
+        if len(dt_str) > pos:
+            if self._sep is None or dt_str[pos:pos + 1] == self._sep:
+                components += self._parse_isotime(dt_str[pos + 1:])
+            else:
+                raise ValueError('String contains unknown ISO components')
+
         if len(components) > 3 and components[3] == 24:
             components[3] = 0
             return datetime(*components) + timedelta(days=1)
 
-        return datetime(*components) 
- 
-    @_takes_ascii 
-    def parse_isodate(self, datestr): 
-        """ 
-        Parse the date portion of an ISO string. 
- 
-        :param datestr: 
-            The string portion of an ISO string, without a separator 
- 
-        :return: 
-            Returns a :class:`datetime.date` object 
-        """ 
-        components, pos = self._parse_isodate(datestr) 
-        if pos < len(datestr): 
-            raise ValueError('String contains unknown ISO ' + 
+        return datetime(*components)
+
+    @_takes_ascii
+    def parse_isodate(self, datestr):
+        """
+        Parse the date portion of an ISO string.
+
+        :param datestr:
+            The string portion of an ISO string, without a separator
+
+        :return:
+            Returns a :class:`datetime.date` object
+        """
+        components, pos = self._parse_isodate(datestr)
+        if pos < len(datestr):
+            raise ValueError('String contains unknown ISO ' +
                              'components: {!r}'.format(datestr.decode('ascii')))
-        return date(*components) 
- 
-    @_takes_ascii 
-    def parse_isotime(self, timestr): 
-        """ 
-        Parse the time portion of an ISO string. 
- 
-        :param timestr: 
-            The time portion of an ISO string, without a separator 
- 
-        :return: 
-            Returns a :class:`datetime.time` object 
-        """ 
+        return date(*components)
+
+    @_takes_ascii
+    def parse_isotime(self, timestr):
+        """
+        Parse the time portion of an ISO string.
+
+        :param timestr:
+            The time portion of an ISO string, without a separator
+
+        :return:
+            Returns a :class:`datetime.time` object
+        """
         components = self._parse_isotime(timestr)
         if components[0] == 24:
             components[0] = 0
         return time(*components)
- 
-    @_takes_ascii 
-    def parse_tzstr(self, tzstr, zero_as_utc=True): 
-        """ 
-        Parse a valid ISO time zone string. 
- 
-        See :func:`isoparser.isoparse` for details on supported formats. 
- 
-        :param tzstr: 
-            A string representing an ISO time zone offset 
- 
-        :param zero_as_utc: 
-            Whether to return :class:`dateutil.tz.tzutc` for zero-offset zones 
- 
-        :return: 
-            Returns :class:`dateutil.tz.tzoffset` for offsets and 
-            :class:`dateutil.tz.tzutc` for ``Z`` and (if ``zero_as_utc`` is 
-            specified) offsets equivalent to UTC. 
-        """ 
-        return self._parse_tzstr(tzstr, zero_as_utc=zero_as_utc) 
- 
-    # Constants 
-    _DATE_SEP = b'-' 
-    _TIME_SEP = b':' 
+
+    @_takes_ascii
+    def parse_tzstr(self, tzstr, zero_as_utc=True):
+        """
+        Parse a valid ISO time zone string.
+
+        See :func:`isoparser.isoparse` for details on supported formats.
+
+        :param tzstr:
+            A string representing an ISO time zone offset
+
+        :param zero_as_utc:
+            Whether to return :class:`dateutil.tz.tzutc` for zero-offset zones
+
+        :return:
+            Returns :class:`dateutil.tz.tzoffset` for offsets and
+            :class:`dateutil.tz.tzutc` for ``Z`` and (if ``zero_as_utc`` is
+            specified) offsets equivalent to UTC.
+        """
+        return self._parse_tzstr(tzstr, zero_as_utc=zero_as_utc)
+
+    # Constants
+    _DATE_SEP = b'-'
+    _TIME_SEP = b':'
     _FRACTION_REGEX = re.compile(b'[\\.,]([0-9]+)')
- 
-    def _parse_isodate(self, dt_str): 
-        try: 
-            return self._parse_isodate_common(dt_str) 
-        except ValueError: 
-            return self._parse_isodate_uncommon(dt_str) 
- 
-    def _parse_isodate_common(self, dt_str): 
-        len_str = len(dt_str) 
-        components = [1, 1, 1] 
- 
-        if len_str < 4: 
-            raise ValueError('ISO string too short') 
- 
-        # Year 
-        components[0] = int(dt_str[0:4]) 
-        pos = 4 
-        if pos >= len_str: 
-            return components, pos 
- 
-        has_sep = dt_str[pos:pos + 1] == self._DATE_SEP 
-        if has_sep: 
-            pos += 1 
- 
-        # Month 
-        if len_str - pos < 2: 
-            raise ValueError('Invalid common month') 
- 
-        components[1] = int(dt_str[pos:pos + 2]) 
-        pos += 2 
- 
-        if pos >= len_str: 
-            if has_sep: 
-                return components, pos 
-            else: 
-                raise ValueError('Invalid ISO format') 
- 
-        if has_sep: 
-            if dt_str[pos:pos + 1] != self._DATE_SEP: 
-                raise ValueError('Invalid separator in ISO string') 
-            pos += 1 
- 
-        # Day 
-        if len_str - pos < 2: 
-            raise ValueError('Invalid common day') 
-        components[2] = int(dt_str[pos:pos + 2]) 
-        return components, pos + 2 
- 
-    def _parse_isodate_uncommon(self, dt_str): 
-        if len(dt_str) < 4: 
-            raise ValueError('ISO string too short') 
- 
-        # All ISO formats start with the year 
-        year = int(dt_str[0:4]) 
- 
-        has_sep = dt_str[4:5] == self._DATE_SEP 
- 
-        pos = 4 + has_sep       # Skip '-' if it's there 
-        if dt_str[pos:pos + 1] == b'W': 
-            # YYYY-?Www-?D? 
-            pos += 1 
-            weekno = int(dt_str[pos:pos + 2]) 
-            pos += 2 
- 
-            dayno = 1 
-            if len(dt_str) > pos: 
-                if (dt_str[pos:pos + 1] == self._DATE_SEP) != has_sep: 
-                    raise ValueError('Inconsistent use of dash separator') 
- 
-                pos += has_sep 
- 
-                dayno = int(dt_str[pos:pos + 1]) 
-                pos += 1 
- 
-            base_date = self._calculate_weekdate(year, weekno, dayno) 
-        else: 
-            # YYYYDDD or YYYY-DDD 
-            if len(dt_str) - pos < 3: 
-                raise ValueError('Invalid ordinal day') 
- 
-            ordinal_day = int(dt_str[pos:pos + 3]) 
-            pos += 3 
- 
-            if ordinal_day < 1 or ordinal_day > (365 + calendar.isleap(year)): 
-                raise ValueError('Invalid ordinal day' + 
-                                 ' {} for year {}'.format(ordinal_day, year)) 
- 
-            base_date = date(year, 1, 1) + timedelta(days=ordinal_day - 1) 
- 
-        components = [base_date.year, base_date.month, base_date.day] 
-        return components, pos 
- 
-    def _calculate_weekdate(self, year, week, day): 
-        """ 
-        Calculate the day of corresponding to the ISO year-week-day calendar. 
- 
-        This function is effectively the inverse of 
-        :func:`datetime.date.isocalendar`. 
- 
-        :param year: 
-            The year in the ISO calendar 
- 
-        :param week: 
-            The week in the ISO calendar - range is [1, 53] 
- 
-        :param day: 
-            The day in the ISO calendar - range is [1 (MON), 7 (SUN)] 
- 
-        :return: 
-            Returns a :class:`datetime.date` 
-        """ 
-        if not 0 < week < 54: 
-            raise ValueError('Invalid week: {}'.format(week)) 
- 
-        if not 0 < day < 8:     # Range is 1-7 
-            raise ValueError('Invalid weekday: {}'.format(day)) 
- 
-        # Get week 1 for the specific year: 
-        jan_4 = date(year, 1, 4)   # Week 1 always has January 4th in it 
-        week_1 = jan_4 - timedelta(days=jan_4.isocalendar()[2] - 1) 
- 
-        # Now add the specific number of weeks and days to get what we want 
-        week_offset = (week - 1) * 7 + (day - 1) 
-        return week_1 + timedelta(days=week_offset) 
- 
-    def _parse_isotime(self, timestr): 
-        len_str = len(timestr) 
-        components = [0, 0, 0, 0, None] 
-        pos = 0 
-        comp = -1 
- 
+
+    def _parse_isodate(self, dt_str):
+        try:
+            return self._parse_isodate_common(dt_str)
+        except ValueError:
+            return self._parse_isodate_uncommon(dt_str)
+
+    def _parse_isodate_common(self, dt_str):
+        len_str = len(dt_str)
+        components = [1, 1, 1]
+
+        if len_str < 4:
+            raise ValueError('ISO string too short')
+
+        # Year
+        components[0] = int(dt_str[0:4])
+        pos = 4
+        if pos >= len_str:
+            return components, pos
+
+        has_sep = dt_str[pos:pos + 1] == self._DATE_SEP
+        if has_sep:
+            pos += 1
+
+        # Month
+        if len_str - pos < 2:
+            raise ValueError('Invalid common month')
+
+        components[1] = int(dt_str[pos:pos + 2])
+        pos += 2
+
+        if pos >= len_str:
+            if has_sep:
+                return components, pos
+            else:
+                raise ValueError('Invalid ISO format')
+
+        if has_sep:
+            if dt_str[pos:pos + 1] != self._DATE_SEP:
+                raise ValueError('Invalid separator in ISO string')
+            pos += 1
+
+        # Day
+        if len_str - pos < 2:
+            raise ValueError('Invalid common day')
+        components[2] = int(dt_str[pos:pos + 2])
+        return components, pos + 2
+
+    def _parse_isodate_uncommon(self, dt_str):
+        if len(dt_str) < 4:
+            raise ValueError('ISO string too short')
+
+        # All ISO formats start with the year
+        year = int(dt_str[0:4])
+
+        has_sep = dt_str[4:5] == self._DATE_SEP
+
+        pos = 4 + has_sep       # Skip '-' if it's there
+        if dt_str[pos:pos + 1] == b'W':
+            # YYYY-?Www-?D?
+            pos += 1
+            weekno = int(dt_str[pos:pos + 2])
+            pos += 2
+
+            dayno = 1
+            if len(dt_str) > pos:
+                if (dt_str[pos:pos + 1] == self._DATE_SEP) != has_sep:
+                    raise ValueError('Inconsistent use of dash separator')
+
+                pos += has_sep
+
+                dayno = int(dt_str[pos:pos + 1])
+                pos += 1
+
+            base_date = self._calculate_weekdate(year, weekno, dayno)
+        else:
+            # YYYYDDD or YYYY-DDD
+            if len(dt_str) - pos < 3:
+                raise ValueError('Invalid ordinal day')
+
+            ordinal_day = int(dt_str[pos:pos + 3])
+            pos += 3
+
+            if ordinal_day < 1 or ordinal_day > (365 + calendar.isleap(year)):
+                raise ValueError('Invalid ordinal day' +
+                                 ' {} for year {}'.format(ordinal_day, year))
+
+            base_date = date(year, 1, 1) + timedelta(days=ordinal_day - 1)
+
+        components = [base_date.year, base_date.month, base_date.day]
+        return components, pos
+
+    def _calculate_weekdate(self, year, week, day):
+        """
+        Calculate the day of corresponding to the ISO year-week-day calendar.
+
+        This function is effectively the inverse of
+        :func:`datetime.date.isocalendar`.
+
+        :param year:
+            The year in the ISO calendar
+
+        :param week:
+            The week in the ISO calendar - range is [1, 53]
+
+        :param day:
+            The day in the ISO calendar - range is [1 (MON), 7 (SUN)]
+
+        :return:
+            Returns a :class:`datetime.date`
+        """
+        if not 0 < week < 54:
+            raise ValueError('Invalid week: {}'.format(week))
+
+        if not 0 < day < 8:     # Range is 1-7
+            raise ValueError('Invalid weekday: {}'.format(day))
+
+        # Get week 1 for the specific year:
+        jan_4 = date(year, 1, 4)   # Week 1 always has January 4th in it
+        week_1 = jan_4 - timedelta(days=jan_4.isocalendar()[2] - 1)
+
+        # Now add the specific number of weeks and days to get what we want
+        week_offset = (week - 1) * 7 + (day - 1)
+        return week_1 + timedelta(days=week_offset)
+
+    def _parse_isotime(self, timestr):
+        len_str = len(timestr)
+        components = [0, 0, 0, 0, None]
+        pos = 0
+        comp = -1
+
         if len_str < 2:
-            raise ValueError('ISO time too short') 
- 
+            raise ValueError('ISO time too short')
+
         has_sep = False
- 
-        while pos < len_str and comp < 5: 
-            comp += 1 
- 
+
+        while pos < len_str and comp < 5:
+            comp += 1
+
             if timestr[pos:pos + 1] in b'-+Zz':
-                # Detect time zone boundary 
-                components[-1] = self._parse_tzstr(timestr[pos:]) 
-                pos = len_str 
-                break 
- 
+                # Detect time zone boundary
+                components[-1] = self._parse_tzstr(timestr[pos:])
+                pos = len_str
+                break
+
             if comp == 1 and timestr[pos:pos+1] == self._TIME_SEP:
                 has_sep = True
                 pos += 1
@@ -355,62 +355,62 @@ class isoparser(object):
                     raise ValueError('Inconsistent use of colon separator')
                 pos += 1
 
-            if comp < 3: 
-                # Hour, minute, second 
-                components[comp] = int(timestr[pos:pos + 2]) 
-                pos += 2 
- 
-            if comp == 3: 
+            if comp < 3:
+                # Hour, minute, second
+                components[comp] = int(timestr[pos:pos + 2])
+                pos += 2
+
+            if comp == 3:
                 # Fraction of a second
                 frac = self._FRACTION_REGEX.match(timestr[pos:])
                 if not frac:
-                    continue 
- 
+                    continue
+
                 us_str = frac.group(1)[:6]  # Truncate to microseconds
-                components[comp] = int(us_str) * 10**(6 - len(us_str)) 
+                components[comp] = int(us_str) * 10**(6 - len(us_str))
                 pos += len(frac.group())
- 
-        if pos < len_str: 
-            raise ValueError('Unused components in ISO string') 
- 
-        if components[0] == 24: 
-            # Standard supports 00:00 and 24:00 as representations of midnight 
-            if any(component != 0 for component in components[1:4]): 
-                raise ValueError('Hour may only be 24 at 24:00:00.000') 
- 
-        return components 
- 
-    def _parse_tzstr(self, tzstr, zero_as_utc=True): 
+
+        if pos < len_str:
+            raise ValueError('Unused components in ISO string')
+
+        if components[0] == 24:
+            # Standard supports 00:00 and 24:00 as representations of midnight
+            if any(component != 0 for component in components[1:4]):
+                raise ValueError('Hour may only be 24 at 24:00:00.000')
+
+        return components
+
+    def _parse_tzstr(self, tzstr, zero_as_utc=True):
         if tzstr == b'Z' or tzstr == b'z':
             return tz.UTC
- 
-        if len(tzstr) not in {3, 5, 6}: 
-            raise ValueError('Time zone offset must be 1, 3, 5 or 6 characters') 
- 
-        if tzstr[0:1] == b'-': 
-            mult = -1 
-        elif tzstr[0:1] == b'+': 
-            mult = 1 
-        else: 
-            raise ValueError('Time zone offset requires sign') 
- 
-        hours = int(tzstr[1:3]) 
-        if len(tzstr) == 3: 
-            minutes = 0 
-        else: 
-            minutes = int(tzstr[(4 if tzstr[3:4] == self._TIME_SEP else 3):]) 
- 
-        if zero_as_utc and hours == 0 and minutes == 0: 
+
+        if len(tzstr) not in {3, 5, 6}:
+            raise ValueError('Time zone offset must be 1, 3, 5 or 6 characters')
+
+        if tzstr[0:1] == b'-':
+            mult = -1
+        elif tzstr[0:1] == b'+':
+            mult = 1
+        else:
+            raise ValueError('Time zone offset requires sign')
+
+        hours = int(tzstr[1:3])
+        if len(tzstr) == 3:
+            minutes = 0
+        else:
+            minutes = int(tzstr[(4 if tzstr[3:4] == self._TIME_SEP else 3):])
+
+        if zero_as_utc and hours == 0 and minutes == 0:
             return tz.UTC
-        else: 
-            if minutes > 59: 
-                raise ValueError('Invalid minutes in time zone offset') 
- 
-            if hours > 23: 
-                raise ValueError('Invalid hours in time zone offset') 
- 
-            return tz.tzoffset(None, mult * (hours * 60 + minutes) * 60) 
- 
- 
-DEFAULT_ISOPARSER = isoparser() 
-isoparse = DEFAULT_ISOPARSER.isoparse 
+        else:
+            if minutes > 59:
+                raise ValueError('Invalid minutes in time zone offset')
+
+            if hours > 23:
+                raise ValueError('Invalid hours in time zone offset')
+
+            return tz.tzoffset(None, mult * (hours * 60 + minutes) * 60)
+
+
+DEFAULT_ISOPARSER = isoparser()
+isoparse = DEFAULT_ISOPARSER.isoparse

+ 70 - 70
contrib/python/dateutil/dateutil/relativedelta.py

@@ -23,7 +23,7 @@ class relativedelta(object):
 
     It is based on the specification of the excellent work done by M.-A. Lemburg
     in his
-    `mx.DateTime <https://www.egenix.com/products/python/mxBase/mxDateTime/>`_ extension. 
+    `mx.DateTime <https://www.egenix.com/products/python/mxBase/mxDateTime/>`_ extension.
     However, notice that this type does *NOT* implement the same algorithm as
     his work. Do *NOT* expect it to behave like mx.DateTime's counterpart.
 
@@ -38,7 +38,7 @@ class relativedelta(object):
 
         year, month, day, hour, minute, second, microsecond:
             Absolute information (argument is singular); adding or subtracting a
-            relativedelta with absolute information does not perform an arithmetic 
+            relativedelta with absolute information does not perform an arithmetic
             operation, but rather REPLACES the corresponding value in the
             original datetime with the value(s) in relativedelta.
 
@@ -48,7 +48,7 @@ class relativedelta(object):
             the corresponding arithmetic operation on the original datetime value
             with the information in the relativedelta.
 
-        weekday:  
+        weekday: 
             One of the weekday instances (MO, TU, etc) available in the
             relativedelta module. These instances may receive a parameter N,
             specifying the Nth weekday, which could be positive or negative
@@ -67,38 +67,38 @@ class relativedelta(object):
             Set the yearday or the non-leap year day (jump leap days).
             These are converted to day/month/leapdays information.
 
-    There are relative and absolute forms of the keyword 
-    arguments. The plural is relative, and the singular is 
-    absolute. For each argument in the order below, the absolute form 
-    is applied first (by setting each attribute to that value) and 
-    then the relative form (by adding the value to the attribute). 
+    There are relative and absolute forms of the keyword
+    arguments. The plural is relative, and the singular is
+    absolute. For each argument in the order below, the absolute form
+    is applied first (by setting each attribute to that value) and
+    then the relative form (by adding the value to the attribute).
 
-    The order of attributes considered when this relativedelta is 
-    added to a datetime is: 
+    The order of attributes considered when this relativedelta is
+    added to a datetime is:
 
-    1. Year 
-    2. Month 
-    3. Day 
-    4. Hours 
-    5. Minutes 
-    6. Seconds 
-    7. Microseconds 
+    1. Year
+    2. Month
+    3. Day
+    4. Hours
+    5. Minutes
+    6. Seconds
+    7. Microseconds
 
-    Finally, weekday is applied, using the rule described above. 
+    Finally, weekday is applied, using the rule described above.
 
-    For example 
+    For example
 
     >>> from datetime import datetime
     >>> from dateutil.relativedelta import relativedelta, MO
-    >>> dt = datetime(2018, 4, 9, 13, 37, 0) 
-    >>> delta = relativedelta(hours=25, day=1, weekday=MO(1)) 
+    >>> dt = datetime(2018, 4, 9, 13, 37, 0)
+    >>> delta = relativedelta(hours=25, day=1, weekday=MO(1))
     >>> dt + delta
     datetime.datetime(2018, 4, 2, 14, 37)
 
-    First, the day is set to 1 (the first of the month), then 25 hours 
-    are added, to get to the 2nd day and 14th hour, finally the 
-    weekday is applied, but since the 2nd is already a Monday there is 
-    no effect. 
+    First, the day is set to 1 (the first of the month), then 25 hours
+    are added, to get to the 2nd day and 14th hour, finally the
+    weekday is applied, but since the 2nd is already a Monday there is
+    no effect.
 
     """
 
@@ -168,14 +168,14 @@ class relativedelta(object):
             self.seconds = delta.seconds + delta.days * 86400
             self.microseconds = delta.microseconds
         else:
-            # Check for non-integer values in integer-only quantities 
-            if any(x is not None and x != int(x) for x in (years, months)): 
-                raise ValueError("Non-integer years and months are " 
-                                 "ambiguous and not currently supported.") 
- 
+            # Check for non-integer values in integer-only quantities
+            if any(x is not None and x != int(x) for x in (years, months)):
+                raise ValueError("Non-integer years and months are "
+                                 "ambiguous and not currently supported.")
+
             # Relative information
-            self.years = int(years) 
-            self.months = int(months) 
+            self.years = int(years)
+            self.months = int(months)
             self.days = days + weeks * 7
             self.leapdays = leapdays
             self.hours = hours
@@ -263,7 +263,7 @@ class relativedelta(object):
 
     @property
     def weeks(self):
-        return int(self.days / 7.0) 
+        return int(self.days / 7.0)
 
     @weeks.setter
     def weeks(self, value):
@@ -436,24 +436,24 @@ class relativedelta(object):
                                           is not None else
                                           other.microsecond))
 
-    def __abs__(self): 
-        return self.__class__(years=abs(self.years), 
-                              months=abs(self.months), 
-                              days=abs(self.days), 
-                              hours=abs(self.hours), 
-                              minutes=abs(self.minutes), 
-                              seconds=abs(self.seconds), 
-                              microseconds=abs(self.microseconds), 
-                              leapdays=self.leapdays, 
-                              year=self.year, 
-                              month=self.month, 
-                              day=self.day, 
-                              weekday=self.weekday, 
-                              hour=self.hour, 
-                              minute=self.minute, 
-                              second=self.second, 
-                              microsecond=self.microsecond) 
- 
+    def __abs__(self):
+        return self.__class__(years=abs(self.years),
+                              months=abs(self.months),
+                              days=abs(self.days),
+                              hours=abs(self.hours),
+                              minutes=abs(self.minutes),
+                              seconds=abs(self.seconds),
+                              microseconds=abs(self.microseconds),
+                              leapdays=self.leapdays,
+                              year=self.year,
+                              month=self.month,
+                              day=self.day,
+                              weekday=self.weekday,
+                              hour=self.hour,
+                              minute=self.minute,
+                              second=self.second,
+                              microsecond=self.microsecond)
+
     def __neg__(self):
         return self.__class__(years=-self.years,
                              months=-self.months,
@@ -544,25 +544,25 @@ class relativedelta(object):
                 self.second == other.second and
                 self.microsecond == other.microsecond)
 
-    def __hash__(self): 
-        return hash(( 
-            self.weekday, 
-            self.years, 
-            self.months, 
-            self.days, 
-            self.hours, 
-            self.minutes, 
-            self.seconds, 
-            self.microseconds, 
-            self.leapdays, 
-            self.year, 
-            self.month, 
-            self.day, 
-            self.hour, 
-            self.minute, 
-            self.second, 
-            self.microsecond, 
-        )) 
+    def __hash__(self):
+        return hash((
+            self.weekday,
+            self.years,
+            self.months,
+            self.days,
+            self.hours,
+            self.minutes,
+            self.seconds,
+            self.microseconds,
+            self.leapdays,
+            self.year,
+            self.month,
+            self.day,
+            self.hour,
+            self.minute,
+            self.second,
+            self.microsecond,
+        ))
 
     def __ne__(self, other):
         return not self.__eq__(other)

+ 42 - 42
contrib/python/dateutil/dateutil/rrule.py

@@ -2,14 +2,14 @@
 """
 The rrule module offers a small, complete, and very fast, implementation of
 the recurrence rules documented in the
-`iCalendar RFC <https://tools.ietf.org/html/rfc5545>`_, 
+`iCalendar RFC <https://tools.ietf.org/html/rfc5545>`_,
 including support for caching of results.
 """
 import calendar
 import datetime
 import heapq
 import itertools
-import re 
+import re
 import sys
 from functools import wraps
 # For warning about deprecation of until and count
@@ -390,11 +390,11 @@ class rrule(rrulebase):
     :param byyearday:
         If given, it must be either an integer, or a sequence of integers,
         meaning the year days to apply the recurrence to.
-    :param byeaster: 
-        If given, it must be either an integer, or a sequence of integers, 
-        positive or negative. Each integer will define an offset from the 
-        Easter Sunday. Passing the offset 0 to byeaster will yield the Easter 
-        Sunday itself. This is an extension to the RFC specification. 
+    :param byeaster:
+        If given, it must be either an integer, or a sequence of integers,
+        positive or negative. Each integer will define an offset from the
+        Easter Sunday. Passing the offset 0 to byeaster will yield the Easter
+        Sunday itself. This is an extension to the RFC specification.
     :param byweekno:
         If given, it must be either an integer, or a sequence of integers,
         meaning the week numbers to apply the recurrence to. Week numbers
@@ -420,10 +420,10 @@ class rrule(rrulebase):
     :param bysecond:
         If given, it must be either an integer, or a sequence of integers,
         meaning the seconds to apply the recurrence to.
-    :param cache: 
-        If given, it must be a boolean value specifying to enable or disable 
-        caching of results. If you will use the same rrule instance multiple 
-        times, enabling caching will improve the performance considerably. 
+    :param cache:
+        If given, it must be a boolean value specifying to enable or disable
+        caching of results. If you will use the same rrule instance multiple
+        times, enabling caching will improve the performance considerably.
      """
     def __init__(self, freq, dtstart=None,
                  interval=1, wkst=None, count=None, until=None, bysetpos=None,
@@ -434,10 +434,10 @@ class rrule(rrulebase):
         super(rrule, self).__init__(cache)
         global easter
         if not dtstart:
-            if until and until.tzinfo: 
-                dtstart = datetime.datetime.now(tz=until.tzinfo).replace(microsecond=0) 
+            if until and until.tzinfo:
+                dtstart = datetime.datetime.now(tz=until.tzinfo).replace(microsecond=0)
             else:
-                dtstart = datetime.datetime.now().replace(microsecond=0) 
+                dtstart = datetime.datetime.now().replace(microsecond=0)
         elif not isinstance(dtstart, datetime.datetime):
             dtstart = datetime.datetime.fromordinal(dtstart.toordinal())
         else:
@@ -458,22 +458,22 @@ class rrule(rrulebase):
             until = datetime.datetime.fromordinal(until.toordinal())
         self._until = until
 
-        if self._dtstart and self._until: 
-            if (self._dtstart.tzinfo is not None) != (self._until.tzinfo is not None): 
-                # According to RFC5545 Section 3.3.10: 
-                # https://tools.ietf.org/html/rfc5545#section-3.3.10 
-                # 
-                # > If the "DTSTART" property is specified as a date with UTC 
-                # > time or a date with local time and time zone reference, 
-                # > then the UNTIL rule part MUST be specified as a date with 
-                # > UTC time. 
-                raise ValueError( 
-                    'RRULE UNTIL values must be specified in UTC when DTSTART ' 
-                    'is timezone-aware' 
-                ) 
- 
+        if self._dtstart and self._until:
+            if (self._dtstart.tzinfo is not None) != (self._until.tzinfo is not None):
+                # According to RFC5545 Section 3.3.10:
+                # https://tools.ietf.org/html/rfc5545#section-3.3.10
+                #
+                # > If the "DTSTART" property is specified as a date with UTC
+                # > time or a date with local time and time zone reference,
+                # > then the UNTIL rule part MUST be specified as a date with
+                # > UTC time.
+                raise ValueError(
+                    'RRULE UNTIL values must be specified in UTC when DTSTART '
+                    'is timezone-aware'
+                )
+
         if count is not None and until:
-            warn("Using both 'count' and 'until' is inconsistent with RFC 5545" 
+            warn("Using both 'count' and 'until' is inconsistent with RFC 5545"
                  " and has been deprecated in dateutil. Future versions will "
                  "raise an error.", DeprecationWarning)
 
@@ -610,13 +610,13 @@ class rrule(rrulebase):
                 self._byweekday = tuple(sorted(self._byweekday))
                 orig_byweekday = [weekday(x) for x in self._byweekday]
             else:
-                orig_byweekday = () 
+                orig_byweekday = ()
 
             if self._bynweekday is not None:
                 self._bynweekday = tuple(sorted(self._bynweekday))
                 orig_bynweekday = [weekday(*x) for x in self._bynweekday]
             else:
-                orig_bynweekday = () 
+                orig_bynweekday = ()
 
             if 'byweekday' not in self._original_rule:
                 self._original_rule['byweekday'] = tuple(itertools.chain(
@@ -625,7 +625,7 @@ class rrule(rrulebase):
         # byhour
         if byhour is None:
             if freq < HOURLY:
-                self._byhour = {dtstart.hour} 
+                self._byhour = {dtstart.hour}
             else:
                 self._byhour = None
         else:
@@ -645,7 +645,7 @@ class rrule(rrulebase):
         # byminute
         if byminute is None:
             if freq < MINUTELY:
-                self._byminute = {dtstart.minute} 
+                self._byminute = {dtstart.minute}
             else:
                 self._byminute = None
         else:
@@ -700,7 +700,7 @@ class rrule(rrulebase):
     def __str__(self):
         """
         Output a string that would generate this RRULE if passed to rrulestr.
-        This is mostly compatible with RFC5545, except for the 
+        This is mostly compatible with RFC5545, except for the
         dateutil-specific extension BYEASTER.
         """
 
@@ -725,7 +725,7 @@ class rrule(rrulebase):
 
         if self._original_rule.get('byweekday') is not None:
             # The str() method on weekday objects doesn't generate
-            # RFC5545-compliant strings, so we should modify that. 
+            # RFC5545-compliant strings, so we should modify that.
             original_rule = dict(self._original_rule)
             wday_strings = []
             for wday in original_rule['byweekday']:
@@ -756,7 +756,7 @@ class rrule(rrulebase):
                 parts.append(partfmt.format(name=name, vals=(','.join(str(v)
                                                              for v in value))))
 
-        output.append('RRULE:' + ';'.join(parts)) 
+        output.append('RRULE:' + ';'.join(parts))
         return '\n'.join(output)
 
     def replace(self, **kwargs):
@@ -1619,17 +1619,17 @@ class _rrulestr(object):
                    forceset=False,
                    compatible=False,
                    ignoretz=False,
-                   tzids=None, 
+                   tzids=None,
                    tzinfos=None):
         global parser
         if compatible:
             forceset = True
             unfold = True
- 
-        TZID_NAMES = dict(map( 
-            lambda x: (x.upper(), x), 
-            re.findall('TZID=(?P<name>[^:]+):', s) 
-        )) 
+
+        TZID_NAMES = dict(map(
+            lambda x: (x.upper(), x),
+            re.findall('TZID=(?P<name>[^:]+):', s)
+        ))
         s = s.upper()
         if not s.strip():
             raise ValueError("empty string")

Some files were not shown because too many files changed in this diff