• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

pywbem / pywbemtools / test-2757

11 Jan 2026 09:04AM UTC coverage: 29.475% (-58.6%) from 88.026%
test-2757

Pull #1501

github

web-flow
Merge 4229b955d into f9b5e1682
Pull Request #1501: Fixed start program timeouts and other issues in listener tests; Enabled tests again

59 of 116 new or added lines in 3 files covered. (50.86%)

3924 existing lines in 32 files now uncovered.

1953 of 6626 relevant lines covered (29.47%)

0.59 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

7.04
/pywbemtools/pywbemcli/_display_cimobjects.py
1
# (C) Copyright 2020 IBM Corp.
2
# (C) Copyright 2020 Inova Development Inc.
3
# All Rights Reserved
4
#
5
# Licensed under the Apache License, Version 2.0 (the "License");
6
# you may not use this file except in compliance with the License.
7
# You may obtain a copy of the License at
8
#
9
#    http://www.apache.org/licenses/LICENSE-2.0
10
#
11
# Unless required by applicable law or agreed to in writing, software
12
# distributed under the License is distributed on an "AS IS" BASIS,
13
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
# See the License for the specific language governing permissions and
15
# limitations under the License.
16
"""
2✔
17
Common function to display cim objects in multiple formats.
18
display_cimobjects() is the function that should be used for all CIM
19
object display in pywbemcli.
20

21
It displays a list of CIM objects of a single type in multiple formats
22
defined by output_format (as CIM objects (mof, etc.) as one or more tables
23
or as text.
24
"""
25

26

27
import re
2✔
28

29
import click
2✔
30

31
from pywbem import CIMInstanceName, CIMInstance, CIMClass, \
2✔
32
    CIMQualifierDeclaration, CIMClassName, ValueMapping, siunit_obj, \
33
    CIMError, CIM_ERR_NOT_SUPPORTED
34
from pywbem._nocasedict import NocaseDict
2✔
35

36
from ._common import sort_cimobjects, to_wbem_uri_folded
2✔
37
from ._cimvalueformatter import cimvalue_to_fmtd_string
2✔
38
from .._utils import get_terminal_width
2✔
39
from .._output_formatting import DEFAULT_MAX_CELL_WIDTH, \
2✔
40
    output_format_is_table, format_table, fold_strings
41

42
INT_TYPE_PATTERN = re.compile(r'^[su]int(8|16|32|64)$')
2✔
43

44
# Minimum width for table view cell size. Below this width the columns become
45
# unreadable.  Note that this overrides the max_terminal width.
46
MIN_CELL_WIDTH = 10
2✔
47

48
####################################################################
49
#
50
#  Display of CIM objects in format defined by output_format
51
#
52
####################################################################
53

54

55
def display_cim_objects(context, cim_objects, output_format, summary=False,
2✔
56
                        property_list=None, quote_strings=True,
57
                        ignore_null_properties=True, object_order=False,
58
                        ctx_options=None):
59

60
    """
61
    Display CIM objects in form determined by input parameters.
62

63
    Input is either a list of cim objects or a single object. It may be
64
    any of the CIM types.  This is used to display:
65

66
      * CIMClass
67
      * CIMClassName:
68
      * CIMInstance
69
      * CIMInstanceName
70
      * CIMQualifierDeclaration
71
      * class associatiors (tuple of CIMClassName, CIMClass)
72
      * list of the above types
73
      * Dictionary of the above types where the keys are namespaces and the
74
        values are any of the above types. This is the return from commands
75
        that handle multiple namespaces in the command option --namespace.
76

77
    This function may override output type choice in cases where the output
78
    choice is not available for the object type.  Thus, for example,
79
    mof output makes no sense for class names. In that case, the output is
80
    the str with the name or namespace:name.
81

82
    Parameters:
83

84
      context (:class:`ContextObj`):
85
        Click context contained in ContextObj object.
86

87
      objects (iterable of :class:`~pywbem.CIMInstance`,
88
        :class:`~pywbem.CIMInstanceName`, :class:`~pywbem.CIMClass`,
89
        :class:`~pywbem.CIMClassName`,
90
        :class:`~pywbem.CIMQualifierDeclaration`, or tuple where the tuple
91
        consists of (CIMClassName, CIMClass) and is return from class
92
        associators or class references.
93
        Iterable of zero or more CIM objects to be displayed.  If the iterable
94
        is a dictionary it contains the objects for multiple namespaces where
95
        the keys are namespace names and the values are lists of objects in
96
        the namespace.
97

98
      output_format (:term:`string`):
99
        String defining the preferred output format. Must not be None since
100
        the correct output_format must have been selected before this call.
101
        Note that the output formats allowed may depend on a) whether
102
        summary is True, b)the specific type because we do not have a table
103
        output format for CIMClass.
104

105
      summary (:class:`py:bool`):
106
        Boolean that defines whether the data in objects should be displayed
107
        or just a summary of the objects (ex. count of number of objects).
108

109
      property_list (iterable of :term:`string`):
110
        List of property names to be displayed, in the desired order, when the
111
        output format is a table, or None to use the default of sorting
112
        the properties alphabetically within key and non-key groups.
113

114
      quote_strings (:class:`py.bool`):
115
        If False, strings are not encased by quote marks in the table view for
116
        instance displays. The default is True so that strings are encased in
117
        quotes in all views.
118

119
      ignore_null_properties (:class:`py.bool`):
120
        If True, cases where a property has Null value or does not exist in all
121
        of the instances to be displayed will not be included in table output.
122
        They will be included in CIM object(MOF) output. If False, The
123
        properties that do not have a value in any instance will be included in
124
        tables.  This allows table output to ignore properties that do not add
125
        value to the table display.
126

127
      object_order (:class:`py.bool`):
128
        If True sort the objects by classname/objectname rather than by
129
        namespace on so that objects of same class in different namespaces
130
        appear together.
131

132
        Used only when the objects parameter is a dictionary ( i.e. multiple
133
        namespaces)
134
    """
135

136
    # Note: In the docstring above, the line for parameter 'objects' was too
137
    #       long. Since we are not putting it into docmentation, we folded it.
UNCOV
138
    context.spinner_stop()
×
139

140
    # Assure that there is output format exists, None not allowed.
UNCOV
141
    assert output_format
×
142

143
    # Sort valid only for dictionaries.  Single lists are always sorted by
144
    # class name
UNCOV
145
    if not isinstance(cim_objects, NocaseDict):
×
UNCOV
146
        assert not object_order
×
147

148
    # If dictionary of objects in namespaces and there is only one namespace
149
    # reduce to list of cim objects. Code deals with single namespace request
150
    # without identifying it in displays as we do for multiple namespace display
UNCOV
151
    if isinstance(cim_objects, NocaseDict) and len(cim_objects) == 1:
×
UNCOV
152
        ns1 = list(cim_objects.keys())
×
UNCOV
153
        ns = ns1[0]
×
UNCOV
154
        cim_objects = cim_objects[ns]
×
155

156
    # If summary flag, call the summary display function and return
UNCOV
157
    if summary:
×
UNCOV
158
        _display_cim_objects_summary(context, cim_objects, output_format)
×
UNCOV
159
        return
×
160

161
    # Terminate if no actual cim_objects returned for single item and list
162
    # before any further processing. Simplifies further processing so it
163
    # can ignore None conditions.
UNCOV
164
    if cim_objects is None or (isinstance(cim_objects, list) and
×
165
                               not cim_objects):
UNCOV
166
        if context.verbose:
×
UNCOV
167
            click.echo("No objects returned")
×
UNCOV
168
        return
×
169

UNCOV
170
    if isinstance(cim_objects, NocaseDict) and \
×
171
            not any(list(cim_objects.values())):
172
        if context.verbose:
×
173
            click.echo('No objects returned for namespace(s): '
×
174
                       f'{", ".join(cim_objects.keys())}')
175
        return
×
176

177
    # Process by type based on receiving dictionaries of ns:objects or lists
178
    # of objects or tuples from class associators/references
179

UNCOV
180
    if output_format_is_table(output_format):
×
181
        # Table format output is processed as a group in a single table both
182
        # for multiple and single namespaces.
UNCOV
183
        _display_as_table(context, cim_objects, output_format,
×
184
                          property_list, quote_strings,
185
                          ignore_null_properties,
186
                          object_order,
187
                          ctx_options)
UNCOV
188
        return
×
189

190
    # pylint: disable=too-many-function-args
UNCOV
191
    _display_as_cim_objects(cim_objects, output_format, object_order)
×
192

193

194
############################################################################
195
#
196
# Support methods for displaying CIM objects.  This includes multiple
197
# output formats (ie. MOF, TABLE, TEXT), table display and summary display.
198
#
199
############################################################################
200

201
def sort_display_objects(cim_objects):
2✔
202
    """
203
    Sort the list of cim_objects and return a sorted list of the objects.
204
    Includes QualdeclWrapper as sortable object
205
    This method requires that there be at least one object in cim_objects
206
    """
UNCOV
207
    assert isinstance(cim_objects, list)
×
UNCOV
208
    if isinstance(cim_objects[0], QualDeclWrapper):
×
209
        return sorted(cim_objects, key=lambda x: x.name())
×
210

UNCOV
211
    return sort_cimobjects(cim_objects)
×
212

213

214
def _order_display_objects(cim_objects, object_order):
2✔
215
    """
216
    Orders the objects in cim_objects in object_name, ns order if object order
217
    is set.  Otherwise it keeps the original order which is the order objects
218
    were inserted for the namespaces and sorts the order of the returned
219
    objects.
220
    Defines an iterator which is returned. That iterator
221
    will return namespace, cim_object for each cim object to be displayed
222
    in the order defined by object_order.
223
    """
UNCOV
224
    if object_order:
×
225
        # Reorder the objects so an iterator returns in object, ns order
UNCOV
226
        order_dict = NocaseDict()
×
UNCOV
227
        for ns, ns_objects in cim_objects.items():
×
UNCOV
228
            if ns_objects:
×
UNCOV
229
                if not isinstance(ns_objects, list):
×
230
                    ns_objects = [ns_objects]
×
UNCOV
231
                ns_objects = sort_display_objects(ns_objects)
×
232

233
                # Determine the object name and find all matching objects
234
                # in the list
UNCOV
235
                for obj in ns_objects:
×
UNCOV
236
                    if isinstance(obj, (CIMClass, CIMClassName)):
×
UNCOV
237
                        obj_name = obj.classname
×
238
                    # Instance name without namespace becomes name for ordering
UNCOV
239
                    elif isinstance(obj, CIMInstance):
×
UNCOV
240
                        objpath = obj.path.copy()
×
UNCOV
241
                        objpath.namespace = None
×
UNCOV
242
                        obj_name = objpath.to_wbem_uri(format="canonical")
×
UNCOV
243
                    elif isinstance(obj, CIMInstanceName):
×
UNCOV
244
                        objtmp = obj.copy()
×
UNCOV
245
                        objtmp.namespace = None
×
UNCOV
246
                        obj_name = objtmp.to_wbem_uri(format="canonical")
×
UNCOV
247
                    elif isinstance(obj, CIMQualifierDeclaration):
×
UNCOV
248
                        obj_name = obj.name
×
UNCOV
249
                    elif isinstance(obj, str):
×
UNCOV
250
                        obj_name = obj
×
251
                    else:
252
                        assert False
×
253

UNCOV
254
                    assert obj_name
×
255

256
                    # Add obj_name,ns,objects to order_dict
UNCOV
257
                    if obj_name not in order_dict:
×
UNCOV
258
                        order_dict[obj_name] = NocaseDict()
×
UNCOV
259
                    if ns not in order_dict[obj_name]:
×
UNCOV
260
                        order_dict[obj_name][ns] = [obj]
×
261
                    else:
262
                        order_dict[obj_name][ns].append(obj)
×
263

UNCOV
264
        for obj_name, namespaces in order_dict.items():
×
UNCOV
265
            for ns in namespaces:
×
266
                # pylint: disable=unnecessary-dict-index-lookup
UNCOV
267
                objs = order_dict[obj_name][ns]
×
UNCOV
268
                for obj in objs:
×
UNCOV
269
                    yield ns, obj
×
270

271
    # No reordering required. Return in ns, object name order
272
    # iterator returns namespace, object in namespace, object order
273
    else:
UNCOV
274
        for ns, ns_objects in cim_objects.items():
×
UNCOV
275
            if ns_objects:
×
UNCOV
276
                if not isinstance(ns_objects, list):
×
UNCOV
277
                    ns_objects = [ns_objects]
×
UNCOV
278
                for ns_object in sort_display_objects(ns_objects):
×
UNCOV
279
                    yield ns, ns_object
×
280

281

282
def _display_as_cim_objects(cim_objects, output_format, object_order):
2✔
283
    """
284
    Display the objects in the objects parameter using one of the individual
285
    object display formats (MOF, etc.). This parameter may be either a
286
    NocaseDictionary (multi-namespace type request) or a list (single namespace
287
    type request)
288
    """
289
    # CIM Object and text output formats are displayed as single objects.
290
    # Note: for class associaters/references each object is a tuple of
291
    # CIMClassName, CIMClass
292
    # If object_order is True, reorganize so the order of display
293
    # is class, namespace
294
    # Otherwise, the order of display is namespace, class
295

296
    # Starts with dict(namespace: list_of_objects)
297
    # Want dict (class: namespace: list of objects with this class, namespace)
298

UNCOV
299
    if isinstance(cim_objects, NocaseDict):
×
UNCOV
300
        for ns, ns_object in _order_display_objects(cim_objects, object_order):
×
UNCOV
301
            _display_one_cim_object(ns_object, output_format, namespace=ns)
×
UNCOV
302
        return
×
303

304
    # If not NocaseDict, it is list or single object. Sort by classname and
305
    # then call _display_one_cim_objects with each item from list
UNCOV
306
    if not isinstance(cim_objects, list):
×
UNCOV
307
        cim_objects = [cim_objects]
×
308

UNCOV
309
    for cim_obj in sort_display_objects(cim_objects):
×
UNCOV
310
        _display_one_cim_object(cim_obj, output_format)
×
311

312

313
def _display_as_table(context, cim_objects, output_format, property_list,
2✔
314
                      quote_strings, ignore_null_properties, object_order,
315
                      ctx_options):
316
    """
317
    Display the cim_objects as a table where the table rows are dependent on
318
    the cim object type being displayed. Calls a method for each type of object
319
    to display that object type information as a table.
320

321
    This function processes objects from multiple namespaces in a NocaseDict to
322
    produce a single table showing the requested output in one row per
323
    namespace/class or from a single list (objects from single namespace) to
324
    produce a table that does not show the namespaces
325
    """
326
    # If NocaseDic reduce cim_objects to a new list of objects for simpler
327
    # table processing. String and QualiferDeclaration objects are modified to
328
    # include the namespace with each object.
UNCOV
329
    if isinstance(cim_objects, NocaseDict):
×
UNCOV
330
        objects = []
×
UNCOV
331
        for ns, ns_object in _order_display_objects(cim_objects, object_order):
×
UNCOV
332
            if isinstance(ns_object, CIMQualifierDeclaration):
×
UNCOV
333
                objects.append(QualDeclWrapper(ns, ns_object))
×
UNCOV
334
            elif isinstance(ns_object, str):
×
335
                objects.append(CIMClassName(ns_object, namespace=ns))
×
336
            else:
UNCOV
337
                objects.append(ns_object)
×
338

339
        # pass ordered object list on to display function for tables
UNCOV
340
        _display_list_as_table(context, objects, output_format,
×
341
                               property_list, quote_strings,
342
                               ignore_null_properties,
343
                               use_namespace=True,
344
                               ctx_options=ctx_options)
345

346
    else:  # original input was a list sort but do not include namespace
UNCOV
347
        if not isinstance(cim_objects, (list)):
×
UNCOV
348
            cim_objects = [cim_objects]
×
UNCOV
349
        cim_objects = sort_display_objects(cim_objects)
×
UNCOV
350
        _display_list_as_table(context, cim_objects, output_format,
×
351
                               property_list, quote_strings,
352
                               ignore_null_properties,
353
                               use_namespace=False,
354
                               ctx_options=ctx_options)
355

356

357
def _display_list_as_table(context, cim_objects, output_format, property_list,
2✔
358
                           quote_strings, ignore_null_properties,
359
                           use_namespace=None, ctx_options=None):
360
    """
361
    Display the cim_objects parameter list as a table.  The parameter must be a
362
    list. Calls lower level display functions for each object type.
363

364
    cim_objects in form of a single list of the objects in the order that
365
    they will be displayed in the table.
366
    """
UNCOV
367
    assert isinstance(cim_objects, list)
×
UNCOV
368
    assert use_namespace is not None
×
369

UNCOV
370
    table_width = get_terminal_width()
×
371

UNCOV
372
    if cim_objects:  # This and the list test probably not necessary
×
373
        # Call table display method for each object type
UNCOV
374
        if isinstance(cim_objects[0], CIMInstance):
×
UNCOV
375
            _display_instances_as_table(
×
376
                cim_objects, table_width, output_format, context=context,
377
                property_list=property_list, quote_strings=quote_strings,
378
                ignore_null_properties=ignore_null_properties,
379
                namespace=use_namespace, ctx_options=ctx_options)
UNCOV
380
        elif isinstance(cim_objects[0], CIMClass):
×
381
            _display_classes_as_table(cim_objects, table_width, output_format)
×
UNCOV
382
        elif isinstance(cim_objects[0], QualDeclWrapper):
×
UNCOV
383
            _display_qual_decls_as_table(cim_objects, table_width,
×
384
                                         output_format)
UNCOV
385
        elif isinstance(cim_objects[0], CIMQualifierDeclaration):
×
UNCOV
386
            _display_qual_decls_as_table(cim_objects, table_width,
×
387
                                         output_format)
UNCOV
388
        elif isinstance(cim_objects[0], (CIMClassName, CIMInstanceName,
×
389
                                         (str,), tuple)):
UNCOV
390
            _display_paths_as_table(cim_objects, table_width, output_format,
×
391
                                    namespace=use_namespace)
392
        else:
393
            assert False
×
394

395

396
def _display_one_cim_object(cim_object, output_format, namespace=None):
2✔
397
    """
398
    Display a single CIM object.  This creates and outputs the display for one
399
    CIM object in  the output formats groups CIM object (MOF, XML, Text).
400

401
    NOTE: namespace defines the namespace in which the object exists. It is
402
    used in some cases to add namespace information to the display.
403
    Specifically it is used if the object is converted to mof, xml to add the
404
    # pragama statement and if the object is a string type (i.e. classname)
405
    """
UNCOV
406
    assert isinstance(
×
407
        cim_object, (CIMClass, CIMClassName, CIMInstance, CIMInstanceName,
408
                     CIMQualifierDeclaration, tuple, (str,)))
409

410
    # Display in the selected CIM object format (mof, xml, repr, txt)
UNCOV
411
    if output_format == 'mof':
×
UNCOV
412
        try:
×
UNCOV
413
            mofstr = cim_object.tomof()
×
UNCOV
414
            if namespace:
×
UNCOV
415
                click.echo(f"#pragma namespace (\"{namespace}\")")
×
UNCOV
416
            click.echo(mofstr)
×
417

UNCOV
418
        except AttributeError:
×
419
            # inserting NL between instance names for readability since the
420
            # display is always a single line per display
UNCOV
421
            if isinstance(cim_object, CIMInstanceName):
×
UNCOV
422
                click.echo(f"\n{cim_object}")
×
UNCOV
423
            elif isinstance(cim_object, CIMClassName):
×
UNCOV
424
                click.echo(cim_object)
×
UNCOV
425
            elif isinstance(cim_object, tuple):  # representation of class assoc
×
UNCOV
426
                assert isinstance(cim_object[0], CIMClassName)
×
UNCOV
427
                assert isinstance(cim_object[1], CIMClass)
×
UNCOV
428
                click.echo(cim_object[0])
×
UNCOV
429
                click.echo(cim_object[1].tomof())
×
UNCOV
430
            elif isinstance(cim_object, str):
×
UNCOV
431
                if namespace:
×
UNCOV
432
                    click.echo(f"{namespace}:{cim_object}")
×
433
                else:
UNCOV
434
                    click.echo(cim_object)
×
435
            else:
436
                raise click.ClickException(
×
437
                    f'output_format {output_format} invalid '
438
                    f'for {type(cim_object)}')
UNCOV
439
    elif output_format == 'xml':
×
UNCOV
440
        try:
×
UNCOV
441
            if isinstance(cim_object,
×
442
                          (CIMClass, CIMInstance, CIMQualifierDeclaration,
443
                           CIMInstanceName, CIMClassName)):
UNCOV
444
                if namespace:
×
445
                    click.echo(f"<!-- Namespace = {namespace} -->")
×
UNCOV
446
                click.echo(cim_object.tocimxmlstr(indent=4))
×
UNCOV
447
            elif isinstance(cim_object, tuple):
×
UNCOV
448
                if namespace:
×
UNCOV
449
                    click.echo(f"<!-- Namespace = {namespace} -->")
×
UNCOV
450
                assert isinstance(cim_object[0], CIMClassName)
×
UNCOV
451
                assert isinstance(cim_object[1], CIMClass)
×
UNCOV
452
                click.echo(cim_object[0].tocimxmlstr(indent=4))
×
UNCOV
453
                click.echo(cim_object[1].tocimxmlstr(indent=4))
×
454
            elif isinstance(cim_object, str):
×
455
                if namespace:
×
456
                    click.echo(f"{namespace}:{cim_object}")
×
457
                else:
458
                    click.echo(cim_object)
×
459
            else:
460
                assert False, f"Output_format {output_format} invalid " \
×
461
                              f"for {type(cim_object)}"
462

463
        except AttributeError:
×
464
            # no tocimxmlstr functionality
465
            raise click.ClickException(
×
466
                f'Output Format {output_format} not supported. '
467
                f'Default to\n{cim_object!r}')
UNCOV
468
    elif output_format == 'repr':
×
UNCOV
469
        try:
×
UNCOV
470
            click.echo(repr(cim_object))
×
471
        except AttributeError:
×
472
            raise click.ClickException(
×
473
                f'"repr" display of {cim_object!r} failed')
474

UNCOV
475
    elif output_format == 'txt':
×
UNCOV
476
        try:
×
UNCOV
477
            click.echo(cim_object)
×
478
        except AttributeError:
×
479
            raise click.ClickException(
×
480
                f'"txt" display of {cim_object!r} failed')
481
    else:
482
        raise click.ClickException(f'Invalid output format {output_format}')
×
483

484

485
class QualDeclWrapper():  # pylint: disable=too-few-public-methods
2✔
486
    """
487
    Convert qualifier declarations to instance of this class to be able to
488
    pass the namespace to the display function unambiguously.  This is only
489
    a wrapper within the display_cim_objects method and its sub-methods since
490
    the CIM_QualifierDeclaration has no namespace component.
491
    """
492
    def __init__(self, namespace, qual_decl_inst):
2✔
UNCOV
493
        self.qualdecl = qual_decl_inst
×
UNCOV
494
        self.namespace = namespace
×
495

496
    def name(self):
2✔
497
        """Return the qualifier declaration name."""
498
        return self.qualdecl.name
×
499

500

501
def _get_cimtype(objects):
2✔
502
    """
503
    Get the cim_type for any returned cim object.  Normally this is the
504
    name of the class name except that the classname return from
505
    getclass and enumerate class is just unicode string
506

507
    Parameters:
508
      objects (dict, list, object where object is any CIM object or tuple)
509

510
    Returns:
511
      The CIM type (string) for that object. If the object is a tuple, the
512
      type comes from the second object in the tuple
513

514
      If no objects are in thd dict or list None is returned
515

516
    """
517
    # associators and references return tuple
UNCOV
518
    if isinstance(objects, list) and objects:
×
UNCOV
519
        test_object = objects[0]
×
UNCOV
520
    elif objects:
×
521
        test_object = object
×
522
    else:
UNCOV
523
        return None
×
524

UNCOV
525
    if isinstance(test_object, tuple):
×
526
        # associator or reference class level return is tuple get type
527
        # from second item, it is the requested object
528
        cim_type = test_object[1].__class__.__name__
×
529
    else:
UNCOV
530
        cim_type = test_object.__class__.__name__
×
531

532
    # account for fact the enumerate class name operation returns uniicode.
UNCOV
533
    if isinstance(test_object, str):
×
UNCOV
534
        cim_type = 'CIMClassName'
×
UNCOV
535
    return cim_type
×
536

537

538
def _display_cim_objects_summary(context, objects, output_format):
2✔
539
    """
540
    Display a summary of the objects received. This displays the
541
    count of objects.
542
    """
UNCOV
543
    context.spinner_stop()
×
544

UNCOV
545
    cim_type = None
×
UNCOV
546
    if isinstance(objects, NocaseDict):
×
UNCOV
547
        headers = ['Namespace', 'Count', 'CIM Type']
×
UNCOV
548
        rows = []
×
UNCOV
549
        for ns, objlist in objects.items():
×
UNCOV
550
            if not cim_type:
×
UNCOV
551
                cim_type = _get_cimtype(objlist)
×
UNCOV
552
            objlistlen = len(objlist) if objlist else 0
×
UNCOV
553
            rows.append([ns, objlistlen, cim_type])
×
554
    else:
UNCOV
555
        headers = ['Count', 'CIM Type']
×
UNCOV
556
        if isinstance(objects, list):
×
UNCOV
557
            cim_type = _get_cimtype(objects)
×
UNCOV
558
            rows = [[len(objects), cim_type]]
×
559
        elif objects is not None:
×
560
            cim_type = _get_cimtype(objects)
×
561
            rows = [["1", cim_type]]
×
562
        else:
563
            rows = [[0, cim_type]]
×
564

UNCOV
565
    title = f'Summary of {cim_type}(s) returned'
×
UNCOV
566
    if output_format_is_table(output_format):
×
UNCOV
567
        click.echo(format_table(rows, headers, title=title,
×
568
                                table_format=output_format))
569
    else:
UNCOV
570
        for row in rows:  # must be type text
×
571
            # The following two are hangover from initial format output
572
            # "<number <object_name>(s)". Future, redo this whole output format
UNCOV
573
            cim_type_pos = len(row) - 1
×
UNCOV
574
            len_pos = len(row) - 2
×
UNCOV
575
            if row[len_pos] == 0:
×
UNCOV
576
                row[cim_type_pos] = "objects"
×
577
            else:
UNCOV
578
                row[cim_type_pos] = f"{row[cim_type_pos]}(s)"
×
UNCOV
579
            row[len_pos] = str(row[len_pos])
×
UNCOV
580
            click.echo(f'{" ".join(row)} returned')
×
581

582

583
#
584
# The following functions each display one object type as a table
585
#
586
def _display_classes_as_table(classes, table_width, table_format):
2✔
587
    """
588
    Display classes as a table.
589
    """
590
    # pylint: disable=unused-argument
591

592
    # ISSUE #249: Display classes as a table or mof rather than just as MOF.
593
    for class_ in classes:
×
594
        click.echo(class_.tomof())
×
595

596

597
def _display_paths_as_table(objects, table_width, table_format, namespace=None):
2✔
598
    # pylint: disable=unused-argument
599
    """
600
    Display paths as a table. This include CIMInstanceName, ClassPath,
601
    and unicode (the return type for enumerateClasses).
602
    """
UNCOV
603
    title = None
×
UNCOV
604
    if objects:
×
605
        # Strings only for single namespace requests for classnames.
606
        # No namespace in result
UNCOV
607
        if isinstance(objects[0], str):
×
UNCOV
608
            title = 'Classnames:'
×
UNCOV
609
            headers = ['Class Name']
×
UNCOV
610
            rows = [[obj] for obj in objects]
×
611
        # if obj is tuple, this is a tuple of namespace and classname
UNCOV
612
        elif isinstance(objects[0], tuple):
×
613
            assert False
×
614
            title = 'Classnames:'
615
            headers = ("namespace", 'class')
616
            rows = [[obj[0], obj[1]] for obj in objects]
617

UNCOV
618
        elif isinstance(objects[0], CIMClassName):
×
619
            title = 'Classnames'
×
620
            headers = ('host', 'namespace', 'class')
×
621
            rows = [[obj.host, obj.namespace, obj.classname] for obj in objects]
×
622

UNCOV
623
        elif isinstance(objects[0], CIMInstanceName):
×
624
            # Since there are cases where the list has instances with
625
            # different keys (i.e. enumerate CIM_ManagedElement), build
626
            # a dictionary for instances with the same key names.
627
            # Sort into dictionary for each set of key names.  If all keys are
628
            # the same, there will be one table.  This allows building
629
            # a table for each set of keynames
UNCOV
630
            objs_by_key_set = {}
×
UNCOV
631
            original_keys = {}
×
632

UNCOV
633
            for instname in objects:
×
634
                # key_names is a tuple of sorted key names in lower case so
635
                # all instances with the same set of keys ends up in a
636
                # single dictionary item. NOTE: objects must be tuple to be
637
                # a dictionary key, the key.
UNCOV
638
                test_key_names = tuple(
×
639
                    sorted(map(lambda x: x.lower(), instname.keys())))
UNCOV
640
                try:
×
UNCOV
641
                    objs_by_key_set[test_key_names].append(instname)
×
UNCOV
642
                except KeyError:
×
UNCOV
643
                    objs_by_key_set[test_key_names] = [instname]
×
644

645
                # Set the original keys into dict to recover correct key
646
                # case later
UNCOV
647
                if test_key_names not in original_keys:
×
UNCOV
648
                    original_keys[test_key_names] = instname.keys()
×
649

650
            # If multiple key_names we create multiple tables and add table
651
            # number to each table title.
652
            # FUTURE: This was a hack. by forcing classname into the table
UNCOV
653
            table_number = 0
×
UNCOV
654
            for key_names, inst_names in objs_by_key_set.items():
×
655
                # Build headers for this table with the common elements and key
656
                # names for each key in the object. We use inst_names[0] to
657
                # restore original case to key strings. We sort keys for
658
                # consistent table output.
UNCOV
659
                inst_keys = sorted(original_keys[key_names])
×
660

UNCOV
661
                rows = []
×
UNCOV
662
                for instname in inst_names:
×
663
                    # Build header row for this table
UNCOV
664
                    row = [instname.host, instname.namespace,
×
665
                           instname.classname]
UNCOV
666
                    for key in inst_keys:
×
UNCOV
667
                        if isinstance(instname[key], CIMInstanceName):
×
668
                            # If key is CIMInstanceName, fold the value
UNCOV
669
                            row.append(to_wbem_uri_folded(
×
670
                                instname[key], uri_format='standard',
671
                                max_len=30))
672
                        else:
UNCOV
673
                            row.append(instname[key])
×
UNCOV
674
                    rows.append(row)
×
675

676
                # If multiple tables, number them as hint to reader that there
677
                # are multiples.
UNCOV
678
                if len(objs_by_key_set) > 1:
×
679
                    table_number += 1
×
680
                    table_number_str = f", (table #{table_number})"
×
681
                else:
UNCOV
682
                    table_number_str = ''
×
683

UNCOV
684
                headers = ['host', 'namespace', 'class'] + \
×
685
                    [f"key=\n{kn}" for kn in inst_keys]
686

UNCOV
687
                title = f'InstanceNames: {inst_names[0].classname}' \
×
688
                        f'{table_number_str}'
689

690
                # Generate multiple tables, one for each key_name and
691
                # return local to this scope.
UNCOV
692
                click.echo(format_table(rows, headers, title=title,
×
693
                                        table_format=table_format))
694

UNCOV
695
            return  # Return to avoid following table output
×
696

697
        else:
698
            assert False
×
699

UNCOV
700
        click.echo(format_table(rows, headers, title=title,
×
701
                                table_format=table_format))
702

703

704
def _display_qual_decls_as_table(qual_decls, table_width, table_format):
2✔
705
    """
706
    Display the elements of qualifier declarations as a table with a
707
    row for each qualifier declaration and a column for each of the attributes
708
    of the qualifier declaration (name, type, Value, Array, Scopes, Flavors.
709

710
    qual_decls is a list of the class QualDeclWrapper where each instance is
711
    namespace, qualdecl ib multiple namespaces are to be didsplayed or a list
712
    of qualdecls if only one namespace is to be displayed
713

714
    The function displays all of the qualifier declarations in the the list
715
    if a qualdecls is a list of CIMQualifierdecl or or by namespace if
716
    qualdecls is a list of tuples
717
    """
UNCOV
718
    def build_row(qdecl, ns):
×
719
        """
720
        Build a single row representing a qualifier declaration
721
        """
UNCOV
722
        scopes = '\n'.join([key for key in qdecl.scopes if qdecl.scopes[key]])
×
UNCOV
723
        flavors = []
×
UNCOV
724
        flavors.append('EnableOverride' if qdecl.overridable
×
725
                       else 'DisableOverride')
UNCOV
726
        flavors.append('ToSubclass' if qdecl.tosubclass else 'Restricted')
×
UNCOV
727
        if qdecl.translatable:
×
UNCOV
728
            flavors.append('Translatable')
×
UNCOV
729
        sep = "\n" if sum(map(len, flavors)) >= max_column_width else ", "
×
UNCOV
730
        flavors = sep.join(flavors)
×
UNCOV
731
        row = [qdecl.name, qdecl.type, qdecl.value, qdecl.is_array, scopes,
×
732
               flavors]
UNCOV
733
        if ns:
×
UNCOV
734
            row.insert(0, ns)
×
UNCOV
735
        return row
×
736

737
    # Build header line.  Add Namespace column if namespace flag set.
UNCOV
738
    rows = []
×
UNCOV
739
    headers = ['Name', 'Type', 'Value', 'Array', 'Scopes', 'Flavors']
×
740

741
    # build the data rows
UNCOV
742
    max_column_width = int(table_width / len(headers)) - 4
×
UNCOV
743
    rows = []
×
UNCOV
744
    add_namespace = False
×
UNCOV
745
    for qdecl in qual_decls:
×
UNCOV
746
        if isinstance(qdecl, QualDeclWrapper):
×
UNCOV
747
            row = build_row(qdecl.qualdecl, qdecl.namespace)
×
UNCOV
748
            add_namespace = True
×
749
        else:
UNCOV
750
            row = build_row(qdecl, None)
×
UNCOV
751
        rows.append(row)
×
752

UNCOV
753
    if add_namespace:
×
UNCOV
754
        headers.insert(0, "namespace")
×
755

UNCOV
756
    click.echo(format_table(rows, headers, title='Qualifier Declarations',
×
757
                            table_format=table_format))
758

759

760
def _format_instances_as_rows(insts, max_cell_width, include_classnames=False,
2✔
761
                              context=None, prop_names=None,
762
                              quote_strings=True, namespace=None):
763
    """
764
    Format the list of instances properties into a list of the property
765
    values for each instance(a row of the table) gathered into a list of
766
    the rows.
767

768
    The prop_names parameter is the list of (originally cased) property names
769
    to be output, in the desired output order. It could be determined from
770
    the instances, but since it is determined already by the caller, it
771
    is passed in as an optimization. For test convenience, None is permitted
772
    and causes the properties to again be determined from the instances.
773

774
    Include_classesnames for each instance if True. Sets the classname as
775
    the first (or second if multiple namespaces) column.
776

777
    max_width if not None folds col entries longer than the defined
778
    max_cell_width. If max_width is None, the data length is ignored.
779

780
    The property values are formatted similar to MOF output. Properties that
781
    have a ValueMap qualifier (effectively, in the creation class of the
782
    instance) are shown with both the actual property value and the mapped
783
    value in parenthesis.
784

785
    NOTE: This is a separate function to allow testing of the table formatting
786
    independently of print output.
787

788
    Returns:
789
        list of strings where each string is a row in the table and each
790
        item in a row is a cell entry
791
    """
UNCOV
792
    conn = context.pywbem_server.conn if context else None
×
793

794
    # Avoid crash deeper in code if max_cell_width is None.
795
    # FUTURE: Should move this up to common place
UNCOV
796
    if max_cell_width is None:
×
UNCOV
797
        max_cell_width = DEFAULT_MAX_CELL_WIDTH
×
UNCOV
798
    rows = []
×
799

800
    # Build list of properties in any of the instances to display.
UNCOV
801
    if prop_names is None:
×
UNCOV
802
        prop_names = _sorted_prop_names(insts)
×
803

804
    # Cache of ValueMapping objects for integer-typed properties.
805
    # Key: classname.propertyname, both in lower case.
806
    # A value of None indicates the property does not have a value mapping.
UNCOV
807
    valuemappings = {}
×
808

UNCOV
809
    for inst in insts:
×
UNCOV
810
        assert isinstance(inst, CIMInstance), \
×
811
            f"Invalid CIM Type {type(inst)}"
812

813
        # Setup row list and set namespace if this is multi-namespace
814
        # Note: We use namespace as a flag here ignoring the actual values.
UNCOV
815
        row = [inst.path.namespace] if namespace else []
×
816

817
        # Insert classname before properties if flag set
UNCOV
818
        if include_classnames:
×
UNCOV
819
            if row:
×
UNCOV
820
                row.append(inst.classname)
×
821
            else:
UNCOV
822
                row = [inst.classname]
×
823

824
        # Get value for each property in this object
UNCOV
825
        for name in prop_names:
×
826
            # Account for possible instances without all properties
827
            # Outputs empty  string.  Note that instance with no value
828
            # results in same output as not instance name.
UNCOV
829
            if name not in inst.properties:
×
UNCOV
830
                val_str = ''
×
831
            else:
UNCOV
832
                value = inst.get(name)
×
UNCOV
833
                prop = inst.properties[name]
×
834

835
                # Cache value mappings for integer-typed properties
UNCOV
836
                if INT_TYPE_PATTERN.match(prop.type) and context:
×
UNCOV
837
                    vm_key = f'{inst.classname.lower()}.{name.lower()}'
×
UNCOV
838
                    try:
×
UNCOV
839
                        valuemapping = valuemappings[vm_key]
×
UNCOV
840
                    except KeyError:
×
UNCOV
841
                        try:
×
UNCOV
842
                            valuemapping = ValueMapping.for_property(
×
843
                                conn,
844
                                inst.path.namespace,
845
                                inst.classname,
846
                                name)
UNCOV
847
                        except ValueError:
×
848
                            # Property does not have a value mapping.
UNCOV
849
                            valuemapping = None
×
UNCOV
850
                        valuemappings[vm_key] = valuemapping
×
851
                else:
UNCOV
852
                    valuemapping = None
×
853

UNCOV
854
                if value is None:
×
UNCOV
855
                    val_str = ''
×
856
                else:
UNCOV
857
                    val_str, _ = cimvalue_to_fmtd_string(
×
858
                        prop.value, prop.type, indent=0, maxline=max_cell_width,
859
                        line_pos=0, end_space=0, avoid_splits=False,
860
                        valuemapping=valuemapping, quote_strings=quote_strings)
UNCOV
861
            row.append(val_str)
×
UNCOV
862
        rows.append(row)
×
863

UNCOV
864
    return rows
×
865

866

867
def _display_instances_as_table(insts, table_width, table_format,
2✔
868
                                include_classnames=False, context=None,
869
                                property_list=None, quote_strings=True,
870
                                ignore_null_properties=True,
871
                                namespace=None, ctx_options=None):
872
    """
873
    Print the properties of the instances defined in insts as a table where
874
    each row is an instance and each column is a property value.
875

876
    All properties in the instance are included.
877

878
    The header line consists of the property names.
879

880
    The property values are formatted similar to MOF output. Properties that
881
    have a ValueMap qualifier (effectively, in the creation class of the
882
    instance) are shown with both the actual property value and the mapped
883
    value in parenthesis.
884

885
    """
UNCOV
886
    conn = context.pywbem_server.conn if context else None
×
887

UNCOV
888
    if table_width is None:
×
UNCOV
889
        table_width = get_terminal_width()
×
890

UNCOV
891
    for inst in insts:
×
UNCOV
892
        assert isinstance(inst, CIMInstance)
×
893

894
    # Include classnames if multiple classes in display: See issue # 1145
895
    # If multiple classes, display classname in table, otherwise show in title
UNCOV
896
    classnames = [inst.classname.lower() for inst in insts]
×
UNCOV
897
    include_classnames = bool(len(set(classnames)) > 1)
×
898

899
    # Build list of properties, either all properties in a list or all
900
    # properties in all instances in list of instances
UNCOV
901
    if property_list:
×
UNCOV
902
        prop_names = _propertylist_prop_names(insts, property_list)
×
903
    else:
UNCOV
904
        prop_names = _sorted_prop_names(insts)
×
905

UNCOV
906
    if ignore_null_properties:
×
907
        # Get names of properties in any instances that have a value (i.e.
908
        # not None or empty list)
UNCOV
909
        props_with_value_dict = NocaseDict()
×
UNCOV
910
        for inst in insts:
×
UNCOV
911
            for propname, propvalue in inst.properties.items():
×
UNCOV
912
                if propvalue.value is not None:
×
UNCOV
913
                    props_with_value_dict[propname] = True
×
914
        # Rebuild prop names from dict in same order as original list
UNCOV
915
        prop_names = [pn for pn in prop_names if pn in props_with_value_dict]
×
916

917
    # Try to estimate max cell width from number of cols and properties
918
    # This allows folding long data. Further, the actual output
919
    # width of a column involves the tabulate outputter, output_format
920
    # so this is not deterministic.
UNCOV
921
    max_cell_width = int(table_width / len(prop_names)) \
×
922
        if prop_names else table_width
923

924
    # Sets a minimum size for cells so they are at least readable.
925
    # This means we can build tables wider than the terminal width.
UNCOV
926
    max_cell_width = max(max_cell_width, MIN_CELL_WIDTH)
×
927

928
    # ISSUE #953 Future: Decide whether and how the showing of units should be
929
    #            controlled. Or are we satisfied with always showing them?
UNCOV
930
    show_units = True
×
931

932
    # Retrieve all creation classes of the instances in order to get
933
    # to their PUnit and Units qualifiers.
UNCOV
934
    if show_units:
×
UNCOV
935
        class_objs = NocaseDict()  # CIMClass objects, by classname
×
UNCOV
936
        for inst in insts:
×
UNCOV
937
            classname = inst.classname
×
UNCOV
938
            if classname in class_objs:
×
UNCOV
939
                continue
×
UNCOV
940
            if inst.path is None:
×
941
                # The only operation returning instances without a path is
942
                # query execution. For now, we simply don't display units in
943
                # case the inst.path is None.
944
                # ISSUE #953: Pass the namespace for query execution to allow
945
                # class retrieval as part of getting  units information.
UNCOV
946
                show_units = False
×
UNCOV
947
                break
×
UNCOV
948
            try:
×
UNCOV
949
                class_obj = conn.GetClass(
×
950
                    classname, namespace=inst.path.namespace,
951
                    IncludeQualifiers=True, LocalOnly=False)
952
            except CIMError as exc:
×
953
                if exc.status_code == CIM_ERR_NOT_SUPPORTED:
×
954
                    # The WBEM Server does not support class operations. We
955
                    # silently give up showing units in this case.
956
                    show_units = False
×
957
                    break
×
UNCOV
958
            class_objs[classname] = class_obj
×
959

960
    # Construct the header line
UNCOV
961
    headers = []  # list of header strings
×
962
    # If we want to include namespace in table as a row
UNCOV
963
    if namespace:
×
UNCOV
964
        headers.append("namespace")
×
UNCOV
965
    if include_classnames:
×
UNCOV
966
        headers.append("classname")
×
UNCOV
967
    for pname in prop_names:
×
UNCOV
968
        hdr = pname
×
UNCOV
969
        if show_units:
×
970
            # In theory, two leaf classes from different vendors could have
971
            # introduced same-named properties with different unit definitions.
972
            # We account for that by showing a list of units in that case.
UNCOV
973
            siunits = []
×
UNCOV
974
            for class_obj in class_objs.values():
×
UNCOV
975
                if pname not in class_obj.properties:
×
976
                    # Not all classes may have the property, but one of them
977
                    # will.
UNCOV
978
                    continue
×
UNCOV
979
                prop_obj = class_obj.properties[pname]
×
UNCOV
980
                siunit = siunit_obj(prop_obj)
×
UNCOV
981
                if siunit is not None and siunit not in siunits:
×
982
                    siunits.append(siunit)
×
UNCOV
983
            for siunit in siunits:
×
984
                hdr += f" [{siunit}]"
×
UNCOV
985
        headers.append(hdr)
×
986

UNCOV
987
    rows = _format_instances_as_rows(
×
988
        insts, max_cell_width,
989
        include_classnames=include_classnames,
990
        context=context, prop_names=prop_names,
991
        quote_strings=quote_strings, namespace=namespace)
992

993
    # Fold the headers if necessary. Fold on either hypens or single word
994
    # too long because the headers are all single words
UNCOV
995
    disp_headers = []
×
UNCOV
996
    for header in headers:
×
UNCOV
997
        if len(header) > max_cell_width:
×
UNCOV
998
            disp_headers.append(fold_strings(header,
×
999
                                             max_cell_width,
1000
                                             break_long_words=True,
1001
                                             break_on_hyphens=True))
1002
        else:
UNCOV
1003
            disp_headers.append(header)
×
1004

1005
    # Display string if cmd option deep_inheritance exists and is True
1006

UNCOV
1007
    di = ""
×
UNCOV
1008
    if ctx_options:   # This is just for test support
×
UNCOV
1009
        di = "; deep-inheritance" if ctx_options.get('deep_inheritance') else ""
×
UNCOV
1010
    title = f'Instances: {insts[0].classname}{di}'
×
1011

UNCOV
1012
    click.echo(format_table(rows, disp_headers, title=title,
×
1013
                            table_format=table_format))
1014

1015

1016
def _sorted_prop_names(insts):
2✔
1017
    """
1018
    Return the list of (originally cased) property names that is the superset
1019
    of all properties in the  any of theinput instances.
1020

1021
    The returned list has the key properties first, followed by the non-key
1022
    properties. Each group is sorted case insensitively.
1023

1024
    The key properties are determined from the instance paths, if present.
1025
    The function tolerates it if only some of the instances have a path,
1026
    and if instances of subclasses have additional keys.
1027
    """
1028

UNCOV
1029
    all_props = NocaseDict()  # key: org prop name, value: lower prop name
×
UNCOV
1030
    key_props = NocaseDict()  # key: org prop name, value: lower prop name
×
UNCOV
1031
    for inst in insts:
×
UNCOV
1032
        for pn in inst.properties:
×
UNCOV
1033
            all_props[pn] = pn.lower()
×
UNCOV
1034
            if inst.path and pn in inst.path.keybindings:
×
UNCOV
1035
                key_props[pn] = pn.lower()
×
1036

UNCOV
1037
    nonkey_props = NocaseDict()  # key: org prop name, value: lower prop name
×
UNCOV
1038
    for pn in all_props:
×
UNCOV
1039
        if pn not in key_props:
×
UNCOV
1040
            nonkey_props[pn] = all_props[pn]
×
1041

UNCOV
1042
    key_prop_list = sorted(key_props.keys(), key=lambda pn: key_props[pn])
×
UNCOV
1043
    nonkey_prop_list = sorted(
×
1044
        nonkey_props.keys(), key=lambda pn: nonkey_props[pn])
UNCOV
1045
    key_prop_list.extend(nonkey_prop_list)
×
UNCOV
1046
    return key_prop_list
×
1047

1048

1049
def _propertylist_prop_names(insts, property_list):
2✔
1050
    """
1051
    Return the originally cased property list, based on the lexical case
1052
    in the instances.
1053

1054
    If a property name is not in any instance, it is not returned.
1055
    """
UNCOV
1056
    prop_list = []
×
UNCOV
1057
    for pname in property_list:
×
UNCOV
1058
        for inst in insts:
×
UNCOV
1059
            if pname in inst:
×
UNCOV
1060
                prop_list.append(inst.properties[pname].name)
×
UNCOV
1061
                break
×
UNCOV
1062
    return prop_list
×
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc