• 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

33.1
/pywbemtools/pywbemcli/_context_obj.py
1
# (C) Copyright 2017 IBM Corp.
2
# (C) Copyright 2017 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
pybemcli context object. This is the common object for click command calls for
18
pywbemcli context information.
19

20
It contains data that is set in the top level and used in command calls
21

22
This object is attached to the Click Context in pywbemcli.py
23
"""
24

25

26
import os
2✔
27
import click
2✔
28
import click_spinner
2✔
29

30
from .._output_formatting import format_table, validate_output_format
2✔
31

32

33
class ContextObj:
2✔
34
    # pylint: disable=useless-object-inheritance, too-many-instance-attributes
35
    """
36
        Manage the pywbemcli context that is carried within the Click
37
        context object in the obj parameter. This is the object that
38
        communicates between the cli commands and command groups. It contains
39
        the information that is common to the multiple click commands
40
    """
41

42
    spinner_envvar = 'PYWBEMCLI_SPINNER'
2✔
43

44
    # pylint: disable=unused-argument
45
    def __init__(self, pywbem_server, output_format, timestats, log, verbose,
2✔
46
                 pdb, warn, connections_repo, interactive_mode,
47
                 close_interactive_server):
48
        """
49
        Parameters:
50

51
          pywbem_server :class:`PywbemServer`):
52
            PywbemServer instance to be used for connection
53

54
          output_format (:term:`string or None):
55
            String representing the type of output from the --output-format
56
            general option or None if the default is to be used.
57

58
          timestats (:class:`py:bool`):
59
            See timestats general option
60

61
          log (:term:`string or None):
62
            String defining the characteristics of log output for each
63
            WBEM operation or None if logging is disabled.
64

65
          verbose (:class:`py:bool`):
66
            See verbose general option
67

68
          pdb (:class:`py:bool`):
69
            See pdb general option
70

71
          warn (:class:`py:bool`):
72
            See warn general option
73

74
          connections_repo:
75

76
          interactive_mode (:class:`py:bool`):
77
            If True, pywbemcli is in interactive mode.
78

79
          close_interactive_server (:class:`py:bool`):
80
            Flag that defines interactive command with a server definition
81
            that must be disconnected after the command
82

83
        """
84

UNCOV
85
        self._pywbem_server = pywbem_server
×
UNCOV
86
        self._output_format = output_format
×
UNCOV
87
        self.timestats = timestats   # has setter method
×
UNCOV
88
        self._log = log
×
UNCOV
89
        self._verbose = verbose
×
UNCOV
90
        self._pdb = pdb
×
91
        # warn included only to insure that original cmd line input flag
92
        # maintained through interactive session.
UNCOV
93
        self._warn = warn
×
UNCOV
94
        self._connections_repo = connections_repo
×
UNCOV
95
        self.interactive_mode = interactive_mode
×
UNCOV
96
        self._close_interactive_server = close_interactive_server
×
97

UNCOV
98
        self._spinner_enabled = None  # Deferred init in getter
×
UNCOV
99
        self._spinner_obj = click_spinner.Spinner()
×
100

101
    def __repr__(self):
2✔
UNCOV
102
        return 'ContextObj(at {:08x}, pywbem_server={!r}, outputformat={}, ' \
×
103
               'timestats={}, log={}, verbose={}, spinner_enabled={}, ' \
104
               'interactive_mode={}' \
105
               .format(id(self), self._pywbem_server, self.output_format,
106
                       self.timestats, self.log, self.verbose,
107
                       self.spinner_enabled, self._interactive_mode)
108

109
    @property
2✔
110
    def output_format(self):
2✔
111
        """
112
        :term:`string`: String defining the output format requested.  This may
113
        be None meaning that the default format should be used or may be
114
        one of the values in the TABLE_FORMATS variable.
115
        """
UNCOV
116
        return self._output_format
×
117

118
    @property
2✔
119
    def timestats(self):
2✔
120
        """
121
        :term:`string`: Output format to be used.
122
        """
UNCOV
123
        return self._timestats
×
124

125
    @timestats.setter
2✔
126
    def timestats(self, value):
2✔
127
        """Setter method; for a description see the getter method."""
128
        # pylint: disable=attribute-defined-outside-init
UNCOV
129
        self._timestats = value
×
130

131
    @property
2✔
132
    def interactive_mode(self):
2✔
133
        """
134
        :class:`py:bool`: 'True' if in interactive mode and 'False' if
135
        in the command mode.
136
        """
UNCOV
137
        return self._interactive_mode
×
138

139
    @interactive_mode.setter
2✔
140
    def interactive_mode(self, mode):
2✔
141
        """Setter method; for a description see the getter method."""
UNCOV
142
        assert isinstance(mode, bool)
×
143
        # pylint: disable=attribute-defined-outside-init
UNCOV
144
        self._interactive_mode = mode
×
145

146
    @property
2✔
147
    def log(self):
2✔
148
        """
149
        :term:`string`: log definition from cmd line
150
        """
UNCOV
151
        return self._log
×
152

153
    @property
2✔
154
    def pdb(self):
2✔
155
        """
156
        bool: Indicates whether to break in the debugger.
157
        """
UNCOV
158
        return self._pdb
×
159

160
    @property
2✔
161
    def warn(self):
2✔
162
        """
163
        bool: Indicates whether to enable Python warnings.
164
        """
UNCOV
165
        return self._warn
×
166

167
    @property
2✔
168
    def connections_repo(self):
2✔
169
        """
170
        :class:`ConnectionRepository` instance defining at least the filename
171
        of the Connections repository. The connections_repo may not be
172
        loaded at this point.
173
        """
UNCOV
174
        return self._connections_repo
×
175

176
    def is_connected(self):
2✔
177
        """
178
        bool: Indicates whether currently connected to a WBEM server. Where
179
        connected implies both the existence of the PywbemServer instance
180
        and that the connection to a server has been made.
181

182
        That is the case as soon as commands have been executed that
183
        communicate with the server.
184
        """
UNCOV
185
        return self._pywbem_server and self._pywbem_server.connected
×
186

187
    @property
2✔
188
    def pywbem_server(self):
2✔
189
        """
190
        :class:`PywbemServer`: Current PywbemServer object for this context.
191

192
        Return the PywbemServer object if it already exists.
193

194
        If the pywbem_server attribute does not exist, generate a click
195
        exception. If no server is specified, `None` is returned.
196

197
        This attribute is settable.
198
        """
UNCOV
199
        if self._pywbem_server:
×
UNCOV
200
            return self._pywbem_server
×
201

UNCOV
202
        ctx = click.get_current_context()
×
UNCOV
203
        if ctx.parent:
×
UNCOV
204
            ctx = ctx.parent
×
UNCOV
205
        cmd = "{} {}".format(ctx.info_name or "",
×
206
                             ctx.invoked_subcommand or "")
UNCOV
207
        raise click.ClickException(
×
208
            f'No current server for command "{cmd}" that requires a WBEM '
209
            'server. Specify a server with the "--server", "--mock-server", or '
210
            '"--name" general option, by setting the corresponding environment '
211
            'variables, or in interactive mode '
212
            'use "connection select" to define a target server')
213

214
    @pywbem_server.setter
2✔
215
    def pywbem_server(self, value):
2✔
216
        """Setter method; for a description see the getter method."""
217
        # pylint: disable=attribute-defined-outside-init
218
        self._pywbem_server = value
×
219

220
    @property
2✔
221
    def spinner_enabled(self):
2✔
222
        """
223
        :class:`py:bool`: Indicates and controls whether the spinner is enabled.
224

225
        If the spinner is enabled, subcommands will display a spinning wheel
226
        while waiting for completion.
227

228
        This attribute can be modified.
229

230
        The initial state of the spinner is enabled, but it can be disabled by
231
        setting the {} environment variable to 'false', '0', or the empty
232
        value.
233
        """.format(self.spinner_envvar)
234

235
        # Deferred initialization
UNCOV
236
        if self._spinner_enabled is None:
×
UNCOV
237
            value = os.environ.get(self.spinner_envvar, None)
×
UNCOV
238
            if value is None:
×
239
                # Default if not set
UNCOV
240
                self._spinner_enabled = True
×
241
            elif value == '0' or value == '' or value.lower() == 'false':
×
242
                self._spinner_enabled = False
×
243
            else:
244
                self._spinner_enabled = True
×
245

UNCOV
246
        return self._spinner_enabled
×
247

248
    @spinner_enabled.setter
2✔
249
    def spinner_enabled(self, enabled):
2✔
250
        """Setter method; for a description see the getter method."""
251
        # pylint: disable=attribute-defined-outside-init
252
        self._spinner_enabled = enabled
×
253

254
    def spinner_start(self):
2✔
255
        """
256
        Start the spinner, if the spinner is enabled.
257
        """
UNCOV
258
        if self.spinner_enabled:
×
UNCOV
259
            self._spinner_obj.start()
×
260

261
    def spinner_stop(self):
2✔
262
        """
263
        Stop the spinner, if the spinner is enabled.
264
        """
UNCOV
265
        if self.spinner_enabled:
×
UNCOV
266
            self._spinner_obj.stop()
×
267

268
    @property
2✔
269
    def verbose(self):
2✔
270
        """
271
        :bool:` '~click.verbose.
272
        """
UNCOV
273
        return self._verbose
×
274

275
    def pywbem_server_exists(self):
2✔
276
        """
277
        Return True if a pywbem_server is defined.  This method allows testing
278
        for the existence of a PywbemServer object without causing an exception.
279
        The normal method is to simply access the property. In that case,
280
        an exception will be generated if there is no PywbemServer defined.
281

282
        Returns:
283
            :class": `boolean True if an instance of PywbemServer is defined.
284
        """
UNCOV
285
        return self._pywbem_server is not None
×
286

287
    def execute_cmd(self, cmd):
2✔
288
        """
289
        Call the cmd executor defined by cmd with the spinner. If the
290
        WBEM server object has not been created it is created so that this WBEM
291
        server can be used for interactive commands.
292

293
        This method is called by every command execution to setup and
294
        execute the command. Thus, each command definition MUST have the line
295
        similar to:
296

297
        context.execute_cmd(lambda: cmd_instance_query(context, query, options))
298
        """
299
        # Env.var PYWBEMCLI_DIAGNOSTICS turns on diagnostic prints for developer
300
        # use and is therefore not documented.
UNCOV
301
        if os.getenv('PYWBEMCLI_DIAGNOSTICS'):
×
302
            ctx = click.get_current_context()
×
303
            click.echo('DIAGNOSTICS-CMD: info_name={!r}, subcommand={!r}, '
×
304
                       'command={!r}, params={!r}'.
305
                       format(ctx.info_name, ctx.invoked_subcommand,
306
                              ctx.command,
307
                              ctx.params))
308
            display_click_context_parents(display_attrs=True)
×
309

UNCOV
310
        if not self.pdb:
×
UNCOV
311
            self.spinner_start()
×
UNCOV
312
        try:
×
UNCOV
313
            if self.pdb:
×
314
                import pdb  # pylint: disable=import-outside-toplevel
×
315
                pdb.set_trace()  # pylint: disable=forgotten-debug-statement
×
316

UNCOV
317
            cmd()  # The pywbemcli command function call.
×
318

319
        finally:
UNCOV
320
            if not self.pdb:
×
UNCOV
321
                self.spinner_stop()
×
322

323
            # Issue statistics if requested and if the command used a conn.
UNCOV
324
            if self.timestats and self.is_connected():
×
UNCOV
325
                context = click.get_current_context()
×
UNCOV
326
                click.echo(self.format_statistics(
×
327
                    self.pywbem_server.conn.statistics, context.obj))
328

329
            # Close any existing connection if in command mode or if the
330
            # close_interactive_server flag is set
UNCOV
331
            if not self.interactive_mode or self._close_interactive_server:
×
UNCOV
332
                if self.is_connected():
×
UNCOV
333
                    self.pywbem_server.disconnect()
×
334

335
    def format_statistics(self, statistics, context):
2✔
336
        # pylint: disable=no-self-use
337
        """
338
        Table formatted output of client statistics
339
        """
UNCOV
340
        output_fmt = validate_output_format(context.output_format, 'TABLE')
×
341

UNCOV
342
        snapshot = sorted(statistics.snapshot(),
×
343
                          key=lambda item: item[1].avg_time,
344
                          reverse=True)
345

UNCOV
346
        header = ['Operation', 'Count', 'Errors',
×
347
                  'Client Time\n[ms]',
348
                  'Server Time\n[ms]',
349
                  'Request Size\n[B]',
350
                  'Response Size\n[B]']
351

UNCOV
352
        rows = []
×
UNCOV
353
        for name, stats in snapshot:
×
UNCOV
354
            row = [name, stats.count, stats.exception_count,
×
355
                   f'{stats.avg_time * 1000:.3f}',
356
                   f'{stats.avg_server_time * 1000:.3f}',
357
                   f'{stats.avg_request_len:.0f}',
358
                   f'{stats.avg_reply_len:.0f}']
UNCOV
359
            rows.append(row)
×
360

UNCOV
361
        click.echo(format_table(
×
362
            rows, header, title='Client statistics',
363
            table_format=output_fmt))
364

365
    @staticmethod
2✔
366
    def update_root_click_context(ctx_obj):
2✔
367
        """
368
        Method to update the click root context with a new context
369
        object.  This makes the values in this context universal for all
370
        future commands within an interactive session.
371
        This is a static method and gets the current click context and
372
        root context from click itself.
373
        """
UNCOV
374
        ctx = click.get_current_context()
×
UNCOV
375
        root = ctx.find_root()
×
UNCOV
376
        root.obj = ctx_obj
×
377

378

379
#
380
#   Debug tools to help understanding the click context.  These are only
381
#   to help analyzing the click_context (ctx) and primarily in the
382
#   interactive mode when several levels of click context can exist
383
#
384
def display_click_context(ctx, msg=None, display_attrs=True):
2✔
385
    """
386
    Debug function displays attributes of click context
387
    This is a diagnostic for developer use
388
    """
389

390
    attrs = vars(ctx)
×
391
    if not msg:
×
392
        msg = "CLICK_CONTEXT"
×
393
    if not display_attrs:
×
394
        click.echo(ctx.obj)
×
395
    else:
396
        myattrs = '\n    '.join(f'{i}: {v}' for (i, v) in
×
397
                                sorted(attrs.items()))
398
        click.echo(f'{msg} {ctx}, attrs:\n    {myattrs}')
×
399

400

401
def display_click_context_parents(display_attrs=False):
2✔
402
    """
403
    Display the current click context and its all of its parents.
404
    This is a diagnostic for developer use
405
    """
406
    ctx = click.get_current_context()
×
407
    disp_ctx = ctx
×
408
    # display in context top-down order
409
    stack = []
×
410
    # Create list of ctx parents
411
    while disp_ctx is not None:
×
412
        stack.append(disp_ctx)
×
413
        disp_ctx = disp_ctx.parent
×
414

415
    # pop off of stack to get reverse order
416
    level = 0
×
417
    while stack:
×
418
        disp_ctx = stack.pop()
×
419
        display_click_context(
×
420
            disp_ctx, msg='DIAGNOSTICS-CTX: Context at parent level {}:'.
421
            format(level), display_attrs=display_attrs)
422
        level += 1
×
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