1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
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
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']
71
72 """
73 A `datetime.tzinfo` implementation defined using a fixed offset in +/-
74 minutes from UTC.
75 """
76
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
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
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
105 """Base type for numeric and datetime CIM types."""
106
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
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
167
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
182 self.__timedelta = dtarg.__timedelta
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
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
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
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
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
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
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
335 return '%s(\'%s\')' % (self.__class__.__name__, str(self))
336
339
342
350
351
352
353 -class CIMInt(CIMType, long):
354 """Base type for integer CIM types."""
355
357 """A value of CIM type uint8."""
358 cimtype = 'uint8'
359
361 """A value of CIM type sint8."""
362 cimtype = 'sint8'
363
365 """A value of CIM type uint16."""
366 cimtype = 'uint16'
367
369 """A value of CIM type sint16."""
370 cimtype = 'sint16'
371
373 """A value of CIM type uint32."""
374 cimtype = 'uint32'
375
377 """A value of CIM type sint32."""
378 cimtype = 'sint32'
379
381 """A value of CIM type uint64."""
382 cimtype = 'uint64'
383
385 """A value of CIM type sint64."""
386 cimtype = 'sint64'
387
391 """Base type for real (floating point) CIM types."""
392
394 """A value of CIM type real32."""
395 cimtype = 'real32'
396
398 """A value of CIM type real64."""
399 cimtype = 'real64'
400
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
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 }
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
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
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:
530 return unicode(obj)
531