• 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

10.27
/pywbemtools/pywbemcli/_common_cmd_functions.py
1
# (C) Copyright 2022 IBM Corp.
2
# (C) Copyright 2022 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
Functions and classes used by the _cmd_xxxx.py command implementations but that
18
apply across multiple command groups (ex. they are used by both _cmd_class
19
and _cmd_instance).
20
"""
21

22

23
from collections import namedtuple
2✔
24
import click
2✔
25

26
from pywbem._nocasedict import NocaseDict
2✔
27

28
from pywbem import Error, CIMError, ModelError, \
2✔
29
    CIM_ERR_NOT_FOUND, CIM_ERR_INVALID_CLASS, CIM_ERR_INVALID_PARAMETER, \
30
    CIM_ERR_INVALID_NAMESPACE
31

32
from ._common import pywbem_error_exception, get_subclass_names, \
2✔
33
    get_leafclass_names, parse_version_value
34

35
from ._display_cimobjects import display_cim_objects
2✔
36

37
from .._output_formatting import output_format_is_table, \
2✔
38
    format_table, warning_msg, output_format_is_cimobject, \
39
    output_format_is_textgroup
40

41

42
class ResultsHandler:
2✔
43
    """
44
    Class that manages the handling of the namespaces, interim results, and
45
    errors in those request action methods that use multiple namespaces.
46

47
    This class:
48
      - Initializes the structures that gather data from pywbem server requests
49
        when multiple requests are to be executed because the command
50
        defines multiple namespaces for the requests,
51
      - Manages determination of the namespaces to be processed.
52
      - Provides methods (add(...) and handle_exception() to capture the
53
        command results
54
      - Manages continuation of execution of requests if a CIMError is
55
        encountered
56
      - Provides a method (display(...) that calls the display_cimobjects to
57
        display the request results
58
      - Displays any error results in a form compatible with the requested
59
        display format and data format.
60

61
    The pattern for this class is:
62

63
      results = ResultsHandler(...)
64
      for namespace in results:
65
         # execute server request and append results to any previous results
66
         results.add(<request method>)
67
      exception CIMError as ce:
68
        # Capture the CIMError exception and either terminate if not one of the
69
        # ones for which continuing would be logical or return
70
        results.handle_exception(ns, ce)
71
        continue
72
      # terminate because an Error exception is going to apply to all requests
73
      exception Error as er:
74
            raise pywbem_error_exception(er)
75

76
      results.display()
77
    """
78
    def __init__(self, context, options, output_format, obj_type,
2✔
79
                 target_object, instpath=None, property_list=None):
80
        """
81
          context (:class:`py:dict`):
82
            click command context dictionary
83
          options (:class:`py:dict`):
84
            click command options dictionary
85
          output_format (:term:`string`)
86
            Output format string that is passed to display_cimobjects())
87
          obj_type ((:term:`string`)):
88
            String defining type of object "class", "instance", etc.
89
          target_object
90

91
          instpath (:term:`string` or :class:`~pywbem.CIMInstanceName`):
92
            Identification of the request target object to be included in
93
            error display
94

95
          property_list (list of :term:`string, :term:`string` or None)
96
            Resolved property list passed to display_cimobjects
97
        """
98

UNCOV
99
        self.context = context
×
UNCOV
100
        self.obj_type = obj_type
×
UNCOV
101
        self.target_object = target_object
×
UNCOV
102
        self.output_format = output_format
×
UNCOV
103
        self.options = options
×
UNCOV
104
        self.property_list = property_list
×
105

106
        # If namespace returned from get_instancename in path, put it into
107
        # ns_names. path on the instance overrides the use of --namespace and
108
        # therefore return from get_namespaces()
UNCOV
109
        if instpath and instpath.namespace:
×
UNCOV
110
            self.ns_names = [instpath.namespace]
×
111
        else:
UNCOV
112
            self.ns_names = get_namespaces(context, options['namespace'])
×
113

UNCOV
114
        self.results = NocaseDict()
×
115

116
        # Set the requested namespaces into the results dictionary
UNCOV
117
        for ns in self.ns_names:
×
UNCOV
118
            self.results[ns] = None
×
119

UNCOV
120
        self.result_errors = {}
×
UNCOV
121
        self.results_to_date = 0
×
122

123
    def __contains__(self, key):
2✔
124
        return key in self.results
×
125

126
    def keys(self):
2✔
127
        """
128
        Return result dictionary keys
129
        """
130
        return self.results.keys()
×
131

132
    def __iter__(self):
2✔
133
        """
134
        Used to iterate the namespaces for which requests are to be made
135
        """
UNCOV
136
        return iter(self.results)
×
137

138
    def add(self, request_result):
2✔
139
        """
140
        Adds reguest_result to results dictionary
141
        """
UNCOV
142
        self.results[self.ns_names[self.results_to_date]] = request_result
×
UNCOV
143
        self.results_to_date += 1
×
144

145
    def handle_exception(self, ns, exc):
2✔
146
        """
147
        Handle CIM_Error exceptions from multi-namespace requests.  This method
148
        processes an exception from a server request methods and determines if
149
        the processing will continue for other namespaces or terminate with an
150
        exception. If one of the listed CIMError status codes in being
151
        processed, it allows the processing to continue but issues a warning
152
        message.
153

154
        If all namespaces in the ns_names list have been processed and there
155
        are no valid responses in the ns_names list, it raises the error shown
156
        by exc.
157

158
        Parameters:
159
          exc (Exception):
160
            The exception caught and  being processed.
161

162
          target_object (:term:`string` or CIMInstanceName,):
163
            The request target object that caused the exception or None
164
        """
165
        # If not one of the following CIMError status codes, terminate with
166
        # Click exception
UNCOV
167
        if exc.status_code not in (CIM_ERR_NOT_FOUND,
×
168
                                   CIM_ERR_INVALID_CLASS,
169
                                   CIM_ERR_INVALID_NAMESPACE,
170
                                   CIM_ERR_INVALID_PARAMETER):
UNCOV
171
            raise pywbem_error_exception(exc)
×
172

173
        # If all returns were in error, terminate with last exception but show
174
        # the errors as part of the errors display
UNCOV
175
        self.results_to_date += 1
×
UNCOV
176
        self.result_errors[ns] = (exc)
×
UNCOV
177
        if self.results_to_date == len(self.results):
×
UNCOV
178
            if not any(self.results.values()):
×
UNCOV
179
                if self.result_errors:
×
UNCOV
180
                    self.display_errors(terminate=False)
×
UNCOV
181
                raise pywbem_error_exception(exc)
×
182

183
    def display(self):
2✔
184
        """
185
        Execute the displays. This will cause the display of both the
186
        results and result_errors.  Note that this method gets specifiec
187
        options from self.options that are required by display_cimobjects
188
        """
UNCOV
189
        summary = self.options.get('summary', None)
×
UNCOV
190
        ignore_null = not self.options.get('show_null', None)
×
UNCOV
191
        object_order = self.options.get("object_order", None)
×
192

UNCOV
193
        display_cim_objects(self.context, self.results, self.output_format,
×
194
                            summary=summary,
195
                            ignore_null_properties=ignore_null,
196
                            property_list=self.property_list,
197
                            object_order=object_order,
198
                            ctx_options=self.options)
199

UNCOV
200
        if self.result_errors:
×
UNCOV
201
            if any(self.result_errors.values()):
×
UNCOV
202
                self.display_errors(terminate=True)
×
203

204
    def display_errors(self, terminate=False):
2✔
205
        """
206
        Display any errors in an appropriate format consistent with the
207
        output format but to stderr.
208
        """
UNCOV
209
        rows = []
×
210
        # values are (exception, self.obj_type, target_object)
UNCOV
211
        for ns, exc in self.result_errors.items():
×
UNCOV
212
            rows.append([ns, exc.status_code_name, exc.status_description])
×
213

UNCOV
214
        title = f"Request Response Errors for Target: ({self.obj_type}) " \
×
215
                f"{self.target_object}"
216

UNCOV
217
        if output_format_is_table(self.output_format):
×
UNCOV
218
            headers = ['namespace', 'CIMError', "Description"]
×
219

UNCOV
220
            click.echo(format_table(rows, headers,
×
221
                                    title=title,
222
                                    table_format=self.output_format))
223

UNCOV
224
        elif output_format_is_cimobject(self.output_format):
×
UNCOV
225
            click.echo(f"\n{title}")
×
UNCOV
226
            for row in rows:
×
UNCOV
227
                click.echo(
×
228
                    f"namespace:{row[0]} CIMError:{row[1]} "
229
                    f"Description:{row[2]}",
230
                    err=True)
231

232
        elif output_format_is_textgroup(self.output_format):
×
233
            click.echo(f"\n{title}")
×
234
            for row in rows:
×
235
                click.echo(
×
236
                    f"namespace:{row[0]} CIMError:{row[1]} "
237
                    f"Description:{row[2]}",
238
                    err=True)
239

240
        # Close with an exception so exception code is raised.
UNCOV
241
        if terminate:
×
UNCOV
242
            raise click.ClickException(
×
243
                f"Errors encountered on {len(self.result_errors)} server "
244
                "request(s)")
245

246

247
def get_namespaces(context, namespaces, default_all_ns=False):
2✔
248
    """
249
    Returns either the namespaces defined in --namespaces parameter or if
250
    namespaces is empty, either all of the namespaces available in the server or
251
    the value None depending on the value of the parameter default_all_ns.
252

253
    Processes either single namespace string, single string containing
254
    multiple comma-separated,namespace definitions  or combination of string
255
    and tuple.
256

257
    This allows a single processor for the namespace option that returns
258
    namespace or default in a form that matches the type of namespaces
259
    (tuple, list) or (string, string)
260

261
    Parameters:
262

263
      context context (:class:`ContextObj` provided with command)
264

265
      namespaces (tuple of :term:`string` or :term:`string`)
266
        tuple of strings where each string is one or more namespace names.
267
        Any single string can contain one or multiple namespaces by
268
        comma-separating the namespace names.
269

270
      default_all_ns (:class:`py:bool`):
271
        Boolean that determines return value if namespaces is None or []:
272
          True: Return all namespaces in server
273
          False: Return default namespace for current conn (default)
274

275
    Returns:
276
        If namespaces is a tuple, returns list of namespaces with
277
        comma-separated strings separated into single items in the list
278
        If namespaces is string, returns the single namespace string
279
        if namespaces is None, returns None if default_all_ns is False
280
        or all namespaces in environment if default_all_ns is True
281

282
    Raises:
283
        CIMError if status code not CIM_ERR_NOT_FOUND
284
    """
UNCOV
285
    def get_default_ns(default_ns):
×
UNCOV
286
        return default_ns if not isinstance(namespaces, tuple) else [default_ns]
×
287

288
    # Return the provided namespace(s) by expanding each entry that has
289
    # comma-separated values add adding those without comma
UNCOV
290
    ns_names = []
×
UNCOV
291
    if namespaces:
×
UNCOV
292
        if isinstance(namespaces, tuple):
×
UNCOV
293
            for ns in namespaces:
×
UNCOV
294
                ns_names.extend(ns.split(','))
×
UNCOV
295
            return ns_names
×
296
        return namespaces
×
297

298
    # If default input param is None, return the default namespace.
299
    # We set the default namespace here rather than None (where the request
300
    # would get the default namespace) because the find command
301
    # attempts to build a dict with namespace and None fails
UNCOV
302
    conn = context.pywbem_server.conn
×
UNCOV
303
    default_ns = conn.default_namespace
×
UNCOV
304
    assert default_all_ns is not None
×
UNCOV
305
    if default_all_ns is False:
×
UNCOV
306
        return get_default_ns(default_ns)
×
307

308
    # Otherwise get all namespaces from server
UNCOV
309
    wbem_server = context.pywbem_server.wbem_server
×
UNCOV
310
    try:
×
UNCOV
311
        assert isinstance(namespaces, tuple)
×
UNCOV
312
        ns_names = wbem_server.namespaces
×
UNCOV
313
        ns_names.sort()
×
UNCOV
314
        return ns_names
×
315

UNCOV
316
    except ModelError:
×
UNCOV
317
        return get_default_ns(default_ns)
×
318

UNCOV
319
    except CIMError as ce:
×
320
        # Allow processing to continue if no interop namespace
321
        if ce.status_code == CIM_ERR_NOT_FOUND:
×
322
            warning_msg(
×
323
                f'{ce}. Using default_namespace {conn.default_namespace}.')
324
            return get_default_ns(default_ns)
×
325
        raise click.ClickException(
×
326
            f'Failed to find namespaces. Exception: {ce} ')
327

UNCOV
328
    except Error as er:
×
UNCOV
329
        raise pywbem_error_exception(er)
×
330

331

332
def enumerate_classes_filtered(context, namespace, classname, options):
2✔
333
    """
334
    Execute EnumerateClasses or EnumerateClassNames in a single namespace
335
    defined in options['namespace'] and return results.
336

337
    If any of the class qualifier filters are defined in the options parameter,
338
    enumerate the classes, filter the result for those parameters, and return
339
    only class names if --names-only set.
340

341
    This function may be executed by multiple command action functions with
342
    varying options in the options. Each option must be tested to validate
343
    that it exists in the options dictionary
344

345
    Parameters:
346

347
      context:  (instance of :class:`ContextObj`):
348
        Used to retrieve conn parameter
349

350
      classname (:term:`string`):
351
        classname for the enumerate or None if all classes to be enumerated.
352

353
      options: Click options dictionary
354
        Options that form basis for this Enumerate and filter processing.
355

356
    Returns:
357
        List of classes or classnames that satisfy the criteria
358

359
    Raises:
360
        pywbem Error exceptions generated by EnumerateClassNames and
361
        enumerateClasses
362
    """
UNCOV
363
    conn = context.pywbem_server.conn
×
UNCOV
364
    filters = _build_filters_dict(conn, namespace, options)
×
365

UNCOV
366
    names_only = options.get('names_only', False)
×
367

UNCOV
368
    iq = not options.get('no_qualifiers', False)
×
369

370
    # Force IncludeQualifier true if results are to be filtered since
371
    # the filter requires that qualifiers exist.
UNCOV
372
    request_iq = True if filters else iq
×
373

UNCOV
374
    local_only = options.get('local_only', False)
×
UNCOV
375
    deep_inheritance = options.get('deep_inheritance', True)
×
UNCOV
376
    include_classorigin = options.get('include_classorigin', True)
×
377

UNCOV
378
    if names_only and not filters:
×
UNCOV
379
        results = conn.EnumerateClassNames(
×
380
            ClassName=classname,
381
            namespace=namespace,
382
            DeepInheritance=deep_inheritance)
383
    else:
UNCOV
384
        results = conn.EnumerateClasses(
×
385
            ClassName=classname,
386
            namespace=namespace,
387
            LocalOnly=local_only,
388
            DeepInheritance=deep_inheritance,
389
            IncludeQualifiers=request_iq,
390
            IncludeClassOrigin=include_classorigin)
UNCOV
391
        if filters:
×
UNCOV
392
            results = _filter_classes(results, filters,
×
393
                                      names_only, iq)
UNCOV
394
    return results
×
395

396

397
# Namedtupe defining each entry in the filters dictionary values
398
FILTERDEF = namedtuple('FILTERDEF', 'optionvalue qualifiername scopes')
2✔
399

400

401
def _build_filters_dict(conn, ns, options):
2✔
402
    """
403
    Build a dictionary defining the filters to be processed against list
404
    of classes from
405
    the filter definitons in the Click options dictionary. There is an entry
406
    in the dictionary for each filter to be applied to filter a list of
407
    classes.
408

409
    Returns:
410
      Dict of filters where the names are the  filtersthemselves , the types
411
      are the type of test (i.e. qualifier, superclass)
412
      and the value for each is a tuple:
413
      * Name of the qualifier (:term:`string`)
414
      * The value of the qualifier filter option (True or False which
415
        determines whether to display the existence or non-existence of the
416
        qualifier
417
      * A tuple containing Booleans for the value of each of the possible
418
        element scopes (class, property, method, parameter) indicating whether
419
        the qualifier is allowed in that element.
420
    """
UNCOV
421
    filters = {}
×
422

UNCOV
423
    def set_qualifier_option(qname, option_value):
×
UNCOV
424
        qualdecl = conn.GetQualifier(qname, ns)
×
425
        # Note: qualdecl.scopes performs test case-insensitively
UNCOV
426
        if qualdecl.scopes['any']:
×
UNCOV
427
            scopes_map = [True, True, True, True]
×
428
        else:
UNCOV
429
            scopes_map = [False, False, False, False]
×
UNCOV
430
            scopes_map[0] = any([qualdecl.scopes['class'],
×
431
                                 qualdecl.scopes['association'],
432
                                 qualdecl.scopes['indication']])
UNCOV
433
            scopes_map[1] = qualdecl.scopes['property']
×
UNCOV
434
            scopes_map[2] = qualdecl.scopes['method']
×
UNCOV
435
            scopes_map[3] = qualdecl.scopes['parameter']
×
UNCOV
436
        filters['qualifier'] = FILTERDEF(option_value, qname,
×
437
                                         tuple(scopes_map))
438

439
    # Qualifier options
UNCOV
440
    if options['association'] is not None:
×
UNCOV
441
        set_qualifier_option('association', options['association'])
×
UNCOV
442
    if options['indication'] is not None:
×
UNCOV
443
        set_qualifier_option('indication', options['indication'])
×
UNCOV
444
    if options['experimental'] is not None:
×
UNCOV
445
        set_qualifier_option('experimental', options['experimental'])
×
446
    # If set, the entity is deprecated
UNCOV
447
    if options['deprecated'] is not None:
×
UNCOV
448
        set_qualifier_option('deprecated', options['deprecated'])
×
UNCOV
449
    if options['since'] is not None:
×
UNCOV
450
        version_tuple = parse_version_str(options['since'])
×
UNCOV
451
        set_qualifier_option('version', version_tuple)
×
452

UNCOV
453
    if options['schema'] is not None:
×
UNCOV
454
        test_str = f"{options['schema'].lower()}_"
×
UNCOV
455
        filters['schema'] = FILTERDEF(test_str, None, None)
×
456

UNCOV
457
    if options['subclass_of'] is not None:
×
UNCOV
458
        filters['subclass_of'] = FILTERDEF(options['subclass_of'], None, None)
×
UNCOV
459
    if options['leaf_classes'] is not None:
×
UNCOV
460
        filters['leaf_classes'] = FILTERDEF(options['leaf_classes'], None, None)
×
UNCOV
461
    return filters
×
462

463

464
def parse_version_str(version_str):
2✔
465
    """
466
    Parse a string with 3 positive integers seperated by period (CIM version
467
    string) into a 3 integer tuple and return the tuple. Used to parse the
468
    version value of the DMTF Version qualifier.
469

470
    Parameters:
471
        version_str (:term: str):
472
            String defining 3 components of a CIM version
473

474
    Returns:
475
        tuple containing 3 integers
476

477
    Raises:
478
        click.ClickException if the version_str is invalid (not integers,
479
        not seperated by ".", not 3 values)
480
    """
UNCOV
481
    try:
×
UNCOV
482
        version_tuple = [int(x) for x in version_str.split('.')]
×
UNCOV
483
    except ValueError:
×
UNCOV
484
        raise click.ClickException('--since option value invalid. '
×
485
                                   'Must contain 3 integer elements: '
486
                                   f'int.int.int". {version_str} received')
UNCOV
487
    if len(version_tuple) != 3:
×
UNCOV
488
        raise click.ClickException('Version value must contain 3 integer '
×
489
                                   'elements (int.int.int). '
490
                                   f'{version_str} received')
UNCOV
491
    return version_tuple
×
492

493

494
def _filter_classes(classes, filters, names_only, iq):
2✔
495
    """
496
    Filter a list of classes for the qualifiers defined by  the
497
    qualifier_filter parameter where this parameter is a list of tuples.
498
    each tuple contains the qualifier name and a dictionary with qualifier
499
     name as key and tuple containing the option_value(True or False) and
500
    a list of booleans where each boolean represents one of the scope types
501
    ()
502
    whether to display or not display if it exists.
503

504
    This method only works for boolean qualifiers
505

506
    Parameters:
507

508
      classes (list of :class:`~pywbem.CIMClass`):
509
        list of classes to be filtered
510

511
      qualifier_filters (dict):
512
        Dictionary defining the filtering to be performed. It contains an entry
513
        for each qualifier filter that is defined. See _build_qualifier_filters
514
        for a definition of this list.
515

516
      names_only (:class:`py:bool`):
517
        If True, return only the classnames. Otherwise returns the filtered
518
        classes. This is because we must get the classes from the server to
519
        perform the filtering
520

521
      iq (:class:`py:bool`):
522
        If not True, remove any qualifiers from the classes.  This is because
523
        we must get the classes from the server with qualifiers to
524
        perform the filtering.
525
    """
526

UNCOV
527
    def class_has_qualifier(cls, qname, scopes):
×
528
        """
529
        Determine if the qualifier defined by qname exists in the elements
530
        of the class where the elements are defined by the scopes parameter
531
        for this filter.
532

533
        Parameters:
534

535
          cls (:class:`~pywbem.CIMClass`):
536
            The class to be inspected for the qualifier defined by qname
537

538
          qname (:term:`string`):
539
            The qualifier for which we are searching
540

541
          scopes (tuple of booleans):
542
            A tuple containing a boolean value for each of the possible scopes
543
            (class, property, method, parameter)
544

545
        Returns:
546
          True if the qualifier with name quname is found in the elements where
547
          the scope is True. Otherwise, False is returned
548

549
        """
550
        # Test class scope
UNCOV
551
        if scopes[0] and qname in cls.qualifiers:
×
UNCOV
552
            return True
×
553

554
        # if property scope, test properties
UNCOV
555
        if scopes[1]:
×
UNCOV
556
            for prop in cls.properties.values():
×
UNCOV
557
                if qname in prop.qualifiers:
×
UNCOV
558
                    return True
×
559
        # If method scope, test methods and if parameter scope, test parameters
UNCOV
560
        if scopes[2]:
×
UNCOV
561
            for method in cls.methods.values():
×
UNCOV
562
                if qname in method.qualifiers:
×
UNCOV
563
                    return True
×
UNCOV
564
                if scopes[3]:
×
UNCOV
565
                    params = method.parameters
×
UNCOV
566
                    for param in params.values():
×
UNCOV
567
                        if qname in param.qualifiers:
×
UNCOV
568
                            return True
×
UNCOV
569
        return False
×
570

571
    # Test all classes in the input property for the defined filters.
UNCOV
572
    filtered_classes = []
×
UNCOV
573
    subclass_names = []
×
574
    # Build list of subclass names that will be used later as a filter on the
575
    # classes to be returned
UNCOV
576
    if 'subclass_of' in filters:
×
UNCOV
577
        try:
×
UNCOV
578
            subclass_names = get_subclass_names(
×
579
                classes,
580
                classname=filters['subclass_of'].optionvalue,
581
                deep_inheritance=True)
UNCOV
582
        except ValueError:
×
UNCOV
583
            ft = filters['subclass_of'].optionvalue
×
UNCOV
584
            raise click.ClickException(
×
585
                f'Classname {ft} for "subclass-of" not found in returned '
586
                'classes.')
587

588
    # Build a list of leaf class names that will be used later as a filter on
589
    # the classes to be returned.
UNCOV
590
    leafclass_names = None  # avoid pylint issue
×
UNCOV
591
    if 'leaf_classes' in filters:
×
UNCOV
592
        try:
×
UNCOV
593
            if subclass_names:
×
UNCOV
594
                clsx = [cls for cls in classes if cls.classname in
×
595
                        subclass_names]
UNCOV
596
                leafclass_names = get_leafclass_names(clsx)
×
597
            else:
UNCOV
598
                leafclass_names = get_leafclass_names(classes)
×
599

600
        except ValueError:
×
601
            ft = filters['leaf_classes'].optionvalue
×
602
            raise click.ClickException(
×
603
                f'Classname {ft} for "leaf_classes-of" not found in returned '
604
                'classes.')
605

UNCOV
606
    for cls in classes:
×
UNCOV
607
        show_class_list = []
×
UNCOV
608
        for filter_name, filter_ in filters.items():
×
UNCOV
609
            if filter_name == 'qualifier':
×
UNCOV
610
                option_value = filter_.optionvalue
×
UNCOV
611
                if class_has_qualifier(cls, filter_.qualifiername,
×
612
                                       filter_.scopes):
UNCOV
613
                    if filter_.qualifiername == 'version':
×
UNCOV
614
                        if filter_.qualifiername in cls.qualifiers:
×
UNCOV
615
                            cls_version = \
×
616
                                cls.qualifiers[filter_.qualifiername].value
UNCOV
617
                            val = parse_version_value(cls_version,
×
618
                                                      cls.classname)
UNCOV
619
                            option_value = bool(val >= filter_.optionvalue)
×
620

UNCOV
621
                    show_class_list.append(option_value)
×
622
                else:
UNCOV
623
                    show_class_list.append(not option_value)
×
624

UNCOV
625
            elif filter_name == 'schema':
×
UNCOV
626
                show_class_list.append(
×
627
                    cls.classname.lower().startswith(filter_.optionvalue))
UNCOV
628
            elif filter_name == 'subclass_of':
×
UNCOV
629
                show_class_list.append(cls.classname in subclass_names)
×
UNCOV
630
            elif filter_name == 'leaf_classes':
×
UNCOV
631
                show_class_list.append(cls.classname in leafclass_names)
×
632

633
            else:
634
                assert False  # Future for other test_types
×
635

636
        # Show if all options are True for this class
UNCOV
637
        show_this_class = all(show_class_list)
×
638

UNCOV
639
        if show_this_class:
×
640
            # If returning instances, honor the names_only option
UNCOV
641
            if not names_only and not iq:
×
UNCOV
642
                cls.qualifiers = []
×
UNCOV
643
                for p in cls.properties.values():
×
UNCOV
644
                    p.qualifiers = []
×
UNCOV
645
                for m in cls.methods.values():
×
UNCOV
646
                    m.qualifiers = []
×
UNCOV
647
                    for p in m.parameters.values():
×
UNCOV
648
                        p.qualifiers = []
×
UNCOV
649
            filtered_classes.append(cls)
×
650

651
    # If names_only parameter create list of classnames
UNCOV
652
    if names_only:
×
UNCOV
653
        filtered_classes = [cls.classname for cls in filtered_classes]
×
UNCOV
654
    return filtered_classes
×
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