-
Notifications
You must be signed in to change notification settings - Fork 26
/
_cim_obj.py
8139 lines (6369 loc) · 296 KB
/
_cim_obj.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
#
# (C) Copyright 2003-2007 Hewlett-Packard Development Company, L.P.
# (C) Copyright 2006-2007 Novell, Inc.
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2.1 of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this program; if not, write to the Free Software
# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
#
# Author: Tim Potter <tpot@hp.com>
# Author: Martin Pool <mbp@hp.com>
# Author: Bart Whiteley <bwhiteley@suse.de>
# Author: Ross Peoples <ross.peoples@gmail.com>
#
# pylint: disable=line-too-long
"""
CIM objects are local representations of CIM instances, classes, properties,
etc., as Python objects. They are used as input to and output from WBEM
operations:
========================================== ==========================================================================
CIM object Purpose
========================================== ==========================================================================
:class:`~pywbem.CIMInstanceName` Instance path of a CIM instance
:class:`~pywbem.CIMInstance` CIM instance
:class:`~pywbem.CIMClassName` Name of a CIM class, optionally with class path
:class:`~pywbem.CIMClass` CIM class
:class:`~pywbem.CIMProperty` CIM property, both as property value in a CIM instance and as property
declaration in a CIM class
:class:`~pywbem.CIMMethod` CIM method declaration in a CIM class
:class:`~pywbem.CIMParameter` CIM parameter, both as a parameter value in a method invocation and as a
parameter declaration in a CIM method declaration in a CIM class
:class:`~pywbem.CIMQualifier` CIM qualifier value
:class:`~pywbem.CIMQualifierDeclaration` CIM qualifier type/declaration
========================================== ==========================================================================
.. _`Putting CIM objects in sets`:
Putting CIM objects in sets
---------------------------
Using sets for holding the result of :ref:`WBEM operations` is not uncommon,
because that allows comparison of results without regard to the (undefined)
order in which the objects are returned.
:ref:`CIM objects` are mutable and :term:`unchanged-hashable`. This requires
some caution when putting them in sets, or using them in any other way that
relies on their hash values.
The caution that is needed is that the public attributes, and therefore the
state of the CIM objects, must not change as long as they are a member of a
set, or used in any other way that relies on their hash values.
The following example shows what happens if a CIM object is modified while
being a member of a set:
::
import pywbem
s = set()
# Create CIM object c1 and show its identity and hash value:
c1 = pywbem.CIMClass('C')
print(id(c1), hash(c1)) # (140362966049680, -7623128493967117119)
# Add c1 to the set and verify the set content:
s.add(c1)
print([id(c) for c in s]) # [140362966049680]
# Modify the c1 object; it now has changed its hash value:
c1.superclass = 'B'
print(id(c1), hash(c1)) # (140362966049680, 638672161836520435)
# Create a CIM object c2 with the same attribute values as c1, and show
# that they compare equal and that c2 has the same hash value as c1 now has:
c2 = pywbem.CIMClass('C', superclass='B')
print(c1 == c2) # True
print(id(c2), hash(c2)) # (140362970983696, 638672161836520435)
# Add c2 to the set and verify the set content:
s.add(c2)
print([id(c) for c in s]) # [140362966049680, 140362970983696] !!
At the end, the set contains both objects even though they have the same hash
value. This is not what one would expect from
:ref:`set types <py:types-set>`.
The reason is that at the time the object c1 was added to the set, it had a
different hash value, and the set uses the hash value it found at insertion
time of its member for identifying the object. When the second object is added,
it finds it has a yet unknown hash value, and adds it.
While the set type in this particular Python implementation was able to still
look up the first member object even though its hash value has changed
meanwhile, other collection types or other Python implementations may not be as
forgiving and may not even be able to look up the object once its hash value
has changed.
Therefore, always make sure that the public attributes of CIM objects that are
put into a set remain unchanged while the object is in the set. The same
applies to any other usage of CIM objects that relies on their hash values.
.. _`Order of CIM child objects`:
Order of CIM child objects
--------------------------
:ref:`CIM objects` have zero or more lists of child objects. For example, a CIM
class (the parent object) has a list of CIM properties, CIM methods and CIM
qualifiers (the child objects).
In pywbem, the parent CIM object allows initializing each list of child objects
via an init parameter. For example, the :class:`~pywbem.CIMClass` init method
has a parameter named ``properties`` that allows specifying the CIM properties
of the CIM class.
Once the parent CIM object exists, each list of child objects can be modified
via a settable attribute. For example, the :class:`~pywbem.CIMClass` class has
a :attr:`~pywbem.CIMClass.properties` attribute for its list of CIM properties.
For such attributes and init parameters that specify lists of child
objects, pywbem supports a number of different ways the child objects can be
specified.
Some of these ways preserve the order of child objects and some don't.
This section uses CIM properties in CIM classes as an example, but it applies
to all kinds of child objects in CIM objects.
The possible input objects for the ``properties`` init parameter
and for the :attr:`~pywbem.CIMClass.properties` attribute of
:class:`~pywbem.CIMClass` is described in the type
:term:`properties input object`, and must be one of these objects:
* iterable of :class:`~pywbem.CIMProperty`
* iterable of tuple(key, value)
* :class:`~py:collections.OrderedDict` with key and value
* :class:`py:dict` with key and value (will not preserve order)
The keys are always the property names, and the values are always
:class:`~pywbem.CIMProperty` objects (at least when initializing classes).
Even though the :class:`~py:collections.OrderedDict` class preserves the order
of its items, intializing the dictionary with keyword arguments causes the
order of items to be lost before the dictionary is even initialized (before
Python 3.6). The only way to initialize a dictionary without loosing order of
items is by providing a list of tuples(key,value).
The following examples work but loose the order of properties in the class:
::
# Examples where order of properties in class is not as specified:
# Using an OrderedDict object, initialized with keyword arguments
# (before Python 3.6):
c1_props = OrderedDict(
Prop1=CIMProperty('Prop1', value='abc'),
Prop2=CIMProperty('Prop2', value=None, type='string'),
)
# Using a dict object, initialized with keyword arguments (This time
# specified using key:value notation):
c1_props = {
'Prop1': CIMProperty('Prop1', value='abc'),
'Prop2': CIMProperty('Prop2', value=None, type='string'),
}
# Using a dict object, initialized with list of tuple(key,value):
c1_props = dict([
('Prop1', CIMProperty('Prop1', value='abc')),
('Prop2', CIMProperty('Prop2', value=None, type='string')),
])
# Any of the above objects can be used to initialize the class properties:
c1 = CIMClass('CIM_Foo', properties=c1_props)
The following examples all preserve the order of properties in the class:
::
# Examples where order of properties in class is as specified:
# Using a list of CIMProperty objects (starting with pywbem 0.12):
c1_props = [
CIMProperty('Prop1', value='abc'),
CIMProperty('Prop2', value=None, type='string'),
]
# Using an OrderedDict object, initialized with list of tuple(key,value):
c1_props = OrderedDict([
('Prop1', CIMProperty('Prop1', value='abc')),
('Prop2', CIMProperty('Prop2', value=None, type='string')),
])
# Using a list of tuple(key,value):
c1_props = [
('Prop1', CIMProperty('Prop1', value='abc')),
('Prop2', CIMProperty('Prop2', value=None, type='string')),
]
# Any of the above objects can be used to initialize the class properties:
c1 = CIMClass('CIM_Foo', properties=c1_props)
""" # noqa: E501
# pylint: enable=line-too-long
# Note: When used before module docstrings, Pylint scopes the disable statement
# to the whole rest of the file, so we need an enable statement.
# This module is meant to be safe for 'import *'.
from __future__ import print_function, absolute_import
import warnings
import copy as copy_
import re
try:
from collections import OrderedDict
except ImportError:
from ordereddict import OrderedDict
try:
from collections.abc import ValuesView, ItemsView
except ImportError:
from collections import ValuesView, ItemsView
try:
from builtins import type as builtin_type
except ImportError: # py2
from __builtin__ import type as builtin_type
from xml.dom.minidom import Element
import six
from . import _cim_xml
from .config import SEND_VALUE_NULL
from . import config
from ._cim_types import _CIMComparisonMixin, type_from_name, cimtype, \
atomic_to_cim_xml, CIMType, CIMDateTime, number_types, CIMInt, \
CIMFloat, _Longint, Char16
from ._nocasedict import NocaseDict
from ._utils import _ensure_unicode, _ensure_bool, \
_hash_name, _hash_item, _hash_dict, _format, _integerValue_to_int, \
_realValue_to_float, _to_unicode, _eq_name, _eq_item, _eq_dict, \
_stacklevel_above_module
__all__ = ['CIMClassName', 'CIMProperty', 'CIMInstanceName', 'CIMInstance',
'CIMClass', 'CIMMethod', 'CIMParameter', 'CIMQualifier',
'CIMQualifierDeclaration', 'tocimxml', 'tocimxmlstr', 'cimvalue']
# Constants for MOF formatting output
MOF_INDENT = 3
MAX_MOF_LINE = 80
# Patterns for WBEM URI parsing, consistent with DSP0207, except that for a
# local WBEM URI (no namespace type, no authority), the leading slash required
# by DSP0207 is optional for pywbem.
WBEM_URI_CLASSPATH_REGEXP = re.compile(
r'^(?:([\w\-]+):)?' # namespace type (URI scheme)
r'(?://([\w.:@\[\]]*))?' # authority (host)
r'(?:/|^/?)(\w+(?:/\w+)*)?' # namespace name (leading slash optional)
r'(?::|^:?)(\w+)$', # class name (leading colon optional)
flags=re.UNICODE)
WBEM_URI_INSTANCEPATH_REGEXP = re.compile(
r'^(?:([\w\-]+):)?' # namespace type (URI scheme)
r'(?://([\w.:@\[\]]*))?' # authority (host)
r'(?:/|^/?)(\w+(?:/\w+)*)?' # namespace name (leading slash optional)
r'(?::|^:?)(\w+)' # class name (leading colon optional)
r'\.(.+)$', # key bindings
flags=re.UNICODE)
# For parsing the key bindings using a regexp, we just distinguish the
# differently quoted forms. The exact types of the key values are determined
# lateron:
_KB_NOT_QUOTED = r'[^,"\'\\]+'
_KB_SINGLE_QUOTED = r"'(?:[^'\\]|\\.)*'"
_KB_DOUBLE_QUOTED = r'"(?:[^"\\]|\\.)*"'
_KB_VAL = r'(?:{0}|{1}|{2})'.format(
_KB_NOT_QUOTED, _KB_SINGLE_QUOTED, _KB_DOUBLE_QUOTED)
# To get all repetitions, capture a repeated group instead of repeating a
# capturing group: https://www.regular-expressions.info/captureall.html
WBEM_URI_KEYBINDINGS_REGEXP = re.compile(
r'^(\w+={0})((?:,\w+={0})*)$'.format(_KB_VAL),
flags=(re.UNICODE | re.IGNORECASE))
WBEM_URI_KB_FINDALL_REGEXP = re.compile(
r',\w+={0}'.format(_KB_VAL),
flags=(re.UNICODE | re.IGNORECASE))
# Valid namespace types (URI schemes) for WBEM URI parsing
WBEM_URI_NAMESPACE_TYPES = [
'http', 'https',
'cimxml-wbem', 'cimxml-wbems',
]
# CIM data type names, used for checking
ALL_CIMTYPES = set([
'boolean',
'string',
'char16',
'datetime',
'uint8',
'uint16',
'uint32',
'uint64',
'sint8',
'sint16',
'sint32',
'sint64',
'real32',
'real64',
'reference',
])
class _DictView(object):
# pylint: disable=too-few-public-methods
"""
Base class for directory views, with commmon methods.
"""
def __init__(self, dct):
self._dict = dct
def __len__(self):
return len(self._dict)
def __contains__(self, x):
# pylint: disable=invalid-name
return x in iter(self)
def __reversed__(self):
return reversed(list(iter(self)))
def __repr__(self):
return "{t}({d!r})".format(
t=self.__class__.__name__, d=self._dict)
class propvalue_values(_DictView, ValuesView):
# pylint: disable=too-few-public-methods,invalid-name
"""
Dictionary view returned by CIMInstance.values(), returning the `value`
attribute of the CIMProperty objects that are iterated in the view.
The objects iterated over are the CIMProperty objects in the
CIMinstance.properties dictionary.
"""
def __iter__(self):
for prop in self._dict.values():
yield prop.value
class propvalue_items(_DictView, ItemsView):
# pylint: disable=too-few-public-methods,invalid-name
"""
Dictionary view returned by CIMInstance.items(), returning the `value`
attribute of the CIMProperty objects that are iterated in the view.
The objects iterated over are the CIMProperty objects in the
CIMinstance.properties dictionary.
"""
def __iter__(self):
for pname, prop in self._dict.items():
yield pname, prop.value
def _qualifiers_tomof(qualifiers, indent, maxline=MAX_MOF_LINE):
"""
Return a MOF string with the qualifier values, including the surrounding
square brackets. The qualifiers are ordered by their name.
Return empty string if no qualifiers.
Normally multiline output and may fold qualifiers into multiple lines.
The order of qualifiers is preserved.
Parameters:
qualifiers (NocaseDict of CIMQualifier): Qualifiers to format.
indent (:term:`integer`): Number of spaces to indent each line of
the returned string, counted to the opening bracket in the first line.
Returns:
:term:`unicode string`: MOF string.
"""
if not qualifiers:
return u''
mof = []
mof.append(_indent_str(indent))
mof.append(u'[')
line_pos = indent + 1
mof_quals = []
for q in qualifiers.values():
mof_quals.append(q.tomof(indent + 1 + MOF_INDENT, maxline, line_pos))
delim = ',\n' + _indent_str(indent + 1)
mof.append(delim.join(mof_quals))
mof.append(u']\n')
return u''.join(mof)
def _indent_str(indent):
"""
Return a MOF indent pad unicode string from the indent integer variable
that defines number of spaces to indent. Used to format MOF output.
"""
return u''.ljust(indent, u' ')
def _mof_escaped(strvalue):
# Note: This is a raw docstring because it shows many backslashes, and
# that avoids having to double them.
r"""
Return a MOF-escaped string from the input string.
Parameters:
strvalue (:term:`unicode string`): The string value. Must not be `None`.
Special characters must not be backslash-escaped.
Details on backslash-escaping:
`DSP0004` defines that the character repertoire for MOF string constants
is the entire repertoire for the CIM string datatype. That is, the entire
Unicode character repertoire except for U+0000.
The only character for which `DSP0004` requires the use of a MOF escape
sequence in a MOF string constant, is the double quote (because a MOF
string constant is enclosed in double quotes).
`DSP0004` defines MOF escape sequences for several more characters, but it
does not require their use in MOF. For example, it is valid for a MOF
string constant to contain the (unescaped) characters U+000D (newline) or
U+0009 (horizontal tab), and others.
Processing the MOF escape sequences as unescaped characters may not be
supported by MOF-related tools, and therefore this function plays it safe
and uses the MOF escape sequences defined in `DSP0004` as much as possible.
The following table shows the MOF escape sequences defined in `DSP0004`
and whether they are used (i.e. generated) by this function:
========== ==== ===========================================================
MOF escape Used Character
sequence
========== ==== ===========================================================
\b yes U+0008: Backspace
\t yes U+0009: Horizontal tab
\n yes U+000A: Line feed
\f yes U+000C: Form feed
\r yes U+000D: Carriage return
\" yes U+0022: Double quote (") (required to be used)
\' yes U+0027: Single quote (')
\\ yes U+005C: Backslash (\)
\x<hex> (1) U+<hex>: Any UCS-2 character, where <hex> is one to four
hex digits, representing its UCS code position (this form
is limited to the UCS-2 character repertoire)
\X<hex> no U+<hex>: Any UCS-2 character, where <hex> is one to four
hex digits, representing its UCS code position (this form
is limited to the UCS-2 character repertoire)
========== ==== ===========================================================
(1) Yes, for all other characters in the so called "control range"
U+0001..U+001F.
"""
escaped_str = strvalue
# Escape backslash (\)
escaped_str = escaped_str.replace('\\', '\\\\')
# Escape \b, \t, \n, \f, \r
# Note, the Python escape sequences happen to be the same as in MOF
escaped_str = escaped_str.\
replace('\b', '\\b').\
replace('\t', '\\t').\
replace('\n', '\\n').\
replace('\f', '\\f').\
replace('\r', '\\r')
# Escape remaining control characters (U+0001...U+001F), skipping
# U+0008, U+0009, U+000A, U+000C, U+000D that are already handled.
# We hard code it to be faster, plus we can easily skip already handled
# chars.
# The generic code would be (not skipping already handled chars):
# for cp in range(1, 32):
# c = six.unichr(cp)
# esc = '\\x{0:04X}'.format(cp)
# escaped_str = escaped_str.replace(c, esc)
escaped_str = escaped_str.\
replace(u'\u0001', '\\x0001').\
replace(u'\u0002', '\\x0002').\
replace(u'\u0003', '\\x0003').\
replace(u'\u0004', '\\x0004').\
replace(u'\u0005', '\\x0005').\
replace(u'\u0006', '\\x0006').\
replace(u'\u0007', '\\x0007').\
replace(u'\u000B', '\\x000B').\
replace(u'\u000E', '\\x000E').\
replace(u'\u000F', '\\x000F').\
replace(u'\u0010', '\\x0010').\
replace(u'\u0011', '\\x0011').\
replace(u'\u0012', '\\x0012').\
replace(u'\u0013', '\\x0013').\
replace(u'\u0014', '\\x0014').\
replace(u'\u0015', '\\x0015').\
replace(u'\u0016', '\\x0016').\
replace(u'\u0017', '\\x0017').\
replace(u'\u0018', '\\x0018').\
replace(u'\u0019', '\\x0019').\
replace(u'\u001A', '\\x001A').\
replace(u'\u001B', '\\x001B').\
replace(u'\u001C', '\\x001C').\
replace(u'\u001D', '\\x001D').\
replace(u'\u001E', '\\x001E').\
replace(u'\u001F', '\\x001F')
# Escape single and double quote
escaped_str = escaped_str.replace('"', '\\"')
escaped_str = escaped_str.replace("'", "\\'")
return escaped_str
def mofstr(value, indent=MOF_INDENT, maxline=MAX_MOF_LINE, line_pos=0,
end_space=0, avoid_splits=False, quote_char=u'"'):
"""
Low level function that returns the MOF representation of a string value
(i.e. a value that can be split into multiple parts, for example a string,
reference or datetime typed value).
The function performs the backslash-escaping of characters in the string
(for details, see function _mof_escaped()), handles the splitting into
multiple string parts if the current line does not have sufficient space
left, and surrounds the string parts (or the entire string, if it ends up
having only one part) with the specified quote characters.
The strategy for starting new lines and for splitting the string into parts
is:
* If the string fits into the current line, it is output.
* If the 'avoid_splits' flag is set, a new line is generating. If the
string fits onto the new line, it is output. Otherwise, the string is
split into parts and these are output starting with the new line,
generating additional new lines as needed.
* If the 'avoid_splits' flag is not set, the string is split into parts and
these are output starting with the current line, generating new lines as
needed.
* Strings are first tried to split after the rightmost space character that
would still make it fit onto the line, and only if there is no space
character in that range, the string is split at a non-space position.
Parameters:
value (:term:`unicode string`): The string value. Must not be `None`.
Special characters must not be backslash-escaped.
indent (:term:`integer`): Number of spaces to indent any new lines that
are generated.
maxline (:term:`integer`): Maximum line length for the generated MOF.
line_pos (:term:`integer`): Length of content already on the current
line.
end_space (:term:`integer`): Length of space to be left free on the last
line.
avoid_splits (bool): Avoid splits at the price of starting a new line
instead of using the current line.
quote_char (:term:`unicode string`): Character to be used for surrounding
the string parts with. For CIM string typed values, this must be a
double quote (the default), and for CIM char16 typed values, this must
be a single quote.
Returns:
tuple of
* :term:`unicode string`: MOF string.
* new line_pos
"""
assert isinstance(value, six.text_type)
value = _mof_escaped(value)
quote_len = 2 # length of the quotes surrounding a string part
new_line = u'\n' + _indent_str(indent)
mof = []
while True:
# Prepare safety check for endless loops
saved_value = value
avl_len = maxline - line_pos - quote_len
# Decide whether to start a new line
if len(value) > avl_len - end_space:
if avoid_splits or avl_len < 0:
# Start a new line
mof.append(new_line)
line_pos = indent
avl_len = maxline - indent - quote_len
else:
# Find last fitting blank
blank_pos = value.rfind(u' ', 0, avl_len)
if blank_pos < 0:
# We cannot split at a blank -> start a new line
mof.append(new_line)
line_pos = indent
avl_len = maxline - indent - quote_len
# Check whether the entire string fits (that is a last line, then)
if len(value) <= avl_len - end_space:
mof.append(quote_char)
mof.append(value)
mof.append(quote_char)
line_pos += quote_len + len(value)
break
# Split the string and output the next part
split_pos = value.rfind(u' ', 0, avl_len)
if split_pos < 0:
# We have to split within a word
split_pos = avl_len - 1
part_value = value[0:split_pos + 1]
value = value[split_pos + 1:]
mof.append(quote_char)
mof.append(part_value)
mof.append(quote_char)
line_pos += quote_len + len(part_value)
if value == u'':
break
# A safety check for endless loops
assert value != saved_value, \
_format("Endless loop in mofstr() with state: "
"mof_str={0!A}, value={1!A}, avl_len={2}, end_space={3}, "
"split_pos={4}",
u''.join(mof), value, avl_len, end_space, split_pos)
mof_str = u''.join(mof)
return mof_str, line_pos
def mofval(value, indent=MOF_INDENT, maxline=MAX_MOF_LINE, line_pos=0,
end_space=0):
"""
Low level function that returns the MOF representation of a non-string
value (i.e. a value that cannot not be split into multiple parts, for
example a numeric or boolean value).
If the MOF representation of the value does not fit into the remaining
space of the current line, it is put into a new line, considering the
specified indentation. If it also does not fit on the remaining space of
the new line, ValueError is raised.
Parameters:
value (:term:`unicode string`): The non-string value. Must not be `None`.
indent (:term:`integer`): Number of spaces to indent any new lines that
are generated.
maxline (:term:`integer`): Maximum line length for the generated MOF.
line_pos (:term:`integer`): Length of content already on the current
line.
end_space (:term:`integer`): Length of space to be left free on the last
line.
Returns:
tuple of
* :term:`unicode string`: MOF string.
* new line_pos
Raises:
ValueError: The value does not fit onto an entire new line.
"""
assert isinstance(value, six.text_type)
# Check for output on current line
avl_len = maxline - line_pos - end_space
if len(value) <= avl_len:
line_pos += len(value)
return value, line_pos
# Check for output on new line
avl_len = maxline - indent - end_space
if len(value) <= avl_len:
mof_str = u'\n' + _indent_str(indent) + value
line_pos = indent + len(value)
return mof_str, line_pos
raise ValueError(
_format("Cannot fit value {0!A} onto new MOF line, missing {1} "
"characters", value, len(value) - avl_len))
def moftype(cim_type, refclass):
"""
Converts a CIM data type name to MOF syntax.
"""
return (refclass + ' REF') if cim_type == 'reference' else cim_type
def _scalar_value_tomof(
value, type, indent=0, maxline=MAX_MOF_LINE, line_pos=0, end_space=0,
avoid_splits=False):
# pylint: disable=line-too-long,redefined-builtin
"""
Return a MOF string representing a scalar CIM-typed value.
`None` is returned as 'NULL'.
Parameters:
value (:term:`CIM data type`, :class:`~pywbem.CIMInstance`, :class:`~pywbem.CIMClass`):
The scalar CIM-typed value. May be `None`.
Must not be an array/list/tuple. Must not be a :ref:`CIM object` other
than those listed.
type (string): CIM data type name.
indent (:term:`integer`): Number of spaces to indent any new lines that
are generated.
maxline (:term:`integer`): Maximum line length for the generated MOF.
line_pos (:term:`integer`): Length of content already on the current
line.
end_space (:term:`integer`): Length of space to be left free on the last
line.
avoid_splits (bool): Avoid splits at the price of starting a new line
instead of using the current line.
Returns:
tuple of
* :term:`unicode string`: MOF string.
* new line_pos
""" # noqa: E501
if value is None:
return mofval(u'NULL', indent, maxline, line_pos, end_space)
if type == 'string':
if isinstance(value, six.string_types):
return mofstr(value, indent, maxline, line_pos, end_space,
avoid_splits)
if isinstance(value, (CIMInstance, CIMClass)):
# embedded instance or class
return mofstr(value.tomof(), indent, maxline, line_pos, end_space,
avoid_splits)
raise TypeError(
_format("Scalar value of CIM type {0} has invalid Python type "
"type {1} for conversion to a MOF string",
type, builtin_type(value)))
if type == 'char16':
return mofstr(value, indent, maxline, line_pos, end_space, avoid_splits,
quote_char=u"'")
if type == 'boolean':
val = u'true' if value else u'false'
return mofval(val, indent, maxline, line_pos, end_space)
if type == 'datetime':
val = six.text_type(value)
return mofstr(val, indent, maxline, line_pos, end_space, avoid_splits)
if type == 'reference':
val = value.to_wbem_uri()
return mofstr(val, indent, maxline, line_pos, end_space, avoid_splits)
assert isinstance(value, (CIMFloat, CIMInt)), \
_format("Scalar value of CIM type {0} has invalid Python type {1} "
"for conversion to a MOF string",
type, builtin_type(value))
val = six.text_type(value)
return mofval(val, indent, maxline, line_pos, end_space)
def _value_tomof(
value, type, indent=0, maxline=MAX_MOF_LINE, line_pos=0, end_space=0,
avoid_splits=False):
# pylint: disable=redefined-builtin
"""
Return a MOF string representing a CIM-typed value (scalar or array).
In case of an array, the array items are separated by comma, but the
surrounding curly braces are not added.
Parameters:
value (CIM-typed value or list of CIM-typed values): The value.
indent (:term:`integer`): Number of spaces to indent any new lines that
are generated.
maxline (:term:`integer`): Maximum line length for the generated MOF.
line_pos (:term:`integer`): Length of content already on the current
line.
end_space (:term:`integer`): Length of space to be left free on the last
line.
avoid_splits (bool): Avoid splits at the price of starting a new line
instead of using the current line.
Returns:
tuple of
* :term:`unicode string`: MOF string.
* new line_pos
"""
if isinstance(value, list):
mof = []
for i, v in enumerate(value):
if i > 0:
# Assume we would add comma and space as separator
line_pos += 2
val_str, line_pos = _scalar_value_tomof(
v, type, indent, maxline, line_pos, end_space + 2, avoid_splits)
if i > 0:
# Add the actual separator
mof.append(u',')
if val_str[0] != '\n':
mof.append(u' ')
else:
# Adjust by the space we did not need
line_pos -= 1
mof.append(val_str)
mof_str = u''.join(mof)
else:
mof_str, line_pos = _scalar_value_tomof(
value, type, indent, maxline, line_pos, end_space, avoid_splits)
return mof_str, line_pos
def _cim_keybinding(key, value):
"""
Return a keybinding value, from dict item input (key+value).
Key may be None (for unnamed keys).
The returned value will be a CIM-typed value, except if it was provided as
Python number type (in which case it will remain that type).
Invalid types or values cause TypeError or ValueError to be raised.
"""
if key is not None and isinstance(value, CIMProperty):
if value.name.lower() != key.lower():
raise ValueError(
_format("Invalid keybinding name: CIMProperty.name must be "
"dictionary key {0!A}, but is {1!A}",
key, value.name))
if value.type == 'char16':
return Char16(value.value)
return copy_.copy(value.value)
if value is None:
return None
if isinstance(value, six.text_type):
return value
if isinstance(value, six.binary_type):
return _to_unicode(value)
if isinstance(value, (bool, CIMInstanceName, CIMType)):
return value
# pylint: disable=unidiomatic-typecheck
if builtin_type(value) in number_types:
# Note: The CIM data types are derived from the built-in types, so we
# cannot use isinstance() for this test.
# Ideally, pywbem won't accept keybinding values specified as Python
# number typed values, but require a CIM data type (e.g. Uint32 or
# Real32).
# However, there are two reasons for continuing to allow that:
# * It was allowed in earlier versions of pywbem.
# * Parsing the (untyped) WBEM URI of an instance path, results in
# int or float values without size, and the size information
# to automatically convert that into numeric CIM data types is
# not available.
return value
if isinstance(value, (CIMClass, CIMInstance)):
raise TypeError(
_format("Value of keybinding {0!A} cannot be an embedded object: "
"{1}", key, type(value)))
if isinstance(value, list):
raise TypeError(
_format("Value of keybinding {0!A} cannot be a list", key))
raise TypeError(
_format("Value of keybinding {0!A} has an invalid type: {1}",
key, type(value)))
def _cim_property_value(key, value):
"""
Return a CIMProperty object for representing a property value, from dict
item input (key+value), after performing some checks.
If the input value is a CIMProperty object, it is returned.
Otherwise, a new CIMProperty object is created from the input value, and
returned.
"""
if key is None:
raise ValueError("Property name must not be None")
if isinstance(value, CIMProperty):
if value.name.lower() != key.lower():
raise ValueError(
_format("CIMProperty.name must be dictionary key {0!A}, but is"
"{1!A}", key, value.name))
prop = value
else:
# We no longer check for the common error to set CIM numeric values as
# Python number types, because that is done in the CIMProperty
# init method.
prop = CIMProperty(key, value)
return prop
def _cim_property_decl(key, value):
"""
Return a CIMProperty object for representing a property declaration, from
dict item input (key+value), after performing some checks.
The input value must be a CIMProperty object, which is returned.
"""