cdxcore 0.1.5__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 +15 -0
- cdxcore/config.py +1633 -0
- cdxcore/crman.py +105 -0
- cdxcore/deferred.py +220 -0
- cdxcore/dynaplot.py +1155 -0
- cdxcore/filelock.py +430 -0
- cdxcore/jcpool.py +411 -0
- cdxcore/logger.py +319 -0
- cdxcore/np.py +1098 -0
- cdxcore/npio.py +270 -0
- cdxcore/prettydict.py +388 -0
- cdxcore/prettyobject.py +64 -0
- cdxcore/sharedarray.py +285 -0
- cdxcore/subdir.py +2963 -0
- cdxcore/uniquehash.py +970 -0
- cdxcore/util.py +1041 -0
- cdxcore/verbose.py +403 -0
- cdxcore/version.py +402 -0
- cdxcore-0.1.5.dist-info/METADATA +1418 -0
- cdxcore-0.1.5.dist-info/RECORD +30 -0
- cdxcore-0.1.5.dist-info/WHEEL +5 -0
- cdxcore-0.1.5.dist-info/licenses/LICENSE +21 -0
- cdxcore-0.1.5.dist-info/top_level.txt +4 -0
- conda/conda_exists.py +10 -0
- conda/conda_modify_yaml.py +42 -0
- tests/_cdxbasics.py +1086 -0
- tests/test_uniquehash.py +469 -0
- tests/test_util.py +329 -0
- up/git_message.py +7 -0
- up/pip_modify_setup.py +55 -0
cdxcore/verbose.py
ADDED
|
@@ -0,0 +1,403 @@
|
|
|
1
|
+
"""
|
|
2
|
+
verbose
|
|
3
|
+
Utility for verbose printing with indentation
|
|
4
|
+
Hans Buehler 2022
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from .util import fmt, Timer
|
|
8
|
+
from .util import _verify
|
|
9
|
+
from .crman import CRMan, Callable
|
|
10
|
+
|
|
11
|
+
class Context(object):
|
|
12
|
+
"""
|
|
13
|
+
Class for printing indented messages, filtered by overall level of verbosity.
|
|
14
|
+
|
|
15
|
+
context = Context( verbose = 4 )
|
|
16
|
+
|
|
17
|
+
def f_2( context ):
|
|
18
|
+
|
|
19
|
+
context.report( 1, "Running 'f_2'")
|
|
20
|
+
|
|
21
|
+
def f_1( context ):
|
|
22
|
+
|
|
23
|
+
context.report( 1, "Running 'f_1'")
|
|
24
|
+
f_2( context.sub(1, "Entering 'f_2'") )
|
|
25
|
+
|
|
26
|
+
"""
|
|
27
|
+
|
|
28
|
+
QUIET = "quiet"
|
|
29
|
+
ALL = "all"
|
|
30
|
+
|
|
31
|
+
def __init__(self, verbose_or_init = None, *,
|
|
32
|
+
indent : int = 2,
|
|
33
|
+
fmt_level : str = "%02ld: ",
|
|
34
|
+
level : int = None,
|
|
35
|
+
channel : Callable = None
|
|
36
|
+
):
|
|
37
|
+
"""
|
|
38
|
+
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
|
+
"""
|
|
86
|
+
if not level is None: _verify( level>=0, "'level' must not be negative; found {level}", exception=ValueError)
|
|
87
|
+
if isinstance( verbose_or_init, Context ) or type(verbose_or_init).__name__ == "Context":
|
|
88
|
+
# copy constructor
|
|
89
|
+
self.verbose = verbose_or_init.verbose
|
|
90
|
+
self.level = verbose_or_init.level if level is None else level
|
|
91
|
+
self.indent = verbose_or_init.indent
|
|
92
|
+
self.fmt_level = verbose_or_init.fmt_level
|
|
93
|
+
self.crman = CRMan()
|
|
94
|
+
self.channel = verbose_or_init.channel if channel is None else channel
|
|
95
|
+
return
|
|
96
|
+
|
|
97
|
+
if isinstance( verbose_or_init, str ):
|
|
98
|
+
# construct with key word
|
|
99
|
+
if verbose_or_init == self.QUIET:
|
|
100
|
+
verbose_or_init = -1
|
|
101
|
+
else:
|
|
102
|
+
_verify( verbose_or_init == self.ALL, lambda : f"'verbose_or_init': if provided as a string, has to be '{self.QUIET}' or '{self.ALL}'. Found '{verbose_or_init}'", exception=ValueError)
|
|
103
|
+
verbose_or_init = None
|
|
104
|
+
elif not verbose_or_init is None:
|
|
105
|
+
verbose_or_init = int(verbose_or_init)
|
|
106
|
+
|
|
107
|
+
indent = int(indent)
|
|
108
|
+
_verify( indent >=0, "'indent' cannot be negative. Found {indent}", exception=ValueError)
|
|
109
|
+
|
|
110
|
+
self.verbose = verbose_or_init # print up to this level
|
|
111
|
+
self.level = 0 if level is None else level
|
|
112
|
+
self.indent = indent # indentation level
|
|
113
|
+
self.fmt_level = str(fmt_level) # output format
|
|
114
|
+
self.crman = CRMan()
|
|
115
|
+
self.channel = channel
|
|
116
|
+
|
|
117
|
+
def write( self, message : str, *args, end : str = "\n", head : bool = True, **kwargs ):
|
|
118
|
+
"""
|
|
119
|
+
Report message at level 0 with the formattting arguments at curent context level.
|
|
120
|
+
The message will be formatted as util.fmt( message, *args, **kwargs )
|
|
121
|
+
It will be displayed in all cases except if the context is 'quiet'.
|
|
122
|
+
|
|
123
|
+
The parameter 'end' matches 'end' in print, e.g. end='' avoids a newline at the end of the message.
|
|
124
|
+
If 'head' is True, then the first line of the text will be preceeded by proper indentation.
|
|
125
|
+
If 'head' is False, the first line will be printed without preamble.
|
|
126
|
+
|
|
127
|
+
This means the following is a valid pattern
|
|
128
|
+
|
|
129
|
+
verbose = Context()
|
|
130
|
+
verbose.write("Doing something... ", end='')
|
|
131
|
+
# do something
|
|
132
|
+
verbose.write("done.", head=False)
|
|
133
|
+
|
|
134
|
+
which now prints
|
|
135
|
+
|
|
136
|
+
00: Doing something... done.
|
|
137
|
+
|
|
138
|
+
"""
|
|
139
|
+
self.report( 0, message, *args, end=end, head=head, **kwargs )
|
|
140
|
+
|
|
141
|
+
def write_t( self, message : str, *args, end : str = "\n", head : bool = True, **kwargs ) -> Timer:
|
|
142
|
+
"""
|
|
143
|
+
Same as using write() first and then timer()
|
|
144
|
+
|
|
145
|
+
Report message at level 0 with the formattting arguments at curent context level.
|
|
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:
|
|
155
|
+
|
|
156
|
+
verbose = Context()
|
|
157
|
+
with verbose.write_t("Doing something... ", end='') as t:
|
|
158
|
+
# do something
|
|
159
|
+
verbose.write("done; this took {t}.", head=False)
|
|
160
|
+
"""
|
|
161
|
+
self.report( 0, message, *args, end=end, head=head, **kwargs )
|
|
162
|
+
return Timer()
|
|
163
|
+
|
|
164
|
+
def report( self, level : int, message : str, *args, end : str = "\n", head : bool = True, **kwargs ):
|
|
165
|
+
"""
|
|
166
|
+
Print message with the formattting arguments at curent context level plus 'level'
|
|
167
|
+
The message will be formatted as util.fmt( message, *args, **kwargs )
|
|
168
|
+
Will print empty lines.
|
|
169
|
+
|
|
170
|
+
The 'end' and 'head' parameters can be used as follows
|
|
171
|
+
|
|
172
|
+
verbose = Context()
|
|
173
|
+
|
|
174
|
+
verbose.report(1, "Testing... ", end='')
|
|
175
|
+
# do some stuff
|
|
176
|
+
verbose.report(1, "done.\nOverall result is good", head=False)
|
|
177
|
+
|
|
178
|
+
prints
|
|
179
|
+
|
|
180
|
+
01: Testing... done.
|
|
181
|
+
01: Overall result is good
|
|
182
|
+
|
|
183
|
+
Parameters
|
|
184
|
+
----------
|
|
185
|
+
level : int
|
|
186
|
+
Additional context level, added to the level of 'self'.
|
|
187
|
+
message, args, kwargs:
|
|
188
|
+
Parameters for util.fmt().
|
|
189
|
+
end : string
|
|
190
|
+
Same function as in print(). In particular, end='' avoids a newline at the end of the messahe
|
|
191
|
+
head : bool
|
|
192
|
+
If False, do not print out preamble for first line of 'message'
|
|
193
|
+
|
|
194
|
+
"""
|
|
195
|
+
message = self.fmt( level, message, *args, head=head, **kwargs )
|
|
196
|
+
if not message is None:
|
|
197
|
+
self.crman.write(message,end=end,flush=True, channel=self.channel )
|
|
198
|
+
|
|
199
|
+
def fmt( self, level : int, message : str, *args, head : bool = True, **kwargs ) -> str:
|
|
200
|
+
"""
|
|
201
|
+
Formats message with the formattting arguments at curent context level plus 'level'
|
|
202
|
+
The message will be formatted with util.fmt( message, *args, **kwargs ) and then indented appropriately.
|
|
203
|
+
|
|
204
|
+
Parameters
|
|
205
|
+
----------
|
|
206
|
+
level : int
|
|
207
|
+
Additional context level, added to the level of 'self'.
|
|
208
|
+
message, args, kwargs:
|
|
209
|
+
Parameters for the util.fmt().
|
|
210
|
+
head : bool
|
|
211
|
+
Set to False to turn off indentation for the first line of the resulting
|
|
212
|
+
message. See write() for an example use case
|
|
213
|
+
|
|
214
|
+
Returns
|
|
215
|
+
-------
|
|
216
|
+
Formatted string, or None if not to be reported at set level.
|
|
217
|
+
"""
|
|
218
|
+
if not self.shall_report(level):
|
|
219
|
+
return None
|
|
220
|
+
message = str(message)
|
|
221
|
+
if message == "":
|
|
222
|
+
return ""
|
|
223
|
+
str_level = self.str_indent( level )
|
|
224
|
+
text = fmt( message, *args, **kwargs ) if (len(args) + len(kwargs) > 0) else message
|
|
225
|
+
text = text[:-1].replace("\r", "\r" + str_level ) + text[-1]
|
|
226
|
+
text = text[:-1].replace("\n", "\n" + str_level ) + text[-1]
|
|
227
|
+
text = str_level + text if head and text[:1] != "\r" else text
|
|
228
|
+
return text
|
|
229
|
+
|
|
230
|
+
def sub( self, add_level : int = 1, message : str = None, *args, **kwargs ):
|
|
231
|
+
"""
|
|
232
|
+
Create a sub context at level 'sub_level'. The latter defaults to self.default_sub
|
|
233
|
+
|
|
234
|
+
Parameters
|
|
235
|
+
----------
|
|
236
|
+
add_level : int
|
|
237
|
+
Level of the sub context with respect to self. Set to 0 for the same level.
|
|
238
|
+
message, fmt, args:
|
|
239
|
+
If message is not None, call report() at _current_ level, not the newly
|
|
240
|
+
created sub level
|
|
241
|
+
|
|
242
|
+
Returns
|
|
243
|
+
-------
|
|
244
|
+
Context
|
|
245
|
+
Sub context with level = self.level + sub_level
|
|
246
|
+
"""
|
|
247
|
+
add_level = int(add_level)
|
|
248
|
+
_verify( add_level >= 0, "'add_level' cannot be negative. Found {add_level}", exception=ValueError)
|
|
249
|
+
|
|
250
|
+
if not message is None:
|
|
251
|
+
self.write( message=message, *args, **kwargs )
|
|
252
|
+
|
|
253
|
+
sub = Context(self.verbose)
|
|
254
|
+
assert sub.verbose == self.verbose, "Internal error"
|
|
255
|
+
sub.level = self.level + add_level
|
|
256
|
+
sub.indent = self.indent
|
|
257
|
+
sub.fmt_level = self.fmt_level
|
|
258
|
+
return sub
|
|
259
|
+
|
|
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
|
+
@property
|
|
323
|
+
def as_verbose(self):
|
|
324
|
+
""" Return a Context at the same level as 'self' with full verbosity """
|
|
325
|
+
copy = Context(self)
|
|
326
|
+
copy.verbose = None
|
|
327
|
+
return copy
|
|
328
|
+
|
|
329
|
+
@property
|
|
330
|
+
def as_quiet(self):
|
|
331
|
+
""" Return a Context at the same level as 'self' with zero verbosity """
|
|
332
|
+
copy = Context(self)
|
|
333
|
+
copy.verbose = 0
|
|
334
|
+
return copy
|
|
335
|
+
|
|
336
|
+
@property
|
|
337
|
+
def is_quiet(self) -> bool:
|
|
338
|
+
""" Whether the current context is quiet """
|
|
339
|
+
return not self.verbose is None and self.verbose < 0
|
|
340
|
+
|
|
341
|
+
def shall_report(self, sub_level : int = 0 ) -> bool:
|
|
342
|
+
""" Returns whether to print something at 'sub_level' relative to the current level """
|
|
343
|
+
sub_level = int(sub_level)
|
|
344
|
+
_verify( sub_level >= 0, "'sub_level' cannot be negative. Found {sub_level}", exception=ValueError)
|
|
345
|
+
return self.verbose is None or self.verbose >= self.level + sub_level
|
|
346
|
+
|
|
347
|
+
def str_indent(self, sub_level : int = 0) -> str:
|
|
348
|
+
""" Returns the string identation for a given 'sub_level', or the context """
|
|
349
|
+
sub_level = int(sub_level)
|
|
350
|
+
_verify( sub_level >= 0, "'sub_level' cannot be negative. Found {sub_level}", exception=ValueError)
|
|
351
|
+
s1 = ' ' * (self.indent * (self.level + sub_level))
|
|
352
|
+
s2 = self.fmt_level if self.fmt_level.find("%") == -1 else self.fmt_level % (self.level + sub_level)
|
|
353
|
+
return s2+s1
|
|
354
|
+
|
|
355
|
+
# Misc
|
|
356
|
+
# ----
|
|
357
|
+
|
|
358
|
+
def timer(self) -> Timer:
|
|
359
|
+
"""
|
|
360
|
+
Returns a new util.Timer object to measure time spent in a block of code
|
|
361
|
+
|
|
362
|
+
verbose = Context("all")
|
|
363
|
+
with verbose.timer() as t:
|
|
364
|
+
verbose.write("Starting... ", end='')
|
|
365
|
+
...
|
|
366
|
+
verbose.write(f"this took {t}.", head=False)
|
|
367
|
+
|
|
368
|
+
Equivalent to Context.Timer()
|
|
369
|
+
"""
|
|
370
|
+
return Timer()
|
|
371
|
+
|
|
372
|
+
# uniqueHash
|
|
373
|
+
# ----------
|
|
374
|
+
|
|
375
|
+
def __unique_hash__( self, uniqueHash, debug_trace ) -> str:
|
|
376
|
+
"""
|
|
377
|
+
Compute non-hash for use with cdxbasics.util.uniqueHash()
|
|
378
|
+
This function always returns an empty string, which means that the object is never hashed.
|
|
379
|
+
"""
|
|
380
|
+
return ""
|
|
381
|
+
|
|
382
|
+
# Channels
|
|
383
|
+
# --------
|
|
384
|
+
|
|
385
|
+
def apply_channel( self, channel : Callable ):
|
|
386
|
+
"""
|
|
387
|
+
Returns a new Context object with the same currrent state as 'self', but pointing to 'channel'
|
|
388
|
+
"""
|
|
389
|
+
return Context( self, channel=channel ) if channel != self.channel else self
|
|
390
|
+
|
|
391
|
+
|
|
392
|
+
# Recommended default parameter 'quiet' for functions accepting a context parameter
|
|
393
|
+
quiet = Context(Context.QUIET)
|
|
394
|
+
Context.quiet = quiet
|
|
395
|
+
|
|
396
|
+
all_ = Context(Context.ALL)
|
|
397
|
+
Context.all = all_
|
|
398
|
+
|
|
399
|
+
Context.Timer = Timer
|
|
400
|
+
|
|
401
|
+
|
|
402
|
+
|
|
403
|
+
|