From bb7fa19d636d999bf844d80939e155b8f212ef3e Mon Sep 17 00:00:00 2001 From: Andreas Maier Date: Fri, 23 Jun 2017 19:13:51 +0200 Subject: [PATCH] Made CIMDateTime always timezone-aware, re-enabled test_cim_types.py Details: - Ensured that 'CIMDateTime' objects for point in time values are timezone-aware when supplied with a timezone-naive 'datetime' object. This does not change the behavior, but increases code clarity. - Clarified that in the documentation of 'CIMDateTime'. - Added testcases for CIMDateTime. - Re-enabled the testing of test_cim_types.py. Since its change to using pytest it was not run because the test methods were all static methods which apparently does not work. Not sure this ever worked. Signed-off-by: Andreas Maier --- docs/changes.rst | 5 + pywbem/cim_types.py | 22 +-- testsuite/test_cim_types.py | 369 +++++++++++++++++++++++--------------------- 3 files changed, 207 insertions(+), 189 deletions(-) diff --git a/docs/changes.rst b/docs/changes.rst index 272ed30d..6fdfbcf4 100644 --- a/docs/changes.rst +++ b/docs/changes.rst @@ -72,6 +72,11 @@ Enhancements * Added unit test for recorder. See issue #676 +* Ensured that `CIMDateTime` objects for point in time values are + timezone-aware when supplied with a timezone-naive `datetime` object. + This does not change the behavior, but increases code clarity. + Clarified that in the documentation of `CIMDateTime`. See issue #698. + Bug fixes ^^^^^^^^^ diff --git a/pywbem/cim_types.py b/pywbem/cim_types.py index 6d1f140c..5ecc7707 100644 --- a/pywbem/cim_types.py +++ b/pywbem/cim_types.py @@ -74,6 +74,7 @@ import re import warnings import six +import copy from . import config @@ -294,9 +295,11 @@ def __init__(self, dtarg): * A :term:`string` object will be interpreted as CIM datetime format (see :term:`DSP0004`) and will result in a point in time or a time interval. - * A :class:`py:datetime.datetime` object must be timezone-aware - (see :class:`~pywbem.MinutesFromUTC`) and will result in a point - in time. + * A :class:`py:datetime.datetime` object will result in a point + in time. If the :class:`py:datetime.datetime` object is + timezone-aware (see :class:`~pywbem.MinutesFromUTC`), the + specified timezone will be used. Otherwise, a default timezone + of UTC will be assumed. * A :class:`py:datetime.timedelta` object will result in a time interval. * Another :class:`~pywbem.CIMDateTime` object will be copied. @@ -342,14 +345,15 @@ def __init__(self, dtarg): raise ValueError('dtarg argument "%s" has an invalid CIM ' 'datetime format' % dtarg) elif isinstance(dtarg, datetime): - self.__datetime = dtarg + if dtarg.tzinfo is None: + self.__datetime = dtarg.replace(tzinfo=MinutesFromUTC(0)) + else: + self.__datetime = copy.copy(dtarg) elif isinstance(dtarg, timedelta): - self.__timedelta = dtarg + self.__timedelta = copy.copy(dtarg) elif isinstance(dtarg, CIMDateTime): - # pylint: disable=protected-access - self.__datetime = dtarg.__datetime - # pylint: disable=protected-access - self.__timedelta = dtarg.__timedelta + self.__datetime = copy.copy(dtarg.datetime) + self.__timedelta = copy.copy(dtarg.timedelta) else: raise TypeError('dtarg argument "%s" has an invalid type: %s ' '(expected datetime, timedelta, string, or ' diff --git a/testsuite/test_cim_types.py b/testsuite/test_cim_types.py index 4ae354d3..b1f54d06 100755 --- a/testsuite/test_cim_types.py +++ b/testsuite/test_cim_types.py @@ -43,105 +43,99 @@ def integer_tuple(request): return request.param -class TestIntegers: - """ - Test CIM integer data type classes. - """ - - @staticmethod - def test_class_attrs_class(integer_tuple): - """Test class attrs via class level""" - obj_type, exp_cimtype, exp_minvalue, exp_maxvalue = integer_tuple - assert obj_type.cimtype == exp_cimtype - assert obj_type.minvalue == exp_minvalue - assert obj_type.maxvalue == exp_maxvalue - - @staticmethod - def test_class_attrs_inst(integer_tuple): - """Test class attrs via instance level""" - obj_type, exp_cimtype, exp_minvalue, exp_maxvalue = integer_tuple - obj = obj_type(42) - assert obj.cimtype == exp_cimtype - assert obj.minvalue == exp_minvalue - assert obj.maxvalue == exp_maxvalue - - @staticmethod - def test_inheritance(integer_tuple): - """Test inheritance""" - obj_type = integer_tuple[0] - obj = obj_type(42) - assert isinstance(obj, obj_type) - assert isinstance(obj, CIMType) - assert isinstance(obj, CIMInt) - assert not isinstance(obj, CIMFloat) - - @staticmethod - def test_init_int(integer_tuple): - """Test initialization from integer value""" - obj_type = integer_tuple[0] - obj = obj_type(42) - assert obj == 42 - - @staticmethod - def test_init_str(integer_tuple): - """Test initialization from string value""" - obj_type = integer_tuple[0] - obj = obj_type('42') - assert obj == 42 - - @staticmethod - def test_init_str_base10(integer_tuple): - """Test initialization from string value with base 10""" - obj_type = integer_tuple[0] - obj = obj_type('42', 10) - assert obj == 42 - - @staticmethod - def test_init_str_base16(integer_tuple): - """Test initialization from string value with base 16""" - obj_type = integer_tuple[0] - obj = obj_type('2A', 16) - assert obj == 42 - - @staticmethod - def test_init_minimum(integer_tuple): - """Test initialization from integer value at minimum""" - obj_type = integer_tuple[0] - exp_minvalue = integer_tuple[2] - obj = obj_type(exp_minvalue) - assert obj == exp_minvalue - - @staticmethod - def test_init_maximum(integer_tuple): - """Test initialization from integer value at maximum""" - obj_type = integer_tuple[0] - exp_maxvalue = integer_tuple[3] - obj = obj_type(exp_maxvalue) - assert obj == exp_maxvalue - - @staticmethod - def test_init_too_low(integer_tuple): - """Test initialization from integer value below minimum""" - obj_type = integer_tuple[0] - exp_minvalue = integer_tuple[2] - try: - obj_type(exp_minvalue - 1) - except ValueError: - pass - else: - raise AssertionError("ValueError was not raised.") - - @staticmethod - def test_init_too_high(integer_tuple): - """Test initialization from integer value above maximum""" - obj_type = integer_tuple[0] - exp_maxvalue = integer_tuple[3] - try: - obj_type(exp_maxvalue + 1) - except ValueError: - pass - else: - raise AssertionError("ValueError was not raised.") +def test_integer_class_attrs_class(integer_tuple): + """Test class attrs via class level""" + obj_type, exp_cimtype, exp_minvalue, exp_maxvalue = integer_tuple + assert obj_type.cimtype == exp_cimtype + assert obj_type.minvalue == exp_minvalue + assert obj_type.maxvalue == exp_maxvalue + + +def test_integer_class_attrs_inst(integer_tuple): + """Test class attrs via instance level""" + obj_type, exp_cimtype, exp_minvalue, exp_maxvalue = integer_tuple + obj = obj_type(42) + assert obj.cimtype == exp_cimtype + assert obj.minvalue == exp_minvalue + assert obj.maxvalue == exp_maxvalue + + +def test_integer_inheritance(integer_tuple): + """Test inheritance""" + obj_type = integer_tuple[0] + obj = obj_type(42) + assert isinstance(obj, obj_type) + assert isinstance(obj, CIMType) + assert isinstance(obj, CIMInt) + assert not isinstance(obj, CIMFloat) + + +def test_integer_init_int(integer_tuple): + """Test initialization from integer value""" + obj_type = integer_tuple[0] + obj = obj_type(42) + assert obj == 42 + + +def test_integer_init_str(integer_tuple): + """Test initialization from string value""" + obj_type = integer_tuple[0] + obj = obj_type('42') + assert obj == 42 + + +def test_integer_init_str_base10(integer_tuple): + """Test initialization from string value with base 10""" + obj_type = integer_tuple[0] + obj = obj_type('42', 10) + assert obj == 42 + + +def test_integer_init_str_base16(integer_tuple): + """Test initialization from string value with base 16""" + obj_type = integer_tuple[0] + obj = obj_type('2A', 16) + assert obj == 42 + + +def test_integer_init_minimum(integer_tuple): + """Test initialization from integer value at minimum""" + obj_type = integer_tuple[0] + exp_minvalue = integer_tuple[2] + obj = obj_type(exp_minvalue) + assert obj == exp_minvalue + + +def test_integer_init_maximum(integer_tuple): + """Test initialization from integer value at maximum""" + obj_type = integer_tuple[0] + exp_maxvalue = integer_tuple[3] + obj = obj_type(exp_maxvalue) + assert obj == exp_maxvalue + + +def test_integer_init_too_low(integer_tuple): + """Test initialization from integer value below minimum""" + obj_type = integer_tuple[0] + exp_minvalue = integer_tuple[2] + try: + obj_type(exp_minvalue - 1) + except ValueError: + pass + else: + raise AssertionError("ValueError was not raised.") + + +def test_integer_init_too_high(integer_tuple): + """Test initialization from integer value above maximum""" + obj_type = integer_tuple[0] + exp_maxvalue = integer_tuple[3] + try: + obj_type(exp_maxvalue + 1) + except ValueError: + pass + else: + raise AssertionError("ValueError was not raised.") # @@ -164,47 +158,41 @@ def real_tuple(request): return request.param -class TestReals: - """ - Test CIM real data type classes. - """ - - @staticmethod - def test_class_attrs_class(real_tuple): - """Test class attrs via class level""" - obj_type, exp_cimtype = real_tuple - assert obj_type.cimtype == exp_cimtype - - @staticmethod - def test_class_attrs_inst(real_tuple): - """Test class attrs via instance level""" - obj_type, exp_cimtype = real_tuple - obj = obj_type(42) - assert obj.cimtype == exp_cimtype - - @staticmethod - def test_inheritance(real_tuple): - """Test inheritance""" - obj_type = real_tuple[0] - obj = obj_type(42) - assert isinstance(obj, obj_type) - assert isinstance(obj, CIMType) - assert isinstance(obj, CIMFloat) - assert not isinstance(obj, CIMInt) - - @staticmethod - def test_init_float(real_tuple): - """Test initialization from floating point value""" - obj_type = real_tuple[0] - obj = obj_type(42.0) - assert obj == 42.0 - - @staticmethod - def test_init_str(real_tuple): - """Test initialization from string value""" - obj_type = real_tuple[0] - obj = obj_type('42.0') - assert obj == 42.0 +def test_real_class_attrs_class(real_tuple): + """Test class attrs via class level""" + obj_type, exp_cimtype = real_tuple + assert obj_type.cimtype == exp_cimtype + + +def test_real_class_attrs_inst(real_tuple): + """Test class attrs via instance level""" + obj_type, exp_cimtype = real_tuple + obj = obj_type(42) + assert obj.cimtype == exp_cimtype + + +def test_real_inheritance(real_tuple): + """Test inheritance""" + obj_type = real_tuple[0] + obj = obj_type(42) + assert isinstance(obj, obj_type) + assert isinstance(obj, CIMType) + assert isinstance(obj, CIMFloat) + assert not isinstance(obj, CIMInt) + + +def test_real_init_float(real_tuple): + """Test initialization from floating point value""" + obj_type = real_tuple[0] + obj = obj_type(42.0) + assert obj == 42.0 + + +def test_real_init_str(real_tuple): + """Test initialization from string value""" + obj_type = real_tuple[0] + obj = obj_type('42.0') + assert obj == 42.0 # @@ -271,6 +259,26 @@ def test_init_str(real_tuple): '20140924193040.654321+120' ), ( + datetime(year=2014, month=9, day=24, hour=19, minute=30, second=40, + microsecond=654321, tzinfo=MinutesFromUTC(0)), + 'timestamp', + datetime(year=2014, month=9, day=24, hour=19, minute=30, second=40, + microsecond=654321, tzinfo=MinutesFromUTC(0)), + None, + 0, + '20140924193040.654321+000' + ), + ( + datetime(year=2014, month=9, day=24, hour=19, minute=30, second=40, + microsecond=654321), + 'timestamp', + datetime(year=2014, month=9, day=24, hour=19, minute=30, second=40, + microsecond=654321, tzinfo=MinutesFromUTC(0)), + None, + 0, + '20140924193040.654321+000' + ), + ( '20140924193040.654321+120', 'timestamp', datetime(year=2014, month=9, day=24, hour=19, minute=30, second=40, @@ -325,46 +333,47 @@ def datetime_init_tuple(request): return request.param -class TestDatetime: - """ - Test CIM real data type classes. - """ - - @staticmethod - def test_class_attrs_class(): - """Test class attrs via class level""" - assert CIMDateTime.cimtype == 'datetime' - - @staticmethod - def test_class_attrs_inst(): - """Test class attrs via instance level""" - obj = CIMDateTime('00000000000000.000000:000') - assert obj.cimtype == 'datetime' - - @staticmethod - def test_inheritance(): - """Test inheritance""" - obj = CIMDateTime('00000000000000.000000:000') - assert isinstance(obj, CIMDateTime) - assert isinstance(obj, CIMType) - assert not isinstance(obj, CIMFloat) - assert not isinstance(obj, CIMInt) - - @staticmethod - def test_init(datetime_init_tuple): - """Test initialization from all input types""" - (dtarg, exp_kind, exp_datetime, exp_timedelta, exp_minutesfromutc, - exp_str) = datetime_init_tuple - try: - obj = CIMDateTime(dtarg) - except Exception as exc: - assert isinstance(exc, exp_kind) - else: - assert obj.is_interval == (exp_kind == 'interval') - assert obj.datetime == exp_datetime - assert obj.timedelta == exp_timedelta - assert obj.minutes_from_utc == exp_minutesfromutc - assert str(obj) == exp_str +def test_datetime_class_attrs_class(): + """Test class attrs via class level""" + assert CIMDateTime.cimtype == 'datetime' + + +def test_datetime_class_attrs_inst(): + """Test class attrs via instance level""" + obj = CIMDateTime('00000000000000.000000:000') + assert obj.cimtype == 'datetime' + + +def test_datetime_inheritance(): + """Test inheritance""" + obj = CIMDateTime('00000000000000.000000:000') + assert isinstance(obj, CIMDateTime) + assert isinstance(obj, CIMType) + assert not isinstance(obj, CIMFloat) + assert not isinstance(obj, CIMInt) + + +def test_datetime_init(datetime_init_tuple): + """Test initialization from all input types""" + (dtarg, exp_kind, exp_datetime, exp_timedelta, exp_minutesfromutc, + exp_str) = datetime_init_tuple + try: + obj = CIMDateTime(dtarg) + except Exception as exc: + assert isinstance(exc, exp_kind) + else: + assert obj.is_interval == (exp_kind == 'interval') + assert obj.datetime == exp_datetime + if obj.datetime is not None: + assert isinstance(obj.datetime, datetime) + # We ensure that the datetime is always timezone-aware: + assert obj.datetime.tzinfo is not None + assert obj.timedelta == exp_timedelta + if obj.timedelta is not None: + assert isinstance(obj.timedelta, timedelta) + assert obj.minutes_from_utc == exp_minutesfromutc + assert str(obj) == exp_str + # TODO: Add testcases for get_local_utcoffset() # TODO: Add testcases for now()