Package pywbem :: Module cim_types
[frames] | no frames]

Source Code for Module pywbem.cim_types

  1  # 
  2  # (C) Copyright 2003,2004 Hewlett-Packard Development Company, L.P. 
  3  # (C) Copyright 2006,2007 Novell, Inc. 
  4  # 
  5  # This library is free software; you can redistribute it and/or 
  6  # modify it under the terms of the GNU Lesser General Public 
  7  # License as published by the Free Software Foundation; either 
  8  # version 2.1 of the License, or (at your option) any later version. 
  9  # 
 10  # This program is distributed in the hope that it will be useful, but 
 11  # WITHOUT ANY WARRANTY; without even the implied warranty of 
 12  # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU 
 13  # Lesser General Public License for more details. 
 14  # 
 15  # You should have received a copy of the GNU Lesser General Public 
 16  # License along with this program; if not, write to the Free Software 
 17  # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 
 18  # 
 19  # Author: Tim Potter <tpot@hp.com> 
 20  # Author: Bart Whiteley <bwhiteley@suse.de> 
 21  # 
 22   
 23  """ 
 24  Types to represent CIM typed values, and related conversion functions. 
 25   
 26  The following table shows how CIM typed values are represented as Python 
 27  objects: 
 28   
 29  =========================  =========================== 
 30  CIM type                   Python type 
 31  =========================  =========================== 
 32  boolean                    `bool` 
 33  char16                     `unicode` or `str` 
 34  string                     `unicode` or `str` 
 35  string (EmbeddedInstance)  `CIMInstance` 
 36  string (EmbeddedObject)    `CIMInstance` or `CIMClass` 
 37  datetime                   `CIMDateTime` 
 38  reference                  `CIMInstanceName` 
 39  uint8                      `Uint8` 
 40  uint16                     `Uint16` 
 41  uint32                     `Uint32` 
 42  uint64                     `Uint64` 
 43  sint8                      `Sint8` 
 44  sint16                     `Sint16` 
 45  sint32                     `Sint32` 
 46  sint64                     `Sint64` 
 47  real32                     `Real32` 
 48  real64                     `Real64` 
 49  [] (array)                 `list` 
 50  =========================  =========================== 
 51   
 52  Note that constructors of PyWBEM classes that take CIM typed values as input 
 53  may support Python types in addition to those shown above. For example, the 
 54  `CIMProperty` class represents CIM datetime values internally as a 
 55  `CIMDateTime` object, but its constructor accepts `datetime.timedelta`, 
 56  `datetime.datetime`, `str`, and `unicode` objects in addition to 
 57  `CIMDateTime` objects. 
 58  """ 
 59   
 60  # This module is meant to be safe for 'import *'. 
 61   
 62  from datetime import tzinfo, datetime, timedelta 
 63  import re 
 64   
 65  __all__ = ['MinutesFromUTC', 'CIMType', 'CIMDateTime', 'CIMInt', 'Uint8', 
 66             'Sint8', 'Uint16', 'Sint16', 'Uint32', 'Sint32', 'Uint64', 'Sint64', 
 67             'CIMFloat', 'Real32', 'Real64', 'cimtype', 'type_from_name', 
 68             'atomic_to_cim_xml'] 
69 70 -class MinutesFromUTC(tzinfo):
71 72 """ 73 A `datetime.tzinfo` implementation defined using a fixed offset in +/- 74 minutes from UTC. 75 """ 76
77 - def __init__(self, offset): # pylint: disable=super-init-not-called
78 """ 79 Initialize the `MinutesFromUTC` object from a timezone offset. 80 81 :Parameters: 82 83 offset : `int` 84 Timezone offset in +/- minutes from UTC, where a positive value 85 indicates minutes east of UTC, and a negative value indicates 86 minutes west of UTC. 87 """ 88 self.__offset = timedelta(minutes=offset)
89
90 - def utcoffset(self, dt): # pylint: disable=unused-argument
91 """ 92 Implement the `datetime.tzinfo.utcoffset` method by returning 93 the timezone offset as a `datetime.timedelta` object. 94 """ 95 return self.__offset 96
97 - def dst(self, dt): # pylint: disable=unused-argument
98 """ 99 Implement the `datetime.tzinfo.dst` method by returning 100 a DST value of 0 as a `datetime.timedelta` object. 101 """ 102 return timedelta(0) 103
104 -class CIMType(object):
105 """Base type for numeric and datetime CIM types."""
106
107 -class CIMDateTime(CIMType):
108 109 """ 110 A value of CIM type datetime. 111 112 The object represents either a timezone-aware point in time, or a time 113 interval. 114 """ 115
116 - def __init__(self, dtarg):
117 """ 118 Initialize the `CIMDateTime` object from different types of input 119 object. 120 121 :Parameters: 122 123 dtarg 124 The input object, as one of the following types: 125 An `str` or `unicode` object will be interpreted as CIM datetime 126 format (see DSP0004) and will result in a point in time or a time 127 interval. 128 A `datetime.datetime` object must be timezone-aware and will 129 result in a point in time. 130 A `datetime.timedelta` object will result in a time interval. 131 Another `CIMDateTime` object will be copied. 132 133 :Raises: 134 :raise ValueError: 135 :raise TypeError: 136 """ 137 self.cimtype = 'datetime' 138 self.__timedelta = None 139 self.__datetime = None 140 if isinstance(dtarg, basestring): 141 date_pattern = re.compile( 142 r'^(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})\.' \ 143 r'(\d{6})([+|-])(\d{3})') 144 s = date_pattern.search(dtarg) 145 if s is not None: 146 g = s.groups() 147 offset = int(g[8]) 148 if g[7] == '-': 149 offset = -offset 150 try: 151 self.__datetime = datetime(int(g[0]), int(g[1]), 152 int(g[2]), int(g[3]), 153 int(g[4]), int(g[5]), 154 int(g[6]), 155 MinutesFromUTC(offset)) 156 except ValueError as exc: 157 raise ValueError('dtarg argument "%s" has invalid field '\ 158 'values for CIM datetime timestamp '\ 159 'format: %s' % (dtarg, exc)) 160 else: 161 tv_pattern = re.compile( 162 r'^(\d{8})(\d{2})(\d{2})(\d{2})\.(\d{6})(:)(000)') 163 s = tv_pattern.search(dtarg) 164 if s is not None: 165 g = s.groups() 166 # Because the input values are limited by the matched 167 # pattern, timedelta() never throws any exception. 168 self.__timedelta = timedelta(days=int(g[0]), 169 hours=int(g[1]), 170 minutes=int(g[2]), 171 seconds=int(g[3]), 172 microseconds=int(g[4])) 173 else: 174 raise ValueError('dtarg argument "%s" has an invalid CIM '\ 175 'datetime format' % dtarg) 176 elif isinstance(dtarg, datetime): 177 self.__datetime = dtarg 178 elif isinstance(dtarg, timedelta): 179 self.__timedelta = dtarg 180 elif isinstance(dtarg, CIMDateTime): 181 self.__datetime = dtarg.__datetime # pylint: disable=protected-access 182 self.__timedelta = dtarg.__timedelta # pylint: disable=protected-access 183 else: 184 raise TypeError('dtarg argument "%s" has an invalid type: %s '\ 185 '(expected datetime, timedelta, string, or '\ 186 'CIMDateTime)' % (dtarg, type(dtarg)))
187 188 @property
189 - def minutes_from_utc(self):
190 """ 191 The timezone offset of a point in time object as +/- minutes from UTC. 192 193 A positive value of the timezone offset indicates minutes east of UTC, 194 and a negative value indicates minutes west of UTC. 195 196 0, if the object represents a time interval. 197 """ 198 offset = 0 199 if self.__datetime is not None and \ 200 self.__datetime.utcoffset() is not None: 201 offset = self.__datetime.utcoffset().seconds / 60 202 if self.__datetime.utcoffset().days == -1: 203 offset = -(60*24 - offset) 204 return offset
205 206 @property
207 - def datetime(self):
208 """ 209 The point in time represented by the object, as a `datetime.datetime` 210 object. 211 212 `None` if the object represents a time interval. 213 """ 214 return self.__datetime
215 216 @property
217 - def timedelta(self):
218 """ 219 The time interval represented by the object, as a `datetime.timedelta` 220 object. 221 222 `None` if the object represents a point in time. 223 """ 224 return self.__timedelta
225 226 @property
227 - def is_interval(self):
228 """ 229 A boolean indicating whether the object represents a time interval 230 (`True`) or a point in time (`False`). 231 """ 232 return self.__timedelta is not None
233 234 @staticmethod
236 """ 237 Return the timezone offset of the current local timezone from UTC. 238 239 A positive value indicates minutes east of UTC, and a negative 240 value indicates minutes west of UTC. 241 """ 242 utc = datetime.utcnow() 243 local = datetime.now() 244 if local < utc: 245 return - int(float((utc - local).seconds) / 60 + .5) 246 else: 247 return int(float((local - utc).seconds) / 60 + .5)
248 249 @classmethod
250 - def now(cls, tzi=None):
251 """ 252 Factory method that returns a new `CIMDateTime` object representing 253 the current date and time. 254 255 The optional timezone information is used to convert the CIM datetime 256 value into the desired timezone. That does not change the point in time 257 that is represented by the value, but it changes the value of the 258 `hhmmss` components of the CIM datetime value to compensate for changes 259 in the timezone offset component. 260 261 :Parameters: 262 263 tzi : `datetime.tzinfo` 264 Timezone information. `None` means that the current local timezone 265 is used. The `datetime.tzinfo` object may be a `MinutesFromUTC` 266 object. 267 268 :Returns: 269 270 A new `CIMDateTime` object representing the current date and time. 271 """ 272 if tzi is None: 273 tzi = MinutesFromUTC(cls.get_local_utcoffset()) 274 return cls(datetime.now(tzi))
275 276 @classmethod
277 - def fromtimestamp(cls, ts, tzi=None):
278 """ 279 Factory method that returns a new `CIMDateTime` object from a POSIX 280 timestamp value and optional timezone information. 281 282 A POSIX timestamp value is the number of seconds since 1970-01-01 283 00:00:00 UTC. Thus, a POSIX timestamp value is unambiguous w.r.t. the 284 timezone. 285 286 The optional timezone information is used to convert the CIM datetime 287 value into the desired timezone. That does not change the point in time 288 that is represented by the value, but it changes the value of the 289 `hhmmss` components of the CIM datetime value to compensate for changes 290 in the timezone offset component. 291 292 :Parameters: 293 294 ts : `int` 295 POSIX timestamp value. 296 297 tzi : `datetime.tzinfo` 298 Timezone information. `None` means that the current local timezone 299 is used. The `datetime.tzinfo` object may be a `MinutesFromUTC` 300 object. 301 302 :Returns: 303 304 A new `CIMDateTime` object representing the specified point in 305 time. 306 """ 307 if tzi is None: 308 tzi = MinutesFromUTC(cls.get_local_utcoffset()) 309 return cls(datetime.fromtimestamp(ts, tzi))
310
311 - def __str__(self):
312 """ 313 Return a string representing the object in CIM datetime format. 314 """ 315 if self.is_interval: 316 hour = self.timedelta.seconds / 3600 317 minute = (self.timedelta.seconds - hour * 3600) / 60 318 second = self.timedelta.seconds - hour * 3600 - minute * 60 319 return '%08d%02d%02d%02d.%06d:000' % \ 320 (self.timedelta.days, hour, minute, second, 321 self.timedelta.microseconds) 322 else: 323 offset = self.minutes_from_utc 324 sign = '+' 325 if offset < 0: 326 sign = '-' 327 offset = -offset 328 return '%d%02d%02d%02d%02d%02d.%06d%s%03d' % \ 329 (self.datetime.year, self.datetime.month, 330 self.datetime.day, self.datetime.hour, 331 self.datetime.minute, self.datetime.second, 332 self.datetime.microsecond, sign, offset)
333
334 - def __repr__(self):
335 return '%s(\'%s\')' % (self.__class__.__name__, str(self))
336
337 - def __getstate__(self):
338 return str(self)
339
340 - def __setstate__(self, arg):
341 self.__init__(arg)
342
343 - def __cmp__(self, other):
344 if self is other: 345 return 0 346 elif not isinstance(other, CIMDateTime): 347 return 1 348 return cmp(self.datetime, other.datetime) or \ 349 cmp(self.timedelta, other.timedelta)
350
351 # CIM integer types 352 353 -class CIMInt(CIMType, long):
354 """Base type for integer CIM types."""
355
356 -class Uint8(CIMInt):
357 """A value of CIM type uint8.""" 358 cimtype = 'uint8'
359
360 -class Sint8(CIMInt):
361 """A value of CIM type sint8.""" 362 cimtype = 'sint8'
363
364 -class Uint16(CIMInt):
365 """A value of CIM type uint16.""" 366 cimtype = 'uint16'
367
368 -class Sint16(CIMInt):
369 """A value of CIM type sint16.""" 370 cimtype = 'sint16'
371
372 -class Uint32(CIMInt):
373 """A value of CIM type uint32.""" 374 cimtype = 'uint32'
375
376 -class Sint32(CIMInt):
377 """A value of CIM type sint32.""" 378 cimtype = 'sint32'
379
380 -class Uint64(CIMInt):
381 """A value of CIM type uint64.""" 382 cimtype = 'uint64'
383
384 -class Sint64(CIMInt):
385 """A value of CIM type sint64.""" 386 cimtype = 'sint64'
387
388 # CIM float types 389 390 -class CIMFloat(CIMType, float):
391 """Base type for real (floating point) CIM types."""
392
393 -class Real32(CIMFloat):
394 """A value of CIM type real32.""" 395 cimtype = 'real32'
396
397 -class Real64(CIMFloat):
398 """A value of CIM type real64.""" 399 cimtype = 'real64'
400
401 -def cimtype(obj):
402 """ 403 Return the CIM type name of a value, as a string. 404 405 For an array, the type is determined from the first array element because 406 CIM arrays must be homogeneous. If the array is empty, ValueError is 407 raised. 408 409 If the type of the value is not a CIM type, TypeError is raised. 410 411 :Parameters: 412 413 obj : CIM typed value 414 The value whose CIM type name is returned. 415 416 :Returns: 417 418 The CIM type name of the value, as a string. 419 420 :Raises: 421 422 :raise TypeError: 423 Type is not a CIM type. 424 425 :raise ValueError: 426 Cannot determine CIM type from empty array. 427 """ 428 if isinstance(obj, CIMType): 429 return obj.cimtype 430 if isinstance(obj, bool): 431 return 'boolean' 432 if isinstance(obj, (basestring)): 433 return 'string' 434 if isinstance(obj, list): 435 if len(obj) == 0: 436 raise ValueError("Cannot determine CIM type from empty array") 437 return cimtype(obj[0]) 438 if isinstance(obj, (datetime, timedelta)): 439 return 'datetime' 440 raise TypeError("Type %s of this value is not a CIM type: %r" % \ 441 (type(obj), obj))
442 443 _TYPE_FROM_NAME = { 444 'boolean': bool, 445 'string': unicode, 446 'char16': unicode, 447 'datetime': CIMDateTime, 448 # 'reference' covered at run time 449 'uint8': Uint8, 450 'uint16': Uint16, 451 'uint32': Uint32, 452 'uint64': Uint64, 453 'sint8': Sint8, 454 'sint16': Sint16, 455 'sint32': Sint32, 456 'sint64': Sint64, 457 'real32': Real32, 458 'real64': Real64, 459 }
460 461 -def type_from_name(type_name):
462 """ 463 Return the Python type object for a given CIM type name. 464 465 For example, type name `'uint8'` will return type `Uint8`. 466 467 :Parameters: 468 469 type_name : `str` or `unicode` 470 The simple (=non-array) CIM type name (e.g. `'uint8'` or 471 `'reference'`). 472 473 :Returns: 474 475 The Python type object for the CIM type (e.g. `Uint8` or 476 `CIMInstanceName`). 477 478 :Raises: 479 480 :raise ValueError: 481 Unknown CIM type name. 482 """ 483 if type_name == 'reference': 484 # move import to run time to avoid circular imports 485 from pywbem import cim_obj 486 return cim_obj.CIMInstanceName 487 try: 488 type_obj = _TYPE_FROM_NAME[type_name] 489 except KeyError: 490 raise ValueError("Unknown CIM type name: %r" % type_name) 491 return type_obj
492
493 -def atomic_to_cim_xml(obj):
494 """ 495 Convert a value of an atomic scalar CIM type to a CIM-XML unicode string 496 and return that string. 497 498 TODO: Verify whether we can change this function to raise a ValueError in 499 case the value is not CIM typed. 500 501 :Parameters: 502 503 obj : CIM typed value. 504 The CIM typed value`, including `None`. Must be a scalar. Must be an 505 atomic type (i.e. not `CIMInstance` or `CIMClass`). 506 507 :Returns: 508 509 A unicode string in CIM-XML value format representing the CIM typed 510 value. For a value of `None`, `None` is returned. 511 """ 512 if isinstance(obj, bool): 513 if obj: 514 return u"true" 515 else: 516 return u"false" 517 elif isinstance(obj, CIMDateTime): 518 return unicode(obj) 519 elif isinstance(obj, datetime): 520 return unicode(CIMDateTime(obj)) 521 elif obj is None: 522 return obj 523 elif cimtype(obj) == 'real32': 524 return u'%.8E' % obj 525 elif cimtype(obj) == 'real64': 526 return u'%.16E' % obj 527 elif isinstance(obj, str): 528 return obj.decode('utf-8') 529 else: # e.g. unicode, int 530 return unicode(obj)
531