cdxcore 0.1.5__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.5.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.5.dist-info/METADATA +0 -1418
- cdxcore-0.1.5.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.5.dist-info → cdxcore-0.1.9.dist-info}/WHEEL +0 -0
- {cdxcore-0.1.5.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/crman.py
CHANGED
|
@@ -1,52 +1,91 @@
|
|
|
1
|
-
"""
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
1
|
+
r"""
|
|
2
|
+
Overview
|
|
3
|
+
--------
|
|
4
|
+
|
|
5
|
+
A very simple implementation of a tool that tracks the last printed line before a newline was encountered.
|
|
6
|
+
Helps with somewhat consistent progress reporting with "\\r" and "\\n" characters.
|
|
7
|
+
|
|
8
|
+
*This functionality does not quite work accross all terminal types which were tested. Main focus is to make
|
|
9
|
+
it work for Jupyer for now. Any feedback on
|
|
10
|
+
how to make this more generically operational is welcome.
|
|
11
|
+
|
|
12
|
+
Used by :class:`cdxcore.verbose.Context`.
|
|
13
|
+
|
|
14
|
+
Import
|
|
15
|
+
------
|
|
16
|
+
|
|
17
|
+
.. code-block:: python
|
|
18
|
+
|
|
19
|
+
from cdxcore.crman import CRman
|
|
20
|
+
|
|
5
21
|
"""
|
|
6
22
|
|
|
7
23
|
from collections.abc import Callable
|
|
8
|
-
from .logger import Logger#
|
|
9
|
-
_log = Logger(__file__)
|
|
10
24
|
|
|
11
25
|
class CRMan(object):
|
|
12
|
-
"""
|
|
13
|
-
|
|
14
|
-
This class is meant to enable efficient per-line updates using '\r' for text output with a focus on making it work with both Jupyter and the command shell.
|
|
15
|
-
In particular, Jupyter does not support the ANSI \33[2K 'clear line' code.
|
|
26
|
+
r"""
|
|
27
|
+
Carriage Return ("\\r") manager.
|
|
16
28
|
|
|
29
|
+
This class is meant to enable efficient per-line updates using "\\r" for text output with a focus on making it work with both Jupyter and the command shell.
|
|
30
|
+
In particular, Jupyter does not support the ANSI `\\33[2K` 'clear line' code. To simulate clearing
|
|
31
|
+
lines, ``CRMan`` keeps track of the length of the current line, and clears it by appending spaces to a message
|
|
32
|
+
following "\\r"
|
|
33
|
+
accordingly.
|
|
34
|
+
|
|
35
|
+
*This functionality does not quite work accross all terminal types which were tested. Main focus is to make
|
|
36
|
+
it work for Jupyer for now. Any feedback on
|
|
37
|
+
how to make this more generically operational is welcome.*
|
|
38
|
+
|
|
39
|
+
.. code-block:: python
|
|
40
|
+
|
|
17
41
|
crman = CRMan()
|
|
18
42
|
print( crman("\rmessage 111111"), end='' )
|
|
19
43
|
print( crman("\rmessage 2222"), end='' )
|
|
20
44
|
print( crman("\rmessage 33"), end='' )
|
|
21
45
|
print( crman("\rmessage 1\n"), end='' )
|
|
46
|
+
|
|
47
|
+
prints::
|
|
22
48
|
|
|
23
|
-
|
|
24
|
-
|
|
49
|
+
message 1
|
|
50
|
+
|
|
51
|
+
While
|
|
52
|
+
|
|
53
|
+
.. code-block:: python
|
|
54
|
+
|
|
25
55
|
print( crman("\rmessage 111111"), end='' )
|
|
26
56
|
print( crman("\rmessage 2222"), end='' )
|
|
27
57
|
print( crman("\rmessage 33"), end='' )
|
|
28
58
|
print( crman("\rmessage 1"), end='' )
|
|
29
|
-
print( crman("... and more")
|
|
59
|
+
print( crman("... and more.") )
|
|
30
60
|
|
|
31
|
-
|
|
61
|
+
prints
|
|
62
|
+
|
|
63
|
+
.. code-block:: python.
|
|
64
|
+
|
|
65
|
+
message 1... and more
|
|
32
66
|
"""
|
|
33
67
|
|
|
34
68
|
def __init__(self):
|
|
35
|
-
"""
|
|
69
|
+
"""
|
|
70
|
+
See :class:`cdxcore.crman.CRMan`
|
|
71
|
+
:meta private:
|
|
72
|
+
"""
|
|
36
73
|
self._current = ""
|
|
37
74
|
|
|
38
75
|
def __call__(self, message : str) -> str:
|
|
39
|
-
"""
|
|
40
|
-
Convert
|
|
41
|
-
|
|
76
|
+
r"""
|
|
77
|
+
Convert `message` containing "\\r" and "\\n" into a printable string which ensures
|
|
78
|
+
that a "\\r" string does not lead to printed artifacts.
|
|
79
|
+
Afterwards, the object will retain any text not terminated by "\\n".
|
|
42
80
|
|
|
43
81
|
Parameters
|
|
44
82
|
----------
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
83
|
+
message : str
|
|
84
|
+
message containing "\\r" and "\\n".
|
|
85
|
+
|
|
48
86
|
Returns
|
|
49
87
|
-------
|
|
88
|
+
Message: str
|
|
50
89
|
Printable string.
|
|
51
90
|
"""
|
|
52
91
|
if message is None:
|
|
@@ -56,6 +95,8 @@ class CRMan(object):
|
|
|
56
95
|
output = ""
|
|
57
96
|
|
|
58
97
|
# first line
|
|
98
|
+
# handle any `current` line
|
|
99
|
+
|
|
59
100
|
line = lines[0]
|
|
60
101
|
icr = line.rfind('\r')
|
|
61
102
|
if icr == -1:
|
|
@@ -63,6 +104,7 @@ class CRMan(object):
|
|
|
63
104
|
else:
|
|
64
105
|
line = line[icr+1:]
|
|
65
106
|
if len(self._current) > 0:
|
|
107
|
+
# print spaces to clear current line in terminals which do not support \33[2K'
|
|
66
108
|
output += '\r' + ' '*len(self._current) + '\r' + '\33[2K' + '\r'
|
|
67
109
|
output += line
|
|
68
110
|
self._current = line
|
|
@@ -79,6 +121,7 @@ class CRMan(object):
|
|
|
79
121
|
output += line + '\n'
|
|
80
122
|
|
|
81
123
|
# final line
|
|
124
|
+
# keep track of any residuals in `current`
|
|
82
125
|
line = lines[-1]
|
|
83
126
|
if len(line) > 0:
|
|
84
127
|
icr = line.rfind('\r')
|
|
@@ -89,13 +132,38 @@ class CRMan(object):
|
|
|
89
132
|
return output
|
|
90
133
|
|
|
91
134
|
def reset(self):
|
|
92
|
-
"""
|
|
135
|
+
"""
|
|
136
|
+
Reset object.
|
|
137
|
+
"""
|
|
93
138
|
self._current = ""
|
|
94
139
|
|
|
95
|
-
|
|
140
|
+
@property
|
|
141
|
+
def current(self) -> str:
|
|
96
142
|
"""
|
|
97
|
-
|
|
98
|
-
|
|
143
|
+
Return current string.
|
|
144
|
+
|
|
145
|
+
This is the string that ``CRMan``is currently visible to the user
|
|
146
|
+
since the last time a new line was printed.
|
|
147
|
+
"""
|
|
148
|
+
return self._current
|
|
149
|
+
|
|
150
|
+
def write(self, text : str, end : str = '', flush : bool = True, channel : Callable = None ):
|
|
151
|
+
r"""
|
|
152
|
+
Write to a ``channel``,
|
|
153
|
+
|
|
154
|
+
Writes ``text`` to ``channel`` taking into account any ``current`` lines
|
|
155
|
+
and any "\\r" and "\\n" contained in ``text``.
|
|
156
|
+
The ``end`` and ``flush`` parameters mirror those of
|
|
157
|
+
:func:`print`.
|
|
158
|
+
|
|
159
|
+
Parameters
|
|
160
|
+
----------
|
|
161
|
+
text : str
|
|
162
|
+
Text to print, containing "\\r" and "\\n".
|
|
163
|
+
end, flush : optional
|
|
164
|
+
``end`` and ``flush`` parameters mirror those of :func:`print`.
|
|
165
|
+
channel : Callable
|
|
166
|
+
Callable to output the residual text. If ``None``, the default, use :func:`print` to write to ``stdout``.
|
|
99
167
|
"""
|
|
100
168
|
text = self(text+end)
|
|
101
169
|
if channel is None:
|
|
@@ -103,3 +171,5 @@ class CRMan(object):
|
|
|
103
171
|
else:
|
|
104
172
|
channel( text, flush=flush )
|
|
105
173
|
return self
|
|
174
|
+
|
|
175
|
+
|
cdxcore/err.py
ADDED
|
@@ -0,0 +1,371 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
"""
|
|
3
|
+
Overview
|
|
4
|
+
--------
|
|
5
|
+
|
|
6
|
+
Basic error handling and reporting functions.
|
|
7
|
+
|
|
8
|
+
The main use of this module are the functions
|
|
9
|
+
:func:`cdxcore.err.verify` and :func:`cdxcore.err.warn_if`.
|
|
10
|
+
Both test some runtime condition and will either
|
|
11
|
+
raise an ``Exception`` or issue a ``Warning`` if triggered. In both cases, required string formatting is only performed
|
|
12
|
+
if the event is actually triggered.
|
|
13
|
+
|
|
14
|
+
This way we are able to write neat code which produces robust,
|
|
15
|
+
informative errors and warnings without impeding runtime performance.
|
|
16
|
+
|
|
17
|
+
Example::
|
|
18
|
+
|
|
19
|
+
from cdxcore.err import verify, warn_if
|
|
20
|
+
import numpy as np
|
|
21
|
+
|
|
22
|
+
def f( x : np.ndarray ):
|
|
23
|
+
std = np.std(x,axis=0,keepdims=True)
|
|
24
|
+
verify( np.all( std>1E-8 ), "Cannot normalize 'x' by standard deviation: standard deviations are {std}", std=std )
|
|
25
|
+
x /= std
|
|
26
|
+
|
|
27
|
+
f( np.zeros((10,10)) )
|
|
28
|
+
|
|
29
|
+
raises a :class:`RuntimeError`
|
|
30
|
+
|
|
31
|
+
.. code-block:: python
|
|
32
|
+
|
|
33
|
+
Cannot normalize 'x' by standard deviation: standard deviations are [[0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]]
|
|
34
|
+
|
|
35
|
+
For warnings, we can use ``warn_if``::
|
|
36
|
+
|
|
37
|
+
from cdxcore.err import verify, warn_if
|
|
38
|
+
import numpy as np
|
|
39
|
+
|
|
40
|
+
def f( x : np.ndarray ):
|
|
41
|
+
std = np.std(x,axis=0,keepdims=True)
|
|
42
|
+
warn_if( not np.all( std>1E-8 ), lambda : f"Normalizing 'x' by standard deviation: standard deviations are {std}" )
|
|
43
|
+
x = np.where( std<1E-8, 0., x/np.where( std<1E-8, 1., std ) )
|
|
44
|
+
|
|
45
|
+
f( np.zeros((10,10)) )
|
|
46
|
+
|
|
47
|
+
issues a warning::
|
|
48
|
+
|
|
49
|
+
RuntimeWarning: Normalizing 'x' by standard deviation: standard deviations are [[0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]]
|
|
50
|
+
warn_if( not np.all( std>1E-8 ), "Normalizing 'x' by standard deviation: standard deviations are {std}", std=std )
|
|
51
|
+
|
|
52
|
+
Note that though we used two different approaches for message formatting, the the error messages in both cases
|
|
53
|
+
are only formatted if the condition in ``verify`` is not met.
|
|
54
|
+
|
|
55
|
+
Import
|
|
56
|
+
------
|
|
57
|
+
.. code-block:: python
|
|
58
|
+
|
|
59
|
+
from cdxcore.err import verify, warn_if, error, warn
|
|
60
|
+
|
|
61
|
+
Documentation
|
|
62
|
+
-------------
|
|
63
|
+
"""
|
|
64
|
+
|
|
65
|
+
import warnings as warnings
|
|
66
|
+
import os as os
|
|
67
|
+
from collections.abc import Callable
|
|
68
|
+
|
|
69
|
+
def _fmt( text : str, args = None, kwargs = None, f : Callable = None ) -> str:
|
|
70
|
+
""" Utility function. See [cdxcore.err.fmt][]() . 'f' not currently used.':meta private: """
|
|
71
|
+
args = None if not args is None and len(args) == 0 else args
|
|
72
|
+
kwargs = None if not kwargs is None and len(kwargs) == 0 else kwargs
|
|
73
|
+
|
|
74
|
+
# callable
|
|
75
|
+
if not isinstance(text, str) and callable(text):
|
|
76
|
+
# handle callable
|
|
77
|
+
# we pass args and kwargs as provided
|
|
78
|
+
if not args is None:
|
|
79
|
+
if not kwargs is None:
|
|
80
|
+
return text(* args, ** kwargs)
|
|
81
|
+
else:
|
|
82
|
+
return text(*args)
|
|
83
|
+
elif not kwargs is None :
|
|
84
|
+
return text(**kwargs)
|
|
85
|
+
return text()
|
|
86
|
+
text = str(text)
|
|
87
|
+
|
|
88
|
+
# text
|
|
89
|
+
# C-style positional parameters first
|
|
90
|
+
if not args is None:
|
|
91
|
+
# args are only valid for c-style %d, %s
|
|
92
|
+
if not kwargs is None:
|
|
93
|
+
raise ValueError("Cannot specify both 'args' and 'kwargs'", text)
|
|
94
|
+
try:
|
|
95
|
+
return text % tuple(args)
|
|
96
|
+
except TypeError as e:
|
|
97
|
+
raise TypeError(e, text, args)
|
|
98
|
+
# text
|
|
99
|
+
# python 2 and 3 mode
|
|
100
|
+
kwargs = dict() if kwargs is None else kwargs
|
|
101
|
+
if text.find("%(") == -1:
|
|
102
|
+
return text.format(**kwargs)
|
|
103
|
+
else:
|
|
104
|
+
return text % kwargs
|
|
105
|
+
|
|
106
|
+
def fmt(text : str|Callable, * args, ** kwargs) -> str:
|
|
107
|
+
"""
|
|
108
|
+
Basic tool for delayed string formatting.
|
|
109
|
+
|
|
110
|
+
The main use case is that formatting is not executed until this function is called,
|
|
111
|
+
hence potential error messages are not generated until an error actually occurs.
|
|
112
|
+
See, for example, :func:`cdxcore.err.verify`.
|
|
113
|
+
|
|
114
|
+
The follwing example illustrates all four supported modi operandi::
|
|
115
|
+
|
|
116
|
+
from cdxcore.err import fmt
|
|
117
|
+
one = 1
|
|
118
|
+
fmt(lambda : f"one {one:d}) # using a lambda function
|
|
119
|
+
fmt("one {one:d}", one=one) # using python 3 string.format()
|
|
120
|
+
fmt("one %{one}ld", one=one) # using python 2 style
|
|
121
|
+
fmt("one %ld", one) # using c-style
|
|
122
|
+
|
|
123
|
+
As shown, do not use f-strings directly as they are immediately executed in the scope they are typed in
|
|
124
|
+
but wrap them with a ``lambda`` function.
|
|
125
|
+
|
|
126
|
+
Parameters
|
|
127
|
+
----------
|
|
128
|
+
text : str | Callable
|
|
129
|
+
Error text which may contain one of the following string formatting patterns:
|
|
130
|
+
|
|
131
|
+
* Python 3 ```{parameter:d}```, in which case ``message.fmt(kwargs)`` for :meth:`str.format` is used
|
|
132
|
+
to obtain the output message.
|
|
133
|
+
|
|
134
|
+
* Python 2 ```%(parameter)d``` in which case ``message % kwargs`` is used to obtain the output message.
|
|
135
|
+
|
|
136
|
+
* Classic C-stype ```%d, %s, %f``` in which case ``message % args`` is used to obtain the output message.
|
|
137
|
+
|
|
138
|
+
* If ``message`` is a ``Callable`` such as a ``lambda`` function, then ``message( * args, ** kwargs )``
|
|
139
|
+
is called to obtain the output message.
|
|
140
|
+
|
|
141
|
+
A common use case is using an f-string wrapped in a ``lambda`` function; see example above.
|
|
142
|
+
|
|
143
|
+
* args, ** kwargs:
|
|
144
|
+
See above
|
|
145
|
+
|
|
146
|
+
Returns
|
|
147
|
+
-------
|
|
148
|
+
Text : str
|
|
149
|
+
The formatted message.
|
|
150
|
+
"""
|
|
151
|
+
return _fmt(text=text,args=args,kwargs=kwargs,f=fmt)
|
|
152
|
+
|
|
153
|
+
def error( text : str|Callable, *args, exception : Exception = RuntimeError, **kwargs ):
|
|
154
|
+
"""
|
|
155
|
+
Raise an exception with string formatting.
|
|
156
|
+
|
|
157
|
+
See also :func:`cdxcore.err.fmt` for formatting comments.
|
|
158
|
+
The point of this function is to have an interface which is consistent with
|
|
159
|
+
:func:`cdxcore.err.verify`.
|
|
160
|
+
|
|
161
|
+
Examples::
|
|
162
|
+
|
|
163
|
+
from cdxcore.err import error
|
|
164
|
+
one = 1
|
|
165
|
+
error(lambda : f"one {one:d}") # wrapped f-string
|
|
166
|
+
error("one {one:d}", one=one) # using python 3 string.format()
|
|
167
|
+
error("one %{one}ld", one=one) # using python 2 style
|
|
168
|
+
error("one %ld", one) # using c-style
|
|
169
|
+
|
|
170
|
+
As shown, do not use f-strings directly as they are immediately executed in the scope they are typed in
|
|
171
|
+
but wrap them with a ``lambda`` function.
|
|
172
|
+
|
|
173
|
+
Parameters
|
|
174
|
+
----------
|
|
175
|
+
text : str | Callable
|
|
176
|
+
Error text which may contain one of the following string formatting patterns:
|
|
177
|
+
|
|
178
|
+
* Python 3 ```{parameter:d}```, in which case ``message.fmt(kwargs)`` for :meth:`str.format` is used
|
|
179
|
+
to obtain the output message.
|
|
180
|
+
|
|
181
|
+
* Python 2 ```%(parameter)d``` in which case ``message % kwargs`` is used to obtain the output message.
|
|
182
|
+
|
|
183
|
+
* Classic C-stype ```%d, %s, %f``` in which case ``message % args`` is used to obtain the output message.
|
|
184
|
+
|
|
185
|
+
* If ``message`` is a ``Callable`` such as a ``lambda`` function, then ``message( * args, ** kwargs )``
|
|
186
|
+
is called to obtain the output message.
|
|
187
|
+
|
|
188
|
+
A common use case is using an f-string wrapped in a ``lambda`` function; see example above.
|
|
189
|
+
|
|
190
|
+
exception : Exception, optional
|
|
191
|
+
Which type of exception to raise. Defaults to :class:`RuntimeError`.
|
|
192
|
+
|
|
193
|
+
* args, ** kwargs:
|
|
194
|
+
See above
|
|
195
|
+
|
|
196
|
+
Raises
|
|
197
|
+
------
|
|
198
|
+
exception : exception
|
|
199
|
+
"""
|
|
200
|
+
text = _fmt(text=text,args=args,kwargs=kwargs,f=error)
|
|
201
|
+
raise exception( text )
|
|
202
|
+
|
|
203
|
+
def verify( cond : bool, text : str|Callable, *args, exception : Exception = RuntimeError, **kwargs ):
|
|
204
|
+
"""
|
|
205
|
+
Raise an exception using delayed error string formatting if a condition is not met.
|
|
206
|
+
|
|
207
|
+
The point of this function is to only format an error message if a condition ``cond`` is not met
|
|
208
|
+
and an error is to be raised.
|
|
209
|
+
|
|
210
|
+
Examples::
|
|
211
|
+
|
|
212
|
+
from cdxcore.err import verify
|
|
213
|
+
one = 1
|
|
214
|
+
good = False # some condition
|
|
215
|
+
verify(good, lambda : f"one {one:d}") # wrapped f-string
|
|
216
|
+
verify(good, "one {one:d}", one=one) # using python 3 string.format()
|
|
217
|
+
verify(good, "one %{one}ld", one=one) # using python 2 style
|
|
218
|
+
verify(good, "one %ld", one) # using c-style
|
|
219
|
+
|
|
220
|
+
As shown, do not use f-strings directly as they are immediately executed in the scope they are typed in
|
|
221
|
+
but wrap them with a ``lambda`` function.
|
|
222
|
+
|
|
223
|
+
Parameters
|
|
224
|
+
----------
|
|
225
|
+
cond : bool
|
|
226
|
+
Condition to test.
|
|
227
|
+
|
|
228
|
+
text : str | Callable
|
|
229
|
+
Error text which may contain one of the following string formatting patterns:
|
|
230
|
+
|
|
231
|
+
* Python 3 ```{parameter:d}```, in which case ``message.fmt(kwargs)`` for :meth:`str.format` is used
|
|
232
|
+
to obtain the output message.
|
|
233
|
+
|
|
234
|
+
* Python 2 ```%(parameter)d``` in which case ``message % kwargs`` is used to obtain the output message.
|
|
235
|
+
|
|
236
|
+
* Classic C-stype ```%d, %s, %f``` in which case ``message % args`` is used to obtain the output message.
|
|
237
|
+
|
|
238
|
+
* If ``message`` is a ``Callable`` such as a ``lambda`` function, then ``message( * args, ** kwargs )``
|
|
239
|
+
is called to obtain the output message.
|
|
240
|
+
|
|
241
|
+
A common use case is using an f-string wrapped in a ``lambda`` function; see example above.
|
|
242
|
+
|
|
243
|
+
exception : Exception, optiona
|
|
244
|
+
Which type of exception to raise. Defaults to :class:`RuntimeError`.
|
|
245
|
+
|
|
246
|
+
* args, ** kwargs:
|
|
247
|
+
See above
|
|
248
|
+
|
|
249
|
+
Raises
|
|
250
|
+
------
|
|
251
|
+
exception : exception
|
|
252
|
+
"""
|
|
253
|
+
if not cond:
|
|
254
|
+
text = _fmt(text=text,args=args,kwargs=kwargs,f=verify)
|
|
255
|
+
raise exception( fmt(text, * args, ** kwargs) )
|
|
256
|
+
|
|
257
|
+
_warn_skips = (os.path.dirname(__file__),)
|
|
258
|
+
|
|
259
|
+
def warn( text : str|Callable, *args, warning = RuntimeWarning, stack_level : int = 1, **kwargs ):
|
|
260
|
+
"""
|
|
261
|
+
Issue a warning.
|
|
262
|
+
|
|
263
|
+
The point of this function is to have an interface consistent with :func:`cdxcore.err.warn_if`.
|
|
264
|
+
|
|
265
|
+
Examples::
|
|
266
|
+
|
|
267
|
+
from cdxcore.err import warn
|
|
268
|
+
one = 1
|
|
269
|
+
warn(lambda : f"one {one:d}") # wrapped f-string
|
|
270
|
+
warn("one {one:d}", one=one) # using python 3 string.format()
|
|
271
|
+
warn("one %{one}ld", one=one) # using python 2 style
|
|
272
|
+
warn("one %ld", one) # using c-style
|
|
273
|
+
|
|
274
|
+
As shown, do not use f-strings directly as they are immediately executed in the scope they are typed in
|
|
275
|
+
but wrap them with a ``lambda`` function.
|
|
276
|
+
|
|
277
|
+
Parameters
|
|
278
|
+
----------
|
|
279
|
+
text : str | Callable
|
|
280
|
+
Error text which may contain one of the following string formatting patterns:
|
|
281
|
+
|
|
282
|
+
* Python 3 ```{parameter:d}```, in which case ``message.fmt(kwargs)`` for :meth:`str.format` is used
|
|
283
|
+
to obtain the output message.
|
|
284
|
+
|
|
285
|
+
* Python 2 ```%(parameter)d``` in which case ``message % kwargs`` is used to obtain the output message.
|
|
286
|
+
|
|
287
|
+
* Classic C-stype ```%d, %s, %f``` in which case ``message % args`` is used to obtain the output message.
|
|
288
|
+
|
|
289
|
+
* If ``message`` is a ``Callable`` such as a ``lambda`` function, then ``message( * args, ** kwargs )``
|
|
290
|
+
is called to obtain the output message.
|
|
291
|
+
|
|
292
|
+
A common use case is using an f-string wrapped in a ``lambda`` function; see example above.
|
|
293
|
+
|
|
294
|
+
warning : optional
|
|
295
|
+
Which type of warning to issue.
|
|
296
|
+
This corresponds to the ``category`` parameter for :func:`warnings.warn`.
|
|
297
|
+
Default is :class:`RuntimeWarning`.
|
|
298
|
+
|
|
299
|
+
stack_level : int, optional
|
|
300
|
+
What stack to report; see :func:`warnings.warn`.
|
|
301
|
+
Default is 1, which means ``warn`` itself is not reported as part of the stack trace.
|
|
302
|
+
|
|
303
|
+
* args, ** kwargs:
|
|
304
|
+
See above
|
|
305
|
+
"""
|
|
306
|
+
warnings.warn( message=text,
|
|
307
|
+
category=warning,
|
|
308
|
+
stacklevel=stack_level,
|
|
309
|
+
skip_file_prefixes=_warn_skips )
|
|
310
|
+
|
|
311
|
+
def warn_if( cond : bool, text : str|Callable, *args, warning = RuntimeWarning, stack_level : int = 1, **kwargs ):
|
|
312
|
+
"""
|
|
313
|
+
Issue a warning with delayed string formatting if a condition is met.
|
|
314
|
+
|
|
315
|
+
The point of this function is to only format an error message if a condition ``cond`` is met
|
|
316
|
+
and a warning is to be issued.
|
|
317
|
+
|
|
318
|
+
Examples::
|
|
319
|
+
|
|
320
|
+
from cdxcore.err import warn_if
|
|
321
|
+
one = 1
|
|
322
|
+
bad = True # some conditon
|
|
323
|
+
warn_if(bad,lambda : f"one {one:d}") # wrapped f-string
|
|
324
|
+
warn_if(bad,"one {one:d}", one=one) # using python 3 string.format()
|
|
325
|
+
warn_if(bad,"one %{one}ld", one=one) # using python 2 style
|
|
326
|
+
warn_if(bad,"one %ld", one) # using c-style
|
|
327
|
+
|
|
328
|
+
As shown, do not use f-strings directly as they are immediately executed in the scope they are typed in
|
|
329
|
+
but wrap them with a ``lambda`` function.
|
|
330
|
+
|
|
331
|
+
Parameters
|
|
332
|
+
----------
|
|
333
|
+
cond : bool
|
|
334
|
+
Condition to test.
|
|
335
|
+
|
|
336
|
+
text : str | Callable
|
|
337
|
+
Error text which may contain one of the following string formatting patterns:
|
|
338
|
+
|
|
339
|
+
* Python 3 ```{parameter:d}```, in which case ``message.fmt(kwargs)`` for :meth:`str.format` is used
|
|
340
|
+
to obtain the output message.
|
|
341
|
+
|
|
342
|
+
* Python 2 ```%(parameter)d``` in which case ``message % kwargs`` is used to obtain the output message.
|
|
343
|
+
|
|
344
|
+
* Classic C-stype ```%d, %s, %f``` in which case ``message % args`` is used to obtain the output message.
|
|
345
|
+
|
|
346
|
+
* If ``message`` is a ``Callable`` such as a ``lambda`` function, then ``message( * args, ** kwargs )``
|
|
347
|
+
is called to obtain the output message.
|
|
348
|
+
|
|
349
|
+
A common use case is using an f-string wrapped in a ``lambda`` function; see example above.
|
|
350
|
+
|
|
351
|
+
warning : optional
|
|
352
|
+
Which type of warning to issue.
|
|
353
|
+
This corresponds to the ``category`` parameter for :func:`warnings.warn`.
|
|
354
|
+
Default is :class:`RuntimeWarning`.
|
|
355
|
+
|
|
356
|
+
stack_level : int, optional
|
|
357
|
+
What stack to report; see :func:`warnings.warn`.
|
|
358
|
+
Default is 1, which means ``warn`` itself is not reported as part of the stack trace.
|
|
359
|
+
|
|
360
|
+
* args, ** kwargs:
|
|
361
|
+
See above
|
|
362
|
+
"""
|
|
363
|
+
if cond:
|
|
364
|
+
text = _fmt(text=text,args=args,kwargs=kwargs,f=warn_if)
|
|
365
|
+
warnings.warn( message=text,
|
|
366
|
+
category=warning,
|
|
367
|
+
stacklevel=stack_level,
|
|
368
|
+
skip_file_prefixes=_warn_skips )
|
|
369
|
+
|
|
370
|
+
|
|
371
|
+
|