Merge pull request #59 from Tafkas/master

add compatibility for Python3
This commit is contained in:
2017-05-27 09:08:18 -07:00
committed by GitHub
3 changed files with 192 additions and 90 deletions
+100
View File
@@ -0,0 +1,100 @@
# -*- coding: utf-8 -*-
"""
maya.compat
~~~~~~~~~~~~~~~
This module handles import compatibility issues between Python 2 and
Python 3.
"""
import sys
# -------
# Pythons
# -------
# Syntax sugar.
_ver = sys.version_info
#: Python 2.x?
is_py2 = (_ver[0] == 2)
#: Python 3.x?
is_py3 = (_ver[0] == 3)
# ---------
# Specifics
# ---------
if is_py2:
cmp = cmp
elif is_py3:
def cmp(a, b):
"""
Compare two objects.
Returns a negative number if C{a < b}, zero if they are equal, and a
positive number if C{a > b}.
"""
if a < b:
return -1
elif a == b:
return 0
else:
return 1
def comparable(klass):
"""
Class decorator that ensures support for the special C{__cmp__} method.
On Python 2 this does nothing.
On Python 3, C{__eq__}, C{__lt__}, etc. methods are added to the class,
relying on C{__cmp__} to implement their comparisons.
"""
# On Python 2, __cmp__ will just work, so no need to add extra methods:
if not is_py3:
return klass
def __eq__(self, other):
c = self.__cmp__(other)
if c is NotImplemented:
return c
return c == 0
def __ne__(self, other):
c = self.__cmp__(other)
if c is NotImplemented:
return c
return c != 0
def __lt__(self, other):
c = self.__cmp__(other)
if c is NotImplemented:
return c
return c < 0
def __le__(self, other):
c = self.__cmp__(other)
if c is NotImplemented:
return c
return c <= 0
def __gt__(self, other):
c = self.__cmp__(other)
if c is NotImplemented:
return c
return c > 0
def __ge__(self, other):
c = self.__cmp__(other)
if c is NotImplemented:
return c
return c >= 0
klass.__lt__ = __lt__
klass.__gt__ = __gt__
klass.__le__ = __le__
klass.__ge__ = __ge__
klass.__eq__ = __eq__
klass.__ne__ = __ne__
return klass
+10 -8
View File
@@ -1,4 +1,3 @@
# ___ __ ___ _ _ ___
# || \/ | ||=|| \\// ||=||
# || | || || // || ||
@@ -6,19 +5,21 @@
# Ignore warnings for yaml usage.
import warnings
import ruamel.yaml
warnings.simplefilter('ignore', ruamel.yaml.error.UnsafeLoaderWarning)
warnings.simplefilter('ignore', ruamel.yaml.error.UnsafeLoaderWarning)
import email.utils
import time
from datetime import timedelta, datetime as Datetime
import functools
import pytz
import humanize
import dateparser
import pendulum
from tzlocal import get_localzone
from compat import cmp, comparable
_EPOCH_START = (1970, 1, 1)
@@ -276,15 +277,17 @@ def to_iso8601(dt):
def end_of_day_midnight(dt):
return dt if dt.time() == time.min else\
return dt if dt.time() == time.min else \
(dt.replace(hour=0, minute=0, second=0, microsecond=0) + timedelta(days=1))
@comparable
class MayaInterval(object):
"""
A MayaInterval represents a range between two datetimes, inclusive of the start
and exclusive of the end.
"""
def __init__(self, start=None, end=None, duration=None):
try:
# Ensure that proper arguments were passed.
@@ -337,7 +340,6 @@ class MayaInterval(object):
# # Duration and end, such as "P1Y2M10DT2H30M/2008-05-11T15:30:00Z"
raise NotImplementedError()
def __and__(self, i):
return self.intersection(i)
@@ -463,7 +465,7 @@ class MayaInterval(object):
self.start == i.start or
start <= end
)
if ((either_instant and instant_overlap) or (start < end)):
if (either_instant and instant_overlap) or (start < end):
return MayaInterval(start, end)
def contains(self, i):
@@ -505,7 +507,7 @@ class MayaInterval(object):
@staticmethod
def flatten(ii):
return reduce(lambda reduced, i: (
return functools.reduce(lambda reduced, i: (
(reduced[:-1] + i.combine(reduced[-1]))
if reduced else [i]
), sorted(ii), [])
@@ -536,7 +538,8 @@ def when(string, timezone='UTC'):
timezone -- timezone referenced from (default: 'UTC')
"""
dt = dateparser.parse(string, settings={'TIMEZONE': timezone, 'RETURN_AS_TIMEZONE_AWARE': True, 'TO_TIMEZONE': 'UTC'})
dt = dateparser.parse(string,
settings={'TIMEZONE': timezone, 'RETURN_AS_TIMEZONE_AWARE': True, 'TO_TIMEZONE': 'UTC'})
if dt is None:
raise ValueError('invalid datetime input specified.')
@@ -558,7 +561,6 @@ def parse(string, day_first=False):
def seconds_or_timedelta(s):
# Convert seconds into timedelta.
if isinstance(s, int):
s = timedelta(seconds=s)
+82 -82
View File
@@ -5,7 +5,7 @@ import pytest
import pytz
import maya
from compat import cmp
Los_Angeles = pytz.timezone('America/Los_Angeles')
New_York = pytz.timezone('America/New_York')
@@ -65,32 +65,32 @@ def test_interval_init_end_duration():
@pytest.mark.parametrize('start_doy1,end_doy1,start_doy2,end_doy2,intersection_doys', (
(0, 2, 1, 3, (1, 2)),
(0, 2, 3, 4, None),
(0, 2, 2, 3, None),
(0, 1, 0, 1, (0, 1)),
(1, 1, 1, 3, (1, 1)),
(1, 1, 1, 1, (1, 1)),
(1, 1, 2, 3, None),
(2, 2, 1, 3, (2, 2)),
(1, 3, 1, 1, (1, 1)),
(2, 3, 1, 1, None),
(1, 3, 2, 2, (2, 2)),
(0, 2, 1, 3, (1, 2)),
(0, 2, 3, 4, None),
(0, 2, 2, 3, None),
(0, 1, 0, 1, (0, 1)),
(1, 1, 1, 3, (1, 1)),
(1, 1, 1, 1, (1, 1)),
(1, 1, 2, 3, None),
(2, 2, 1, 3, (2, 2)),
(1, 3, 1, 1, (1, 1)),
(2, 3, 1, 1, None),
(1, 3, 2, 2, (2, 2)),
), ids=(
'overlapping',
'non-overlapping',
'adjacent',
'equal',
'instant overlapping start only',
'instant equal',
'instant disjoint',
'instant overlapping',
'instant overlapping start only (left)',
'instant disjoint (left)',
'instant overlapping (left)'
'overlapping',
'non-overlapping',
'adjacent',
'equal',
'instant overlapping start only',
'instant equal',
'instant disjoint',
'instant overlapping',
'instant overlapping start only (left)',
'instant disjoint (left)',
'instant overlapping (left)'
))
def test_interval_intersection(
start_doy1, end_doy1, start_doy2, end_doy2, intersection_doys
start_doy1, end_doy1, start_doy2, end_doy2, intersection_doys
):
base = maya.MayaDT.from_datetime(datetime(2016, 1, 1))
interval1 = maya.MayaInterval(
@@ -161,20 +161,20 @@ def test_interval_duration():
@pytest.mark.parametrize('start_doy1,end_doy1,start_doy2,end_doy2,expected', (
(0, 2, 1, 3, False),
(0, 2, 3, 4, False),
(0, 2, 2, 3, False),
(0, 1, 0, 1, True),
(0, 3, 1, 2, True),
(0, 2, 1, 3, False),
(0, 2, 3, 4, False),
(0, 2, 2, 3, False),
(0, 1, 0, 1, True),
(0, 3, 1, 2, True),
), ids=(
'overlapping',
'non-overlapping',
'adjacent',
'equal',
'subset',
'overlapping',
'non-overlapping',
'adjacent',
'equal',
'subset',
))
def test_interval_contains(
start_doy1, end_doy1, start_doy2, end_doy2, expected
start_doy1, end_doy1, start_doy2, end_doy2, expected
):
base = maya.MayaDT.from_datetime(datetime(2016, 1, 1))
interval1 = maya.MayaInterval(
@@ -191,20 +191,20 @@ def test_interval_contains(
@pytest.mark.parametrize('start_doy,end_doy,dt_doy,expected', (
(2, 4, 1, False),
(2, 4, 2, True),
(2, 4, 3, True),
(2, 4, 4, False),
(2, 4, 5, False),
(2, 4, 1, False),
(2, 4, 2, True),
(2, 4, 3, True),
(2, 4, 4, False),
(2, 4, 5, False),
), ids=(
'before-start',
'on-start',
'during',
'on-end',
'after-end',
'before-start',
'on-start',
'during',
'on-end',
'after-end',
))
def test_interval_in_operator_maya_dt(
start_doy, end_doy, dt_doy, expected
start_doy, end_doy, dt_doy, expected
):
base = maya.MayaDT.from_datetime(datetime(2016, 1, 1))
interval = maya.MayaInterval(
@@ -239,10 +239,10 @@ def test_interval_iter():
(2, 4, 1, 3, 1),
(1, 2, 1, 3, -1),
], ids=(
'equal',
'less-than',
'greater-than',
'use-end-time-if-start-time-identical',
'equal',
'less-than',
'greater-than',
'use-end-time-if-start-time-identical',
))
def test_interval_cmp(start1, end1, start2, end2, expected):
base = maya.now()
@@ -263,10 +263,10 @@ def test_interval_cmp(start1, end1, start2, end2, expected):
(1, 2, 3, 4, [(1, 2), (3, 4)]),
(1, 5, 2, 3, [(1, 5)]),
], ids=(
'adjacent',
'overlapping',
'non-overlapping',
'contains',
'adjacent',
'overlapping',
'non-overlapping',
'contains',
))
def test_interval_combine(start1, end1, start2, end2, expected):
base = maya.now()
@@ -297,14 +297,14 @@ def test_interval_combine(start1, end1, start2, end2, expected):
(1, 4, 1, 2, [(2, 4)]),
(1, 4, 3, 4, [(1, 3)]),
], ids=(
'non-overlapping',
'adjacent',
'contains',
'splits',
'overlaps-left',
'overlaps-right',
'overlaps-left-identical-start',
'overlaps-right-identical-end',
'non-overlapping',
'adjacent',
'contains',
'splits',
'overlaps-left',
'overlaps-right',
'overlaps-left-identical-start',
'overlaps-right-identical-end',
))
def test_interval_subtract(start1, end1, start2, end2, expected):
base = maya.now()
@@ -330,10 +330,10 @@ def test_interval_subtract(start1, end1, start2, end2, expected):
(1, 3, 2, 3, False),
(2, 3, 4, 5, False),
], ids=(
'adjacent-right',
'adjacent-left',
'overlapping',
'non-overlapping',
'adjacent-right',
'adjacent-left',
'overlapping',
'non-overlapping',
))
def test_interval_is_adjacent(start1, end1, start2, end2, expected):
base = maya.now()
@@ -356,12 +356,12 @@ def test_interval_is_adjacent(start1, end1, start2, end2, expected):
(0, 2, 5, False, []),
(0, 2, 5, True, [(0, 2)]),
], ids=(
'even-split',
'even-split-include-partial',
'uneven-split-do-not-include-partial',
'uneven-split-include-partial',
'delta-larger-than-timepsan-do-not-include-partial',
'delta-larger-than-timepsan-include-partial',
'even-split',
'even-split-include-partial',
'uneven-split-do-not-include-partial',
'uneven-split-include-partial',
'delta-larger-than-timepsan-do-not-include-partial',
'delta-larger-than-timepsan-include-partial',
))
def test_interval_split(start, end, delta, include_remainder, expected):
base = maya.now()
@@ -406,16 +406,16 @@ def test_interval_split_non_positive_delta():
((6, 20), (6, 50), 60, 'America/Chicago', False, (6, 0), (6, 0)),
((6, 20), (6, 50), 60, 'America/Chicago', True, (6, 0), (7, 0)),
], ids=(
'normal',
'normal-snap_out',
'already-quantized',
'already-quantized-snap_out',
'with-timezone',
'with-timezone-snap_out',
'too-small',
'too-small-snap_out',
'too-small-with-timezone',
'too-small-with-timezone-snap_out',
'normal',
'normal-snap_out',
'already-quantized',
'already-quantized-snap_out',
'with-timezone',
'with-timezone-snap_out',
'too-small',
'too-small-snap_out',
'too-small-with-timezone',
'too-small-with-timezone-snap_out',
))
def test_quantize(start, end, minutes, timezone, snap_out, expected_start, expected_end):
base = maya.MayaDT.from_datetime(datetime(2017, 1, 1))