cdxcore 0.1.6__py3-none-any.whl → 0.1.9__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of cdxcore might be problematic. Click here for more details.
- cdxcore/__init__.py +1 -9
- cdxcore/config.py +1188 -521
- cdxcore/crman.py +95 -25
- cdxcore/err.py +371 -0
- cdxcore/pretty.py +468 -0
- cdxcore/pretty.py_bak.py +750 -0
- cdxcore/subdir.py +2225 -1334
- cdxcore/uniquehash.py +515 -363
- cdxcore/util.py +358 -417
- cdxcore/verbose.py +683 -248
- cdxcore/version.py +398 -139
- cdxcore-0.1.9.dist-info/METADATA +27 -0
- cdxcore-0.1.9.dist-info/RECORD +36 -0
- {cdxcore-0.1.6.dist-info → cdxcore-0.1.9.dist-info}/top_level.txt +3 -1
- docs/source/conf.py +123 -0
- docs2/source/conf.py +35 -0
- tests/test_config.py +502 -0
- tests/test_crman.py +54 -0
- tests/test_err.py +86 -0
- tests/test_pretty.py +404 -0
- tests/test_subdir.py +289 -0
- tests/test_uniquehash.py +159 -144
- tests/test_util.py +122 -83
- tests/test_verbose.py +119 -0
- tests/test_version.py +153 -0
- up/git_message.py +2 -2
- cdxcore/logger.py +0 -319
- cdxcore/prettydict.py +0 -388
- cdxcore/prettyobject.py +0 -64
- cdxcore-0.1.6.dist-info/METADATA +0 -1418
- cdxcore-0.1.6.dist-info/RECORD +0 -30
- conda/conda_exists.py +0 -10
- conda/conda_modify_yaml.py +0 -42
- tests/_cdxbasics.py +0 -1086
- {cdxcore-0.1.6.dist-info → cdxcore-0.1.9.dist-info}/WHEEL +0 -0
- {cdxcore-0.1.6.dist-info → cdxcore-0.1.9.dist-info}/licenses/LICENSE +0 -0
- {cdxcore → tmp}/deferred.py +0 -0
- {cdxcore → tmp}/dynaplot.py +0 -0
- {cdxcore → tmp}/filelock.py +0 -0
- {cdxcore → tmp}/jcpool.py +0 -0
- {cdxcore → tmp}/np.py +0 -0
- {cdxcore → tmp}/npio.py +0 -0
- {cdxcore → tmp}/sharedarray.py +0 -0
cdxcore/verbose.py
CHANGED
|
@@ -1,34 +1,369 @@
|
|
|
1
|
-
"""
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
1
|
+
r"""
|
|
2
|
+
Overview
|
|
3
|
+
--------
|
|
4
|
+
|
|
5
|
+
This module contains the :class:`cdxcore.verbose.Context` manager class
|
|
6
|
+
which supports printing hierarchical verbose progress reports.
|
|
7
|
+
The key point of this class is to implement an easy-to-use method to print indented progress which
|
|
8
|
+
can also be turned off easily without
|
|
9
|
+
untidy code constructs such as excessive ``if`` blocks. In this case, we also avoid formatting
|
|
10
|
+
any strings.
|
|
11
|
+
|
|
12
|
+
Here is an example::
|
|
13
|
+
|
|
14
|
+
from cdxcore.verbose import Context
|
|
15
|
+
|
|
16
|
+
def f_sub( num=3, context = Context.quiet ):
|
|
17
|
+
context.write("Entering loop")
|
|
18
|
+
for i in range(num):
|
|
19
|
+
context.report(1, "Number %ld", i)
|
|
20
|
+
|
|
21
|
+
def f_main( context = Context.quiet ):
|
|
22
|
+
context.write( "First step" )
|
|
23
|
+
# ... do something
|
|
24
|
+
context.report( 1, "Intermediate step 1" )
|
|
25
|
+
context.report( 1, "Intermediate step 2\\n with newlines" )
|
|
26
|
+
# ... do something
|
|
27
|
+
f_sub( context=context(2) ) # call function f_sub with a sub-context
|
|
28
|
+
# ... do something
|
|
29
|
+
context.write( "Final step" )
|
|
30
|
+
|
|
31
|
+
print("Verbose=1")
|
|
32
|
+
context = Context(1)
|
|
33
|
+
f_main(context)
|
|
34
|
+
|
|
35
|
+
print("\\nVerbose=2")
|
|
36
|
+
context = Context(2)
|
|
37
|
+
f_main(context)
|
|
38
|
+
|
|
39
|
+
print("\\nVerbose='all'")
|
|
40
|
+
context = Context('all')
|
|
41
|
+
f_main(context)
|
|
42
|
+
|
|
43
|
+
print("\\nVerbose='quiet'")
|
|
44
|
+
context = Context('quiet')
|
|
45
|
+
f_main(context)
|
|
46
|
+
|
|
47
|
+
print("\\ndone")
|
|
48
|
+
|
|
49
|
+
Returns::
|
|
50
|
+
|
|
51
|
+
Verbose=1
|
|
52
|
+
00: First step
|
|
53
|
+
01: Intermediate step 1
|
|
54
|
+
01: Intermediate step 2
|
|
55
|
+
01: with newlines
|
|
56
|
+
00: Final step
|
|
57
|
+
|
|
58
|
+
Verbose=2
|
|
59
|
+
00: First step
|
|
60
|
+
01: Intermediate step 1
|
|
61
|
+
01: Intermediate step 2
|
|
62
|
+
01: with newlines
|
|
63
|
+
02: Entering loop
|
|
64
|
+
00: Final step
|
|
65
|
+
|
|
66
|
+
Verbose='all'
|
|
67
|
+
00: First step
|
|
68
|
+
01: Intermediate step 1
|
|
69
|
+
01: Intermediate step 2
|
|
70
|
+
01: with newlines
|
|
71
|
+
02: Entering loop
|
|
72
|
+
03: Number 0
|
|
73
|
+
03: Number 1
|
|
74
|
+
03: Number 2
|
|
75
|
+
00: Final step
|
|
76
|
+
|
|
77
|
+
Verbose='quiet'
|
|
78
|
+
|
|
79
|
+
done
|
|
80
|
+
|
|
81
|
+
Workflow
|
|
82
|
+
^^^^^^^^
|
|
83
|
+
|
|
84
|
+
The basic idea is that the root context has level 0, with increasing levels for sub-contexts.
|
|
85
|
+
When printing information, we can limit printing up to a given level and
|
|
86
|
+
automatically indent the output to reflect the current level of detail.
|
|
87
|
+
|
|
88
|
+
Workflow:
|
|
89
|
+
|
|
90
|
+
* Create a :class:`cdxcore.verbose.Context` model, and define its verbosity in its constructor, e.g.
|
|
91
|
+
by specifying ``"all"``, ``"quiet"``, or a number.
|
|
92
|
+
* To write a text at current level to ``stdout`` use :meth:`cdxcore.verbose.Context.write`.
|
|
93
|
+
* To write a text at an indented sub-level use :meth:`cdxcore.verbose.Context.report`.
|
|
94
|
+
* To create a sub-context (indentation), use :meth:`cdxcore.verbose.Context.__call__`.
|
|
95
|
+
|
|
96
|
+
Lazy Formatting
|
|
97
|
+
^^^^^^^^^^^^^^^
|
|
98
|
+
|
|
99
|
+
:class:`cdxcore.verbose.Context` message formattting is meant to be lazy and only executed
|
|
100
|
+
if a message is actually written. This means that if the ``Contex`` is ``"quiet"`` no string
|
|
101
|
+
formatting takes place.
|
|
102
|
+
|
|
103
|
+
Consider a naive example::
|
|
104
|
+
|
|
105
|
+
from cdxcore.verbose import Context
|
|
106
|
+
import numpy as np
|
|
107
|
+
|
|
108
|
+
def f( data : np.ndarray, verbose : Context = Context.quiet ):
|
|
109
|
+
verbose.write(f"'f' called; data has mean {np.mean(data)} and variance {np.var(data)}")
|
|
110
|
+
# ...
|
|
111
|
+
f( verbose = Context.quiet )
|
|
112
|
+
|
|
113
|
+
In this case ``f`` will compute ``np.mean(data)`` and ``np.var(data)`` even though the use of the ``quiet``
|
|
114
|
+
``Context`` means that the formatted
|
|
115
|
+
string will not be printed.
|
|
116
|
+
|
|
117
|
+
To alleviate this, :meth:`cdxcore.verbose.Context.write` supports a number of alternatives
|
|
118
|
+
which are leveraging :func:`cdxcore.err.fmt`. In above example, the most efficient use case
|
|
119
|
+
is the use of a ``lambda`` function::
|
|
120
|
+
|
|
121
|
+
def f( data : np.ndarray, verbose : Context ):
|
|
122
|
+
verbose.write(lambda : f"'f' called; data has mean {np.mean(data)} and variance {np.var(data)}")
|
|
123
|
+
|
|
124
|
+
The ``lambda`` function is only called when the message is about to be printed.
|
|
125
|
+
|
|
126
|
+
Providing Updates
|
|
127
|
+
^^^^^^^^^^^^^^^^^
|
|
128
|
+
|
|
129
|
+
In many applications we wish to provide progress updates in a single line, and not clutter the output.
|
|
130
|
+
In the example from the beginng, the long lists of output are not informative.
|
|
131
|
+
|
|
132
|
+
:class:`cdxcore.verbose.Context` supports the use of "\\r" and "\\n for simple output formatting.
|
|
133
|
+
Under the hood it uses :class:`cdxcore.crman.CRMan`.
|
|
134
|
+
|
|
135
|
+
Consider the following change to ``f_sub`` in above code example:
|
|
136
|
+
|
|
137
|
+
.. code-block:: python
|
|
138
|
+
:emphasize-lines: 4,5
|
|
139
|
+
|
|
140
|
+
def f_sub( num=3, context = Context.quiet ):
|
|
141
|
+
context.write("Entering loop")
|
|
142
|
+
for i in range(num):
|
|
143
|
+
context.report(1, ":emphasis:`\\r`Number %ld", i, end='') # Notice use of \\r and end=''
|
|
144
|
+
context.write("\\rLoop done") # Notice use of \\r `
|
|
145
|
+
|
|
146
|
+
context = Context('all')
|
|
147
|
+
f_main(context)
|
|
148
|
+
|
|
149
|
+
During execution this prints, for example at step ``i==1``::
|
|
150
|
+
|
|
151
|
+
00: First step
|
|
152
|
+
01: Intermediate step 1
|
|
153
|
+
01: Intermediate step 2
|
|
154
|
+
01: with newlines
|
|
155
|
+
02: Entering loop
|
|
156
|
+
03: Number 1
|
|
157
|
+
|
|
158
|
+
But once the loop finished the update per ``i`` is overwitten::
|
|
159
|
+
|
|
160
|
+
00: First step
|
|
161
|
+
01: Intermediate step 1
|
|
162
|
+
01: with newlines
|
|
163
|
+
02: Entering loop
|
|
164
|
+
02: Loop done
|
|
165
|
+
00: Final step
|
|
166
|
+
|
|
167
|
+
Composing Line Output and Timing
|
|
168
|
+
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
169
|
+
|
|
170
|
+
For lengthy operations it is often considerate to provide the user with an update on
|
|
171
|
+
how long an operation takes. :class:`cdxcore.verbose.Context` provides some simple tooling::
|
|
172
|
+
|
|
173
|
+
from cdxcore.verbose import Context
|
|
174
|
+
import time as time
|
|
175
|
+
|
|
176
|
+
def takes_long( n : int, verbose : context = Context.quiet ):
|
|
177
|
+
with verbose.write_t("About to start... ", end='') as tme:
|
|
178
|
+
for t in range(n):
|
|
179
|
+
verbose.write(lambda : f"\\rTakes long {int(100.*(t+1)/n)}%... ", end='')
|
|
180
|
+
time.sleep(0.22)
|
|
181
|
+
verbose.write(lambda : f"done; this took {tme}.", head=False)
|
|
182
|
+
|
|
183
|
+
takes_long(5, Context.all)
|
|
184
|
+
|
|
185
|
+
During execution prints
|
|
186
|
+
|
|
187
|
+
.. code-block:: python
|
|
188
|
+
|
|
189
|
+
00: Takes long 80%...
|
|
190
|
+
|
|
191
|
+
The example finishes with
|
|
192
|
+
|
|
193
|
+
.. code-block:: python
|
|
194
|
+
|
|
195
|
+
00: Takes long 100%... done; this took 1.1s.
|
|
196
|
+
|
|
197
|
+
Import
|
|
198
|
+
------
|
|
199
|
+
.. code-block:: python
|
|
200
|
+
|
|
201
|
+
from cdxcore.verbose import Context
|
|
202
|
+
|
|
203
|
+
Documentation
|
|
204
|
+
-------------
|
|
5
205
|
"""
|
|
6
206
|
|
|
7
207
|
from .util import fmt, Timer
|
|
8
|
-
from .
|
|
208
|
+
from .err import verify
|
|
9
209
|
from .crman import CRMan, Callable
|
|
10
210
|
|
|
11
211
|
class Context(object):
|
|
12
|
-
"""
|
|
13
|
-
Class for printing indented messages, filtered by overall level of
|
|
212
|
+
r"""
|
|
213
|
+
Class for printing indented messages, filtered by overall level of visibility.
|
|
14
214
|
|
|
15
|
-
|
|
215
|
+
* Construction with keywords::
|
|
216
|
+
|
|
217
|
+
Context( "all" )` or
|
|
218
|
+
Context( "quiet" )
|
|
16
219
|
|
|
17
|
-
|
|
220
|
+
* Display everything::
|
|
221
|
+
|
|
222
|
+
Context( None )
|
|
18
223
|
|
|
19
|
-
|
|
224
|
+
* Display only up to level 2 (top level is 0) e.g.::
|
|
20
225
|
|
|
21
|
-
|
|
226
|
+
Context( 2 )
|
|
227
|
+
|
|
228
|
+
* Copy constructor::
|
|
229
|
+
|
|
230
|
+
Context( context )
|
|
231
|
+
|
|
232
|
+
**Example:**
|
|
22
233
|
|
|
23
|
-
|
|
24
|
-
|
|
234
|
+
.. code-block:: python
|
|
235
|
+
|
|
236
|
+
from cdxcore.verbose import Context
|
|
237
|
+
|
|
238
|
+
def f_2( verbose : Context = Context.quiet ):
|
|
239
|
+
verbose.write( "Running 'f_2'")
|
|
240
|
+
for i in range(5):
|
|
241
|
+
verbose.report(1, "Sub-task {i}", i=i)
|
|
242
|
+
# do something
|
|
243
|
+
|
|
244
|
+
def f_1( verbose : Context = Context.quiet ):
|
|
245
|
+
verbose.write( "Running 'f_1'")
|
|
246
|
+
f_2( verbose(1) )
|
|
247
|
+
# do something
|
|
248
|
+
|
|
249
|
+
verbose = Context("all")
|
|
250
|
+
verbose.write("Starting:")
|
|
251
|
+
f_1(verbose(1))
|
|
252
|
+
verbose.write("Done.")
|
|
25
253
|
|
|
254
|
+
prints
|
|
255
|
+
|
|
256
|
+
.. code-block:: python
|
|
257
|
+
|
|
258
|
+
00: Starting:
|
|
259
|
+
01: Running 'f_1'
|
|
260
|
+
02: Running 'f_2'
|
|
261
|
+
03: Sub-task 0
|
|
262
|
+
03: Sub-task 1
|
|
263
|
+
03: Sub-task 2
|
|
264
|
+
03: Sub-task 3
|
|
265
|
+
03: Sub-task 4
|
|
266
|
+
00: Done.
|
|
267
|
+
|
|
268
|
+
If we set visibility to 2
|
|
269
|
+
|
|
270
|
+
.. code-block:: python
|
|
271
|
+
|
|
272
|
+
verbose = Context(2)
|
|
273
|
+
verbose.write("Starting:")
|
|
274
|
+
f_1(verbose(1)) # <-- make it a level higher
|
|
275
|
+
verbose.write("Done.")
|
|
276
|
+
|
|
277
|
+
we get the reduced
|
|
278
|
+
|
|
279
|
+
.. code-block:: python
|
|
280
|
+
|
|
281
|
+
00: Starting:
|
|
282
|
+
01: Running 'f_1'
|
|
283
|
+
02: Running 'f_2'
|
|
284
|
+
00: Done.
|
|
285
|
+
|
|
286
|
+
**Lazy Formatting**
|
|
287
|
+
|
|
288
|
+
The :meth:`cdxcore.verbose.Context.write` and :meth:`cdxcore.verbose.Context.report` functions provide
|
|
289
|
+
string formatting capabilities. If used, then a message will only be formatted if the current level grants
|
|
290
|
+
it visibility. This avoids unnecessary string operations when no output is required.
|
|
291
|
+
|
|
292
|
+
In the second example above, the format string ``verbose.report(1, "Sub-task {i}", i=i)`` in ``f_2``
|
|
293
|
+
will not be evaluated as that reporting level is turned off.
|
|
294
|
+
|
|
295
|
+
Parameters
|
|
296
|
+
----------
|
|
297
|
+
init : str | int | :class:`cdxcore.verbose.Context`
|
|
298
|
+
|
|
299
|
+
* If a string is provided: must be ``"all"`` or ``"quiet"``.
|
|
300
|
+
|
|
301
|
+
* If an integer is privided it represents the visibility level up to which to print.
|
|
302
|
+
Set to 0 to print only top level messages.
|
|
303
|
+
Any negative number will turn off any messages and is equivalent to ``"quiet"``.
|
|
304
|
+
|
|
305
|
+
* If set to ``None`` display everything.
|
|
306
|
+
|
|
307
|
+
* A ``Context`` is copied.
|
|
308
|
+
|
|
309
|
+
indent : int, optional
|
|
310
|
+
How much to indent strings per level. Default 2.
|
|
311
|
+
|
|
312
|
+
fmt_level : str, optional
|
|
313
|
+
A format string containing ``%d`` for the current indentation.
|
|
314
|
+
Default is ``"%02ld: "``.
|
|
315
|
+
|
|
316
|
+
level : int, optional
|
|
317
|
+
Current level. If ``init`` is another context, and ``level`` is specified,
|
|
318
|
+
it overwrites the ``level`` from the other context.
|
|
319
|
+
|
|
320
|
+
If ``level`` is ``None``:
|
|
321
|
+
|
|
322
|
+
* If ``init`` is another ``Context`` object, use that object's level.
|
|
323
|
+
|
|
324
|
+
* If ``init`` is an integer or one of the keywords above, use the default, 0.
|
|
325
|
+
|
|
326
|
+
channel : Callable, optional
|
|
327
|
+
*Advanced parameter.*
|
|
328
|
+
|
|
329
|
+
A callable which is called to print text. The call signature is::
|
|
330
|
+
|
|
331
|
+
channel( msg : str, flush : bool )`
|
|
332
|
+
|
|
333
|
+
which is meant to mirror
|
|
334
|
+
``print( msg, end='', flush )`` for the provided ``channel``.
|
|
335
|
+
In particular do not terminate ``msg`` automatically with a new line.
|
|
336
|
+
|
|
337
|
+
Illustration::
|
|
338
|
+
|
|
339
|
+
class Collector:
|
|
340
|
+
def __init__(self):
|
|
341
|
+
self.messages = []
|
|
342
|
+
def __call__(self, msg, flush ):
|
|
343
|
+
self.messages.append( msg )
|
|
344
|
+
|
|
345
|
+
collect = Collector()
|
|
346
|
+
verbose = Context( channel = collect )
|
|
347
|
+
|
|
348
|
+
verbose.write("Write at 0")
|
|
349
|
+
verbose.report(1,"Report at 1")
|
|
350
|
+
|
|
351
|
+
print(collect.messages)
|
|
352
|
+
|
|
353
|
+
prints
|
|
354
|
+
|
|
355
|
+
.. code-block:: python
|
|
356
|
+
|
|
357
|
+
['00: Write at 0\\n', '01: Report at 1\\n']
|
|
26
358
|
"""
|
|
27
359
|
|
|
28
360
|
QUIET = "quiet"
|
|
361
|
+
""" Constant for the keyword ``"quiet"`` """
|
|
362
|
+
|
|
29
363
|
ALL = "all"
|
|
364
|
+
""" Constant for the keyword ``"all"`` """
|
|
30
365
|
|
|
31
|
-
def __init__(self,
|
|
366
|
+
def __init__(self, init : str|int|type = None, *,
|
|
32
367
|
indent : int = 2,
|
|
33
368
|
fmt_level : str = "%02ld: ",
|
|
34
369
|
level : int = None,
|
|
@@ -36,78 +371,34 @@ class Context(object):
|
|
|
36
371
|
):
|
|
37
372
|
"""
|
|
38
373
|
Create a Context object.
|
|
39
|
-
|
|
40
|
-
The following three calling styles are supported
|
|
41
|
-
|
|
42
|
-
Construct with keywords
|
|
43
|
-
Context( "all" )
|
|
44
|
-
Context( "quiet" )
|
|
45
|
-
|
|
46
|
-
Display everything
|
|
47
|
-
Context( None )
|
|
48
|
-
|
|
49
|
-
Display only up to level 2 (the root context is level 0)
|
|
50
|
-
Context( 2 )
|
|
51
|
-
|
|
52
|
-
Copy constructor
|
|
53
|
-
Context( context )
|
|
54
|
-
In this case all other parameters are ignored.
|
|
55
|
-
|
|
56
|
-
Parameters
|
|
57
|
-
----------
|
|
58
|
-
verbose_or_init : str, int, or Context
|
|
59
|
-
if a string: one of 'all' or 'quiet'
|
|
60
|
-
if an integer: the level at which to print. Any negative number will not print anything because the default level is 0.
|
|
61
|
-
if None: equivalent to displaying everything ("all")
|
|
62
|
-
if a Context: copy constructor.
|
|
63
|
-
indent : int
|
|
64
|
-
How much to indent prints per level
|
|
65
|
-
fmt_level :
|
|
66
|
-
How to format output given level*indentm using %ld for the current level.
|
|
67
|
-
|
|
68
|
-
Advanced parameters
|
|
69
|
-
-------------------
|
|
70
|
-
level :
|
|
71
|
-
Initial level. This can also be set if verbose_or_init is another context.
|
|
72
|
-
If 'level' is None:
|
|
73
|
-
If 'verbose_or_init' is another Context object, use that object's level
|
|
74
|
-
If 'verbose_or_init' is an integer or one of the keywords above, use 0
|
|
75
|
-
channel :
|
|
76
|
-
A callable which is called to print text.
|
|
77
|
-
It will be called channel( msg : str, flush : bool ) which should mirror print( msg, end='', flush ).
|
|
78
|
-
In particular do not terminate with a new line.
|
|
79
|
-
This can also be set if verbose_or_init is another context, i.e.
|
|
80
|
-
verbose = Context()
|
|
81
|
-
...
|
|
82
|
-
cverbose = Context( verbose, channel=lambda msg, flush : pass )
|
|
83
|
-
will return a silenced verbose.
|
|
84
|
-
|
|
85
374
|
"""
|
|
86
|
-
if not level is None:
|
|
87
|
-
if isinstance(
|
|
375
|
+
if not level is None: verify( level>=0, "'level' must not be negative; found {level}", level=level, exception=ValueError)
|
|
376
|
+
if isinstance( init, Context ) or type(init).__name__ == "Context":
|
|
88
377
|
# copy constructor
|
|
89
|
-
self.
|
|
90
|
-
self.level =
|
|
91
|
-
self.indent =
|
|
92
|
-
self.fmt_level =
|
|
378
|
+
self.visibility = init.visibility
|
|
379
|
+
self.level = init.level if level is None else level
|
|
380
|
+
self.indent = init.indent
|
|
381
|
+
self.fmt_level = init.fmt_level
|
|
93
382
|
self.crman = CRMan()
|
|
94
|
-
self.channel =
|
|
383
|
+
self.channel = init.channel if channel is None else channel
|
|
95
384
|
return
|
|
96
385
|
|
|
97
|
-
if isinstance(
|
|
386
|
+
if isinstance( init, str ):
|
|
98
387
|
# construct with key word
|
|
99
|
-
if
|
|
100
|
-
|
|
388
|
+
if init == self.QUIET:
|
|
389
|
+
init = -1
|
|
101
390
|
else:
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
391
|
+
verify( init == self.ALL,
|
|
392
|
+
lambda : f"'init': if provided as a string, has to be '{self.QUIET}' or"+\
|
|
393
|
+
f"'{self.ALL}'. Found '{init}'", exception=ValueError)
|
|
394
|
+
init = None
|
|
395
|
+
elif not init is None:
|
|
396
|
+
init = int(init)
|
|
106
397
|
|
|
107
398
|
indent = int(indent)
|
|
108
|
-
|
|
399
|
+
verify( indent >=0, "'indent' cannot be negative. Found {indent}", indent=indent, exception=ValueError)
|
|
109
400
|
|
|
110
|
-
self.
|
|
401
|
+
self.visibility = init # print up to this level
|
|
111
402
|
self.level = 0 if level is None else level
|
|
112
403
|
self.indent = indent # indentation level
|
|
113
404
|
self.fmt_level = str(fmt_level) # output format
|
|
@@ -115,241 +406,357 @@ class Context(object):
|
|
|
115
406
|
self.channel = channel
|
|
116
407
|
|
|
117
408
|
def write( self, message : str, *args, end : str = "\n", head : bool = True, **kwargs ):
|
|
118
|
-
"""
|
|
119
|
-
Report message at
|
|
120
|
-
|
|
121
|
-
|
|
409
|
+
r"""
|
|
410
|
+
Report message at current level.
|
|
411
|
+
|
|
412
|
+
The message will be formatted using :func:`cdxcore.err.fmt`
|
|
413
|
+
if the current level is visible. If the current level is not visible no message formatting
|
|
414
|
+
will take place.
|
|
122
415
|
|
|
123
|
-
The parameter
|
|
124
|
-
|
|
125
|
-
|
|
416
|
+
The parameter ``end`` matches ``end`` in :func:`print`
|
|
417
|
+
e.g. ``end=''``
|
|
418
|
+
avoids a newline at the end of the message.
|
|
419
|
+
|
|
420
|
+
* If ``head`` is ``True``, then the first line of the text will be preceeded by proper indentation.
|
|
421
|
+
|
|
422
|
+
* If ``head`` is ``False``, the first line will be printed without preamble.
|
|
126
423
|
|
|
127
|
-
This means the following is a valid pattern
|
|
424
|
+
This means the following is a valid pattern::
|
|
128
425
|
|
|
426
|
+
from cdxcore.verbose import Context
|
|
129
427
|
verbose = Context()
|
|
130
428
|
verbose.write("Doing something... ", end='')
|
|
131
|
-
# do something
|
|
429
|
+
# ... do something
|
|
132
430
|
verbose.write("done.", head=False)
|
|
133
431
|
|
|
134
|
-
which
|
|
432
|
+
which prints
|
|
433
|
+
|
|
434
|
+
.. code-block:: python
|
|
135
435
|
|
|
136
436
|
00: Doing something... done.
|
|
437
|
+
|
|
438
|
+
Another use case is updates per line, for example::
|
|
439
|
+
|
|
440
|
+
from cdxcore.verbose import Context
|
|
441
|
+
verbose = Context()
|
|
442
|
+
N = 1000
|
|
443
|
+
for i in range(N):
|
|
444
|
+
verbose.write(f"\\rDoing something {int(float(i+1)/float(N)*100)}%... ", end='')
|
|
445
|
+
# do something
|
|
446
|
+
verbose.write("done.", head=False)
|
|
447
|
+
|
|
448
|
+
which will provide progress information in a given line.
|
|
449
|
+
|
|
450
|
+
*Implementation notice*: the use of ``\r`` is managed using :class:`cdxcore.crman.CRMan`.
|
|
451
|
+
|
|
452
|
+
Parameters
|
|
453
|
+
----------
|
|
454
|
+
message : str|Callable
|
|
455
|
+
|
|
456
|
+
Text containing format characters.
|
|
457
|
+
|
|
458
|
+
The following alternatives are suppoted:
|
|
459
|
+
|
|
460
|
+
* Python 3 ```{parameter:d}```, in which case ``message.fmt(kwargs)`` for :meth:`str.format` is used
|
|
461
|
+
to obtain the output message.
|
|
462
|
+
|
|
463
|
+
* Python 2 ```%(parameter)d``` in which case ``message % kwargs`` is used to obtain the output message.
|
|
464
|
+
|
|
465
|
+
* Classic C-stype ```%d, %s, %f``` in which case ``message % args`` is used to obtain the output message.
|
|
466
|
+
|
|
467
|
+
* If ``message`` is a ``Callable`` such as a ``lambda`` function, then ``message( *args, **kwargs )``
|
|
468
|
+
is called to obtain the output message.
|
|
469
|
+
|
|
470
|
+
Note that a common use case is using an f-string wrapped in a ``lambda`` function. In this case
|
|
471
|
+
you do not need ``args`` or ``kwargs``::
|
|
472
|
+
|
|
473
|
+
x = 1
|
|
474
|
+
verbose.write(lambda : f"Delayed f-string formatting {x}")
|
|
475
|
+
|
|
476
|
+
end : str, optional
|
|
477
|
+
Terminating string akin to ``end`` in :func:`print`.
|
|
478
|
+
Use ``''`` to not print a newline. See example above for a use case.
|
|
479
|
+
|
|
480
|
+
head : bool, optional;
|
|
481
|
+
Whether this message needs a header (i.e. the ``01`` and spacing).
|
|
482
|
+
Typically ``False`` if the previous call to ``write()`` used `end=''`. See examples above.
|
|
137
483
|
|
|
484
|
+
*args, **kwargs:
|
|
485
|
+
See above
|
|
138
486
|
"""
|
|
139
487
|
self.report( 0, message, *args, end=end, head=head, **kwargs )
|
|
140
488
|
|
|
141
|
-
def write_t( self, message : str, *args, end : str = "\n", head : bool = True, **kwargs ) -> Timer:
|
|
489
|
+
def write_t( self, message : str|Callable, *args, end : str = "\n", head : bool = True, **kwargs ) -> Timer:
|
|
142
490
|
"""
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
The message will be formatted as util.fmt( message, *args, **kwargs )
|
|
147
|
-
It will be displayed in all cases except if the context is 'quiet'.
|
|
148
|
-
|
|
149
|
-
The parameter 'end' matches 'end' in print, e.g. end='' avoids a newline at the end of the message.
|
|
150
|
-
If 'head' is True, then the first line of the text will be preceeded by proper indentation.
|
|
151
|
-
If 'head' is False, the first line will be printed without preamble.
|
|
152
|
-
|
|
153
|
-
This function returns a util.Timer() object which can be used to measure
|
|
154
|
-
time taken for a given task:
|
|
491
|
+
Reports ``message`` subject to string formatting at current level if visible and returns a
|
|
492
|
+
:class:`cdxcore.util.Timer` object
|
|
493
|
+
which can be used to measure time elapsed since ``write_t()`` was called::
|
|
155
494
|
|
|
495
|
+
from cdxcore.verbose import Context
|
|
156
496
|
verbose = Context()
|
|
157
|
-
with verbose.write_t("Doing something... ", end='') as
|
|
497
|
+
with verbose.write_t("Doing something... ", end='') as tme:
|
|
158
498
|
# do something
|
|
159
|
-
verbose.write("done; this took {
|
|
499
|
+
verbose.write("done; this took {tme}.", head=False)
|
|
500
|
+
|
|
501
|
+
produces
|
|
502
|
+
|
|
503
|
+
.. code-block:: python
|
|
504
|
+
|
|
505
|
+
00: Doing something... done; this took 1s.
|
|
506
|
+
|
|
507
|
+
Equivalent to using :meth:`cdxcore.verbose.Context.write` first followed by
|
|
508
|
+
:meth:`cdxcore.verbose.Context.timer`.
|
|
160
509
|
"""
|
|
161
510
|
self.report( 0, message, *args, end=end, head=head, **kwargs )
|
|
162
|
-
return
|
|
511
|
+
return self.timer()
|
|
163
512
|
|
|
164
|
-
def report( self, level : int, message : str, *args, end : str = "\n", head : bool = True, **kwargs ):
|
|
165
|
-
"""
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
513
|
+
def report( self, level : int, message : str|Callable, *args, end : str = "\n", head : bool = True, **kwargs ):
|
|
514
|
+
r"""
|
|
515
|
+
Report message at current level plus ``level``.
|
|
516
|
+
|
|
517
|
+
The message will be formatted using :func:`cdxcore.err.fmt` is the current level
|
|
518
|
+
plus `level` is visible.
|
|
169
519
|
|
|
170
|
-
The
|
|
520
|
+
The parameter ``end`` matches ``end`` in :func:`print`
|
|
521
|
+
e.g. ``end=''``
|
|
522
|
+
avoids a newline at the end of the message.
|
|
523
|
+
|
|
524
|
+
* If ``head`` is ``True``, then the first line of the text will be preceeded by proper indentation.
|
|
525
|
+
|
|
526
|
+
* If ``head`` is ``False``, the first line will be printed without preamble.
|
|
171
527
|
|
|
528
|
+
This means the following is a valid pattern::
|
|
529
|
+
|
|
530
|
+
from cdxcore.verbose import Context
|
|
172
531
|
verbose = Context()
|
|
532
|
+
verbose.report(1, "Doing something... ", end='')
|
|
533
|
+
# ... do something
|
|
534
|
+
verbose.report(1, "done.", head=False)
|
|
173
535
|
|
|
174
|
-
|
|
175
|
-
# do some stuff
|
|
176
|
-
verbose.report(1, "done.\nOverall result is good", head=False)
|
|
536
|
+
which prints
|
|
177
537
|
|
|
178
|
-
|
|
538
|
+
.. code-block:: python
|
|
539
|
+
|
|
540
|
+
01: Doing something... done.
|
|
541
|
+
|
|
542
|
+
Another use case is updates per line, for example:::
|
|
179
543
|
|
|
180
|
-
|
|
181
|
-
|
|
544
|
+
from cdxcore.verbose import Context
|
|
545
|
+
verbose = Context()
|
|
546
|
+
N = 1000
|
|
547
|
+
for i in range(N):
|
|
548
|
+
verbose.report(1,f"\\rStatus {int(float(i+1)/float(N)*100)}%... ", end='')
|
|
549
|
+
# do something
|
|
550
|
+
verbose.report(1,"done.", head=False)
|
|
551
|
+
|
|
552
|
+
will provide progress information in the current line as the loop is processed.
|
|
553
|
+
|
|
554
|
+
*Implementation notice:* The use of ``\\r`` is managed using :class:`cdxcore.crman.CRMan`.
|
|
182
555
|
|
|
183
556
|
Parameters
|
|
184
557
|
----------
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
558
|
+
level : int
|
|
559
|
+
Level to add to current level.
|
|
560
|
+
|
|
561
|
+
message : str|Callable
|
|
562
|
+
|
|
563
|
+
Text containing format characters.
|
|
564
|
+
|
|
565
|
+
The following alternatives are suppoted:
|
|
566
|
+
|
|
567
|
+
* Python 3 ```{parameter:d}```, in which case ``message.fmt(kwargs)`` for :meth:`str.format` is used
|
|
568
|
+
to obtain the output message.
|
|
569
|
+
|
|
570
|
+
* Python 2 ```%(parameter)d``` in which case ``message % kwargs`` is used to obtain the output message.
|
|
571
|
+
|
|
572
|
+
* Classic C-stype ```%d, %s, %f``` in which case ``message % args`` is used to obtain the output message.
|
|
573
|
+
|
|
574
|
+
* If ``message`` is a ``Callable`` such as a ``lambda`` function, then ``message( *args, **kwargs )``
|
|
575
|
+
is called to obtain the output message.
|
|
576
|
+
|
|
577
|
+
Note that a common use case is using an f-string wrapped in a ``lambda`` function. In this case
|
|
578
|
+
you do not need ``args`` or ``kwargs``::
|
|
579
|
+
|
|
580
|
+
x = 1
|
|
581
|
+
verbose.write(lambda : f"Delayed f-string formatting {x}")
|
|
582
|
+
|
|
583
|
+
end : str, optional
|
|
584
|
+
Terminating string akin to ``end`` in :func:`print`.
|
|
585
|
+
Use ``''`` to not print a newline. See example above for a use case.
|
|
586
|
+
|
|
587
|
+
head : bool, optional;
|
|
588
|
+
Whether this message needs a header (i.e. the ``01`` and spacing).
|
|
589
|
+
Typically ``False`` if the previous call to ``write()`` used `end=''`. See examples above.
|
|
193
590
|
|
|
591
|
+
*args, **kwargs:
|
|
592
|
+
See above
|
|
194
593
|
"""
|
|
195
594
|
message = self.fmt( level, message, *args, head=head, **kwargs )
|
|
196
595
|
if not message is None:
|
|
197
596
|
self.crman.write(message,end=end,flush=True, channel=self.channel )
|
|
198
597
|
|
|
199
|
-
def fmt( self, level : int, message : str, *args, head : bool = True, **kwargs ) -> str:
|
|
598
|
+
def fmt( self, level : int, message : str|Callable, *args, head : bool = True, **kwargs ) -> str:
|
|
200
599
|
"""
|
|
201
|
-
Formats message with the formattting arguments at curent context level plus
|
|
202
|
-
|
|
600
|
+
Formats message with the formattting arguments at curent context level plus ``level``.
|
|
601
|
+
|
|
602
|
+
This function returns ```` if current level plus ``level`` is not visible.
|
|
603
|
+
In that case no string formatting takes place.
|
|
203
604
|
|
|
204
605
|
Parameters
|
|
205
606
|
----------
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
607
|
+
level : int
|
|
608
|
+
Level to add to current level.
|
|
609
|
+
|
|
610
|
+
message : str|Callable
|
|
611
|
+
|
|
612
|
+
Text containing format characters.
|
|
613
|
+
|
|
614
|
+
The following alternatives are suppoted:
|
|
615
|
+
|
|
616
|
+
* Python 3 ```{parameter:d}```, in which case ``message.fmt(kwargs)`` for :meth:`str.format` is used
|
|
617
|
+
to obtain the output message.
|
|
618
|
+
|
|
619
|
+
* Python 2 ```%(parameter)d``` in which case ``message % kwargs`` is used to obtain the output message.
|
|
620
|
+
|
|
621
|
+
* Classic C-stype ```%d, %s, %f``` in which case ``message % args`` is used to obtain the output message.
|
|
622
|
+
|
|
623
|
+
* If ``message`` is a ``Callable`` such as a ``lambda`` function, then ``message( *args, **kwargs )``
|
|
624
|
+
is called to obtain the output message.
|
|
625
|
+
|
|
626
|
+
Note that a common use case is using an f-string wrapped in a ``lambda`` function. In this case
|
|
627
|
+
you do not need ``args`` or ``kwargs``::
|
|
628
|
+
|
|
629
|
+
x = 1
|
|
630
|
+
verbose.write(lambda : f"Delayed f-string formatting {x}")
|
|
631
|
+
|
|
632
|
+
head : bool, optional;
|
|
633
|
+
Whether this message needs a header (i.e. the ``01`` and spacing).
|
|
634
|
+
Typically ``False`` if the previous call to ``write()`` used `end=''`. See examples above.
|
|
635
|
+
|
|
636
|
+
*args, **kwargs:
|
|
637
|
+
See above
|
|
213
638
|
|
|
214
639
|
Returns
|
|
215
640
|
-------
|
|
216
|
-
|
|
641
|
+
String : str
|
|
642
|
+
Formatted string, or ``None` `if the current level plus ``level`` is not visible.
|
|
217
643
|
"""
|
|
218
644
|
if not self.shall_report(level):
|
|
219
645
|
return None
|
|
220
|
-
message
|
|
221
|
-
if message == "":
|
|
646
|
+
if isinstance(message, str) and message == "":
|
|
222
647
|
return ""
|
|
223
648
|
str_level = self.str_indent( level )
|
|
224
|
-
text = fmt( message, *args, **kwargs )
|
|
649
|
+
text = fmt( message, *args, **kwargs )
|
|
225
650
|
text = text[:-1].replace("\r", "\r" + str_level ) + text[-1]
|
|
226
651
|
text = text[:-1].replace("\n", "\n" + str_level ) + text[-1]
|
|
227
652
|
text = str_level + text if head and text[:1] != "\r" else text
|
|
228
653
|
return text
|
|
229
654
|
|
|
230
|
-
def
|
|
655
|
+
def __call__(self, add_level : int = 1, message : str|Callable = None, end : str = "\n", head : bool = True, *args, **kwargs ):
|
|
231
656
|
"""
|
|
232
|
-
Create a sub
|
|
657
|
+
Create and return a sub ``Context`` at current level plus ``add_level``.
|
|
658
|
+
|
|
659
|
+
If a ``message`` is provided, :meth:`cdxcore.verbose.Context.write()` is called
|
|
660
|
+
before the new ``Context`` is created.
|
|
661
|
+
|
|
662
|
+
Example::
|
|
663
|
+
|
|
664
|
+
from cdxcore.verbose import Context
|
|
665
|
+
def f( verbose : Context = Context.quiet ):
|
|
666
|
+
# ...
|
|
667
|
+
verbose.write("'f'' usuing a sub-context.")
|
|
668
|
+
verbose = Context.all
|
|
669
|
+
verbose.write("Main")
|
|
670
|
+
f( verbose=verbose(1) ) # create sub-context
|
|
671
|
+
|
|
672
|
+
prints
|
|
673
|
+
|
|
674
|
+
.. code-block:: python
|
|
675
|
+
|
|
676
|
+
00: Main
|
|
677
|
+
01: 'f'' usuing a sub-context.
|
|
233
678
|
|
|
234
679
|
Parameters
|
|
235
680
|
----------
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
681
|
+
add_level : int
|
|
682
|
+
Level to add to the current level. Set to 0 for the same level.
|
|
683
|
+
|
|
684
|
+
message : str|Callable, optional.
|
|
685
|
+
|
|
686
|
+
Text containing format characters, or ``None`` to not print a message.
|
|
687
|
+
|
|
688
|
+
The following alternatives are suppoted:
|
|
689
|
+
|
|
690
|
+
* Python 3 ```{parameter:d}```, in which case ``message.fmt(kwargs)`` for :meth:`str.format` is used
|
|
691
|
+
to obtain the output message.
|
|
692
|
+
|
|
693
|
+
* Python 2 ```%(parameter)d``` in which case ``message % kwargs`` is used to obtain the output message.
|
|
694
|
+
|
|
695
|
+
* Classic C-stype ```%d, %s, %f``` in which case ``message % args`` is used to obtain the output message.
|
|
696
|
+
|
|
697
|
+
* If ``message`` is a ``Callable`` such as a ``lambda`` function, then ``message( *args, **kwargs )``
|
|
698
|
+
is called to obtain the output message.
|
|
699
|
+
|
|
700
|
+
Note that a common use case is using an f-string wrapped in a ``lambda`` function. In this case
|
|
701
|
+
you do not need ``args`` or ``kwargs``::
|
|
702
|
+
|
|
703
|
+
x = 1
|
|
704
|
+
verbose.write(lambda : f"Delayed f-string formatting {x}")
|
|
705
|
+
|
|
706
|
+
head : bool, optional;
|
|
707
|
+
Whether this message needs a header (i.e. the ``01`` and spacing).
|
|
708
|
+
Typically ``False`` if the previous call to ``write()`` used `end=''`. See examples above.
|
|
709
|
+
|
|
710
|
+
*args, **kwargs:
|
|
711
|
+
See above
|
|
241
712
|
|
|
242
713
|
Returns
|
|
243
714
|
-------
|
|
244
|
-
|
|
245
|
-
|
|
715
|
+
verbose : ``Context``
|
|
716
|
+
Sub context with new level equal to current level plus ``add_level``.
|
|
246
717
|
"""
|
|
247
718
|
add_level = int(add_level)
|
|
248
|
-
|
|
719
|
+
verify( add_level >= 0, "'add_level' cannot be negative. Found {add_level}", add_level=add_level, exception=ValueError)
|
|
249
720
|
|
|
250
721
|
if not message is None:
|
|
251
|
-
self.write( message=message, *args, **kwargs )
|
|
722
|
+
self.write( message=message, end=end, head=head, *args, **kwargs )
|
|
252
723
|
|
|
253
|
-
sub
|
|
254
|
-
assert sub.
|
|
724
|
+
sub = Context(self)
|
|
725
|
+
assert sub.visibility == self.visibility, "Internal error"
|
|
255
726
|
sub.level = self.level + add_level
|
|
256
|
-
sub.indent = self.indent
|
|
257
|
-
sub.fmt_level = self.fmt_level
|
|
258
727
|
return sub
|
|
259
728
|
|
|
260
|
-
def __call__(self, add_level : int, message : str = None, *args, **kwargs ):
|
|
261
|
-
"""
|
|
262
|
-
Create a sub context at level 'sub_level'. The latter defaults to self.default_sub.
|
|
263
|
-
Optionally write message at the new level.
|
|
264
|
-
|
|
265
|
-
That means writing
|
|
266
|
-
verbose(1,"Hallo")
|
|
267
|
-
is equivalent to
|
|
268
|
-
verbose.report(1,"Hallo")
|
|
269
|
-
|
|
270
|
-
Parameters
|
|
271
|
-
----------
|
|
272
|
-
add_level : int
|
|
273
|
-
Level of the sub context with respect to self. Set to 0 for the same level.
|
|
274
|
-
For convenience, the user may also let 'add_level' be a string, replacing 'message'.
|
|
275
|
-
|
|
276
|
-
The following two are equivalent
|
|
277
|
-
verbose(0,"Message %(test)s", test="test")
|
|
278
|
-
and
|
|
279
|
-
verbose("Message %(test)s", test="test")
|
|
280
|
-
|
|
281
|
-
message, fmt, args:
|
|
282
|
-
If message is not None, call report() at _current_ level, not the newly
|
|
283
|
-
created sub level.
|
|
284
|
-
|
|
285
|
-
Returns
|
|
286
|
-
-------
|
|
287
|
-
Context
|
|
288
|
-
Sub context with level = self.level + sub_level
|
|
289
|
-
"""
|
|
290
|
-
if message is None:
|
|
291
|
-
assert len(args) == 0 and len(kwargs) == 0, "Internal error: no 'message' is provided."
|
|
292
|
-
return self.sub(add_level)
|
|
293
|
-
if isinstance(add_level, str):
|
|
294
|
-
_verify( message is None, "Cannot specify 'add_level' as string and also specify 'message'", exception=ValueError)
|
|
295
|
-
self.write( add_level, *args, **kwargs )
|
|
296
|
-
return self
|
|
297
|
-
else:
|
|
298
|
-
assert isinstance(add_level, int), "'add_level' should be an int or a string"
|
|
299
|
-
self.report( add_level, message, *args, **kwargs )
|
|
300
|
-
return self.sub(add_level)
|
|
301
|
-
|
|
302
|
-
def limit(self, verbose):
|
|
303
|
-
""" Assigns the minimim verbosity of self and verbose, i.e. self.verbose = min(self.verbose,verbose) """
|
|
304
|
-
if verbose is None:
|
|
305
|
-
return self # None means accepting everything
|
|
306
|
-
if isinstance(verbose, Context) or type(verbose).__name__ == "Context":
|
|
307
|
-
verbose = verbose.verbose
|
|
308
|
-
if verbose is None:
|
|
309
|
-
return self
|
|
310
|
-
elif verbose == self.QUIET:
|
|
311
|
-
verbose = -1
|
|
312
|
-
elif verbose == self.ALL:
|
|
313
|
-
return self
|
|
314
|
-
else:
|
|
315
|
-
verbose = int(verbose)
|
|
316
|
-
if self.verbose is None:
|
|
317
|
-
self.verbose = verbose
|
|
318
|
-
elif self.verbose > verbose:
|
|
319
|
-
self.verbose = verbose
|
|
320
|
-
return self
|
|
321
|
-
|
|
322
729
|
@property
|
|
323
730
|
def as_verbose(self):
|
|
324
|
-
""" Return a Context at the same level as
|
|
731
|
+
""" Return a Context at the same current reporting level as ``self`` with full visibility """
|
|
325
732
|
copy = Context(self)
|
|
326
|
-
copy.
|
|
733
|
+
copy.visibility = None
|
|
327
734
|
return copy
|
|
328
735
|
|
|
329
736
|
@property
|
|
330
737
|
def as_quiet(self):
|
|
331
|
-
""" Return a Context at the same level as
|
|
738
|
+
""" Return a Context at the same current reporting level as ``self`` with zero visibility """
|
|
332
739
|
copy = Context(self)
|
|
333
|
-
copy.
|
|
740
|
+
copy.visibility = 0
|
|
334
741
|
return copy
|
|
335
742
|
|
|
336
743
|
@property
|
|
337
744
|
def is_quiet(self) -> bool:
|
|
338
|
-
""" Whether the current context is quiet """
|
|
339
|
-
return not self.
|
|
340
|
-
|
|
341
|
-
def shall_report(self,
|
|
342
|
-
""" Returns whether to print something at
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
return self.
|
|
346
|
-
|
|
347
|
-
def str_indent(self,
|
|
348
|
-
""" Returns the string identation for
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
s1 = ' ' * (self.indent * (self.level +
|
|
352
|
-
s2 = self.fmt_level if self.fmt_level.find("%") == -1 else self.fmt_level % (self.level +
|
|
745
|
+
""" Whether the current context is ``"quiet"`` """
|
|
746
|
+
return not self.visibility is None and self.visibility < 0
|
|
747
|
+
|
|
748
|
+
def shall_report(self, add_level : int = 0 ) -> bool:
|
|
749
|
+
""" Returns whether to print something at current level plus ``add_level``. """
|
|
750
|
+
add_level = int(add_level)
|
|
751
|
+
verify( add_level >= 0, "'add_level' cannot be negative. Found {add_level}", add_level=add_level, exception=ValueError)
|
|
752
|
+
return self.visibility is None or self.visibility >= self.level + add_level
|
|
753
|
+
|
|
754
|
+
def str_indent(self, add_level : int = 0) -> str:
|
|
755
|
+
""" Returns the string identation for the current level plus ``add_level`` """
|
|
756
|
+
add_level = int(add_level)
|
|
757
|
+
verify( add_level >= 0, "'add_level' cannot be negative. Found {add_level}", add_level=add_level, exception=ValueError)
|
|
758
|
+
s1 = ' ' * (self.indent * (self.level + add_level))
|
|
759
|
+
s2 = self.fmt_level if self.fmt_level.find("%") == -1 else self.fmt_level % (self.level + add_level)
|
|
353
760
|
return s2+s1
|
|
354
761
|
|
|
355
762
|
# Misc
|
|
@@ -357,25 +764,36 @@ class Context(object):
|
|
|
357
764
|
|
|
358
765
|
def timer(self) -> Timer:
|
|
359
766
|
"""
|
|
360
|
-
Returns a new util.Timer object to measure time spent in a block of code
|
|
767
|
+
Returns a new :class:`cdxcore.util.Timer` object to measure time spent in a block of code.
|
|
361
768
|
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
769
|
+
Example::
|
|
770
|
+
|
|
771
|
+
import time as time
|
|
772
|
+
from cdxcore.verbose import Context
|
|
773
|
+
|
|
774
|
+
verbose = Context("all")
|
|
775
|
+
with verbose.Timer() as tme:
|
|
776
|
+
verbose.write("Starting job... ", end='')
|
|
777
|
+
time.sleep(1)
|
|
778
|
+
verbose.write(f"done; this took {tme}.", head=False)
|
|
779
|
+
|
|
780
|
+
produces
|
|
781
|
+
|
|
782
|
+
.. code-block:: python
|
|
783
|
+
|
|
784
|
+
00: Starting job... done; this took 1s.
|
|
785
|
+
|
|
369
786
|
"""
|
|
370
787
|
return Timer()
|
|
371
788
|
|
|
372
789
|
# uniqueHash
|
|
373
790
|
# ----------
|
|
374
791
|
|
|
375
|
-
def __unique_hash__( self,
|
|
792
|
+
def __unique_hash__( self, unique_hash, debug_trace ) -> str:
|
|
376
793
|
"""
|
|
377
|
-
|
|
794
|
+
Hash function for :class:`cdxcore.uniquehash.UniqueHash`.
|
|
378
795
|
This function always returns an empty string, which means that the object is never hashed.
|
|
796
|
+
:meta private:
|
|
379
797
|
"""
|
|
380
798
|
return ""
|
|
381
799
|
|
|
@@ -384,19 +802,36 @@ class Context(object):
|
|
|
384
802
|
|
|
385
803
|
def apply_channel( self, channel : Callable ):
|
|
386
804
|
"""
|
|
387
|
-
|
|
805
|
+
*Advanced Use*
|
|
806
|
+
|
|
807
|
+
Returns a new ```Context`` object with the same currrent state as ``self``,
|
|
808
|
+
but pointing to ``channel``.
|
|
388
809
|
"""
|
|
389
810
|
return Context( self, channel=channel ) if channel != self.channel else self
|
|
390
811
|
|
|
391
812
|
|
|
392
|
-
|
|
393
|
-
|
|
813
|
+
quiet = Context(Context.QUIET)
|
|
814
|
+
all_ = Context(Context.ALL)
|
|
394
815
|
Context.quiet = quiet
|
|
816
|
+
"""
|
|
817
|
+
A default ``Context`` with zero visibility.
|
|
818
|
+
"""
|
|
395
819
|
|
|
396
|
-
|
|
397
|
-
|
|
820
|
+
Context.all = all_
|
|
821
|
+
"""
|
|
822
|
+
A default ``Context`` with full visibility.
|
|
823
|
+
"""
|
|
824
|
+
|
|
825
|
+
Context.quiet.__doc__ = \
|
|
826
|
+
"""
|
|
827
|
+
A default ``Context`` with zero visibility.
|
|
828
|
+
"""
|
|
829
|
+
|
|
830
|
+
Context.quiet.__doc__ = \
|
|
831
|
+
"""
|
|
832
|
+
A default ``Context`` with full visibility.
|
|
833
|
+
"""
|
|
398
834
|
|
|
399
|
-
Context.Timer = Timer
|
|
400
835
|
|
|
401
836
|
|
|
402
837
|
|