at 18.03-beta 17 kB view raw
1From bb7fa19d636d999bf844d80939e155b8f212ef3e Mon Sep 17 00:00:00 2001 2From: Andreas Maier <maiera@de.ibm.com> 3Date: Fri, 23 Jun 2017 19:13:51 +0200 4Subject: [PATCH] Made CIMDateTime always timezone-aware, re-enabled 5 test_cim_types.py 6 7Details: 8- Ensured that 'CIMDateTime' objects for point in time values are 9 timezone-aware when supplied with a timezone-naive 'datetime' 10 object. This does not change the behavior, but increases code 11 clarity. 12- Clarified that in the documentation of 'CIMDateTime'. 13- Added testcases for CIMDateTime. 14- Re-enabled the testing of test_cim_types.py. Since its change 15 to using pytest it was not run because the test methods were 16 all static methods which apparently does not work. Not sure 17 this ever worked. 18 19Signed-off-by: Andreas Maier <maiera@de.ibm.com> 20--- 21 docs/changes.rst | 5 + 22 pywbem/cim_types.py | 22 +-- 23 testsuite/test_cim_types.py | 369 +++++++++++++++++++++++--------------------- 24 3 files changed, 207 insertions(+), 189 deletions(-) 25 26diff --git a/docs/changes.rst b/docs/changes.rst 27index 272ed30d..6fdfbcf4 100644 28--- a/docs/changes.rst 29+++ b/docs/changes.rst 30@@ -72,6 +72,11 @@ Enhancements 31 32 * Added unit test for recorder. See issue #676 33 34+* Ensured that `CIMDateTime` objects for point in time values are 35+ timezone-aware when supplied with a timezone-naive `datetime` object. 36+ This does not change the behavior, but increases code clarity. 37+ Clarified that in the documentation of `CIMDateTime`. See issue #698. 38+ 39 Bug fixes 40 ^^^^^^^^^ 41 42diff --git a/pywbem/cim_types.py b/pywbem/cim_types.py 43index 6d1f140c..5ecc7707 100644 44--- a/pywbem/cim_types.py 45+++ b/pywbem/cim_types.py 46@@ -74,6 +74,7 @@ 47 import re 48 import warnings 49 import six 50+import copy 51 52 from . import config 53 54@@ -294,9 +295,11 @@ def __init__(self, dtarg): 55 * A :term:`string` object will be 56 interpreted as CIM datetime format (see :term:`DSP0004`) and 57 will result in a point in time or a time interval. 58- * A :class:`py:datetime.datetime` object must be timezone-aware 59- (see :class:`~pywbem.MinutesFromUTC`) and will result in a point 60- in time. 61+ * A :class:`py:datetime.datetime` object will result in a point 62+ in time. If the :class:`py:datetime.datetime` object is 63+ timezone-aware (see :class:`~pywbem.MinutesFromUTC`), the 64+ specified timezone will be used. Otherwise, a default timezone 65+ of UTC will be assumed. 66 * A :class:`py:datetime.timedelta` object will result in a time 67 interval. 68 * Another :class:`~pywbem.CIMDateTime` object will be copied. 69@@ -342,14 +345,15 @@ def __init__(self, dtarg): 70 raise ValueError('dtarg argument "%s" has an invalid CIM ' 71 'datetime format' % dtarg) 72 elif isinstance(dtarg, datetime): 73- self.__datetime = dtarg 74+ if dtarg.tzinfo is None: 75+ self.__datetime = dtarg.replace(tzinfo=MinutesFromUTC(0)) 76+ else: 77+ self.__datetime = copy.copy(dtarg) 78 elif isinstance(dtarg, timedelta): 79- self.__timedelta = dtarg 80+ self.__timedelta = copy.copy(dtarg) 81 elif isinstance(dtarg, CIMDateTime): 82- # pylint: disable=protected-access 83- self.__datetime = dtarg.__datetime 84- # pylint: disable=protected-access 85- self.__timedelta = dtarg.__timedelta 86+ self.__datetime = copy.copy(dtarg.datetime) 87+ self.__timedelta = copy.copy(dtarg.timedelta) 88 else: 89 raise TypeError('dtarg argument "%s" has an invalid type: %s ' 90 '(expected datetime, timedelta, string, or ' 91diff --git a/testsuite/test_cim_types.py b/testsuite/test_cim_types.py 92index 4ae354d3..b1f54d06 100755 93--- a/testsuite/test_cim_types.py 94+++ b/testsuite/test_cim_types.py 95@@ -43,105 +43,99 @@ def integer_tuple(request): 96 return request.param 97 98 99-class TestIntegers: 100- """ 101- Test CIM integer data type classes. 102- """ 103- 104- @staticmethod 105- def test_class_attrs_class(integer_tuple): 106- """Test class attrs via class level""" 107- obj_type, exp_cimtype, exp_minvalue, exp_maxvalue = integer_tuple 108- assert obj_type.cimtype == exp_cimtype 109- assert obj_type.minvalue == exp_minvalue 110- assert obj_type.maxvalue == exp_maxvalue 111- 112- @staticmethod 113- def test_class_attrs_inst(integer_tuple): 114- """Test class attrs via instance level""" 115- obj_type, exp_cimtype, exp_minvalue, exp_maxvalue = integer_tuple 116- obj = obj_type(42) 117- assert obj.cimtype == exp_cimtype 118- assert obj.minvalue == exp_minvalue 119- assert obj.maxvalue == exp_maxvalue 120- 121- @staticmethod 122- def test_inheritance(integer_tuple): 123- """Test inheritance""" 124- obj_type = integer_tuple[0] 125- obj = obj_type(42) 126- assert isinstance(obj, obj_type) 127- assert isinstance(obj, CIMType) 128- assert isinstance(obj, CIMInt) 129- assert not isinstance(obj, CIMFloat) 130- 131- @staticmethod 132- def test_init_int(integer_tuple): 133- """Test initialization from integer value""" 134- obj_type = integer_tuple[0] 135- obj = obj_type(42) 136- assert obj == 42 137- 138- @staticmethod 139- def test_init_str(integer_tuple): 140- """Test initialization from string value""" 141- obj_type = integer_tuple[0] 142- obj = obj_type('42') 143- assert obj == 42 144- 145- @staticmethod 146- def test_init_str_base10(integer_tuple): 147- """Test initialization from string value with base 10""" 148- obj_type = integer_tuple[0] 149- obj = obj_type('42', 10) 150- assert obj == 42 151- 152- @staticmethod 153- def test_init_str_base16(integer_tuple): 154- """Test initialization from string value with base 16""" 155- obj_type = integer_tuple[0] 156- obj = obj_type('2A', 16) 157- assert obj == 42 158- 159- @staticmethod 160- def test_init_minimum(integer_tuple): 161- """Test initialization from integer value at minimum""" 162- obj_type = integer_tuple[0] 163- exp_minvalue = integer_tuple[2] 164- obj = obj_type(exp_minvalue) 165- assert obj == exp_minvalue 166- 167- @staticmethod 168- def test_init_maximum(integer_tuple): 169- """Test initialization from integer value at maximum""" 170- obj_type = integer_tuple[0] 171- exp_maxvalue = integer_tuple[3] 172- obj = obj_type(exp_maxvalue) 173- assert obj == exp_maxvalue 174- 175- @staticmethod 176- def test_init_too_low(integer_tuple): 177- """Test initialization from integer value below minimum""" 178- obj_type = integer_tuple[0] 179- exp_minvalue = integer_tuple[2] 180- try: 181- obj_type(exp_minvalue - 1) 182- except ValueError: 183- pass 184- else: 185- raise AssertionError("ValueError was not raised.") 186- 187- @staticmethod 188- def test_init_too_high(integer_tuple): 189- """Test initialization from integer value above maximum""" 190- obj_type = integer_tuple[0] 191- exp_maxvalue = integer_tuple[3] 192- try: 193- obj_type(exp_maxvalue + 1) 194- except ValueError: 195- pass 196- else: 197- raise AssertionError("ValueError was not raised.") 198+def test_integer_class_attrs_class(integer_tuple): 199+ """Test class attrs via class level""" 200+ obj_type, exp_cimtype, exp_minvalue, exp_maxvalue = integer_tuple 201+ assert obj_type.cimtype == exp_cimtype 202+ assert obj_type.minvalue == exp_minvalue 203+ assert obj_type.maxvalue == exp_maxvalue 204+ 205+ 206+def test_integer_class_attrs_inst(integer_tuple): 207+ """Test class attrs via instance level""" 208+ obj_type, exp_cimtype, exp_minvalue, exp_maxvalue = integer_tuple 209+ obj = obj_type(42) 210+ assert obj.cimtype == exp_cimtype 211+ assert obj.minvalue == exp_minvalue 212+ assert obj.maxvalue == exp_maxvalue 213+ 214+ 215+def test_integer_inheritance(integer_tuple): 216+ """Test inheritance""" 217+ obj_type = integer_tuple[0] 218+ obj = obj_type(42) 219+ assert isinstance(obj, obj_type) 220+ assert isinstance(obj, CIMType) 221+ assert isinstance(obj, CIMInt) 222+ assert not isinstance(obj, CIMFloat) 223+ 224+ 225+def test_integer_init_int(integer_tuple): 226+ """Test initialization from integer value""" 227+ obj_type = integer_tuple[0] 228+ obj = obj_type(42) 229+ assert obj == 42 230+ 231+ 232+def test_integer_init_str(integer_tuple): 233+ """Test initialization from string value""" 234+ obj_type = integer_tuple[0] 235+ obj = obj_type('42') 236+ assert obj == 42 237+ 238+ 239+def test_integer_init_str_base10(integer_tuple): 240+ """Test initialization from string value with base 10""" 241+ obj_type = integer_tuple[0] 242+ obj = obj_type('42', 10) 243+ assert obj == 42 244+ 245+ 246+def test_integer_init_str_base16(integer_tuple): 247+ """Test initialization from string value with base 16""" 248+ obj_type = integer_tuple[0] 249+ obj = obj_type('2A', 16) 250+ assert obj == 42 251+ 252+ 253+def test_integer_init_minimum(integer_tuple): 254+ """Test initialization from integer value at minimum""" 255+ obj_type = integer_tuple[0] 256+ exp_minvalue = integer_tuple[2] 257+ obj = obj_type(exp_minvalue) 258+ assert obj == exp_minvalue 259+ 260+ 261+def test_integer_init_maximum(integer_tuple): 262+ """Test initialization from integer value at maximum""" 263+ obj_type = integer_tuple[0] 264+ exp_maxvalue = integer_tuple[3] 265+ obj = obj_type(exp_maxvalue) 266+ assert obj == exp_maxvalue 267+ 268+ 269+def test_integer_init_too_low(integer_tuple): 270+ """Test initialization from integer value below minimum""" 271+ obj_type = integer_tuple[0] 272+ exp_minvalue = integer_tuple[2] 273+ try: 274+ obj_type(exp_minvalue - 1) 275+ except ValueError: 276+ pass 277+ else: 278+ raise AssertionError("ValueError was not raised.") 279+ 280+ 281+def test_integer_init_too_high(integer_tuple): 282+ """Test initialization from integer value above maximum""" 283+ obj_type = integer_tuple[0] 284+ exp_maxvalue = integer_tuple[3] 285+ try: 286+ obj_type(exp_maxvalue + 1) 287+ except ValueError: 288+ pass 289+ else: 290+ raise AssertionError("ValueError was not raised.") 291 292 293 # 294@@ -164,47 +158,41 @@ def real_tuple(request): 295 return request.param 296 297 298-class TestReals: 299- """ 300- Test CIM real data type classes. 301- """ 302- 303- @staticmethod 304- def test_class_attrs_class(real_tuple): 305- """Test class attrs via class level""" 306- obj_type, exp_cimtype = real_tuple 307- assert obj_type.cimtype == exp_cimtype 308- 309- @staticmethod 310- def test_class_attrs_inst(real_tuple): 311- """Test class attrs via instance level""" 312- obj_type, exp_cimtype = real_tuple 313- obj = obj_type(42) 314- assert obj.cimtype == exp_cimtype 315- 316- @staticmethod 317- def test_inheritance(real_tuple): 318- """Test inheritance""" 319- obj_type = real_tuple[0] 320- obj = obj_type(42) 321- assert isinstance(obj, obj_type) 322- assert isinstance(obj, CIMType) 323- assert isinstance(obj, CIMFloat) 324- assert not isinstance(obj, CIMInt) 325- 326- @staticmethod 327- def test_init_float(real_tuple): 328- """Test initialization from floating point value""" 329- obj_type = real_tuple[0] 330- obj = obj_type(42.0) 331- assert obj == 42.0 332- 333- @staticmethod 334- def test_init_str(real_tuple): 335- """Test initialization from string value""" 336- obj_type = real_tuple[0] 337- obj = obj_type('42.0') 338- assert obj == 42.0 339+def test_real_class_attrs_class(real_tuple): 340+ """Test class attrs via class level""" 341+ obj_type, exp_cimtype = real_tuple 342+ assert obj_type.cimtype == exp_cimtype 343+ 344+ 345+def test_real_class_attrs_inst(real_tuple): 346+ """Test class attrs via instance level""" 347+ obj_type, exp_cimtype = real_tuple 348+ obj = obj_type(42) 349+ assert obj.cimtype == exp_cimtype 350+ 351+ 352+def test_real_inheritance(real_tuple): 353+ """Test inheritance""" 354+ obj_type = real_tuple[0] 355+ obj = obj_type(42) 356+ assert isinstance(obj, obj_type) 357+ assert isinstance(obj, CIMType) 358+ assert isinstance(obj, CIMFloat) 359+ assert not isinstance(obj, CIMInt) 360+ 361+ 362+def test_real_init_float(real_tuple): 363+ """Test initialization from floating point value""" 364+ obj_type = real_tuple[0] 365+ obj = obj_type(42.0) 366+ assert obj == 42.0 367+ 368+ 369+def test_real_init_str(real_tuple): 370+ """Test initialization from string value""" 371+ obj_type = real_tuple[0] 372+ obj = obj_type('42.0') 373+ assert obj == 42.0 374 375 376 # 377@@ -271,6 +259,26 @@ def test_init_str(real_tuple): 378 '20140924193040.654321+120' 379 ), 380 ( 381+ datetime(year=2014, month=9, day=24, hour=19, minute=30, second=40, 382+ microsecond=654321, tzinfo=MinutesFromUTC(0)), 383+ 'timestamp', 384+ datetime(year=2014, month=9, day=24, hour=19, minute=30, second=40, 385+ microsecond=654321, tzinfo=MinutesFromUTC(0)), 386+ None, 387+ 0, 388+ '20140924193040.654321+000' 389+ ), 390+ ( 391+ datetime(year=2014, month=9, day=24, hour=19, minute=30, second=40, 392+ microsecond=654321), 393+ 'timestamp', 394+ datetime(year=2014, month=9, day=24, hour=19, minute=30, second=40, 395+ microsecond=654321, tzinfo=MinutesFromUTC(0)), 396+ None, 397+ 0, 398+ '20140924193040.654321+000' 399+ ), 400+ ( 401 '20140924193040.654321+120', 402 'timestamp', 403 datetime(year=2014, month=9, day=24, hour=19, minute=30, second=40, 404@@ -325,46 +333,47 @@ def datetime_init_tuple(request): 405 return request.param 406 407 408-class TestDatetime: 409- """ 410- Test CIM real data type classes. 411- """ 412- 413- @staticmethod 414- def test_class_attrs_class(): 415- """Test class attrs via class level""" 416- assert CIMDateTime.cimtype == 'datetime' 417- 418- @staticmethod 419- def test_class_attrs_inst(): 420- """Test class attrs via instance level""" 421- obj = CIMDateTime('00000000000000.000000:000') 422- assert obj.cimtype == 'datetime' 423- 424- @staticmethod 425- def test_inheritance(): 426- """Test inheritance""" 427- obj = CIMDateTime('00000000000000.000000:000') 428- assert isinstance(obj, CIMDateTime) 429- assert isinstance(obj, CIMType) 430- assert not isinstance(obj, CIMFloat) 431- assert not isinstance(obj, CIMInt) 432- 433- @staticmethod 434- def test_init(datetime_init_tuple): 435- """Test initialization from all input types""" 436- (dtarg, exp_kind, exp_datetime, exp_timedelta, exp_minutesfromutc, 437- exp_str) = datetime_init_tuple 438- try: 439- obj = CIMDateTime(dtarg) 440- except Exception as exc: 441- assert isinstance(exc, exp_kind) 442- else: 443- assert obj.is_interval == (exp_kind == 'interval') 444- assert obj.datetime == exp_datetime 445- assert obj.timedelta == exp_timedelta 446- assert obj.minutes_from_utc == exp_minutesfromutc 447- assert str(obj) == exp_str 448+def test_datetime_class_attrs_class(): 449+ """Test class attrs via class level""" 450+ assert CIMDateTime.cimtype == 'datetime' 451+ 452+ 453+def test_datetime_class_attrs_inst(): 454+ """Test class attrs via instance level""" 455+ obj = CIMDateTime('00000000000000.000000:000') 456+ assert obj.cimtype == 'datetime' 457+ 458+ 459+def test_datetime_inheritance(): 460+ """Test inheritance""" 461+ obj = CIMDateTime('00000000000000.000000:000') 462+ assert isinstance(obj, CIMDateTime) 463+ assert isinstance(obj, CIMType) 464+ assert not isinstance(obj, CIMFloat) 465+ assert not isinstance(obj, CIMInt) 466+ 467+ 468+def test_datetime_init(datetime_init_tuple): 469+ """Test initialization from all input types""" 470+ (dtarg, exp_kind, exp_datetime, exp_timedelta, exp_minutesfromutc, 471+ exp_str) = datetime_init_tuple 472+ try: 473+ obj = CIMDateTime(dtarg) 474+ except Exception as exc: 475+ assert isinstance(exc, exp_kind) 476+ else: 477+ assert obj.is_interval == (exp_kind == 'interval') 478+ assert obj.datetime == exp_datetime 479+ if obj.datetime is not None: 480+ assert isinstance(obj.datetime, datetime) 481+ # We ensure that the datetime is always timezone-aware: 482+ assert obj.datetime.tzinfo is not None 483+ assert obj.timedelta == exp_timedelta 484+ if obj.timedelta is not None: 485+ assert isinstance(obj.timedelta, timedelta) 486+ assert obj.minutes_from_utc == exp_minutesfromutc 487+ assert str(obj) == exp_str 488+ 489 490 # TODO: Add testcases for get_local_utcoffset() 491 # TODO: Add testcases for now()