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/crman.py ADDED
@@ -0,0 +1,105 @@
1
+ """
2
+ crman
3
+ Managing \r updates
4
+ Hans Buehler 2023
5
+ """
6
+
7
+ from collections.abc import Callable
8
+ from .logger import Logger#
9
+ _log = Logger(__file__)
10
+
11
+ class CRMan(object):
12
+ """
13
+ Carraige Return ('\r') manager.
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.
16
+
17
+ crman = CRMan()
18
+ print( crman("\rmessage 111111"), end='' )
19
+ print( crman("\rmessage 2222"), end='' )
20
+ print( crman("\rmessage 33"), end='' )
21
+ print( crman("\rmessage 1\n"), end='' )
22
+
23
+ --> message 1
24
+
25
+ print( crman("\rmessage 111111"), end='' )
26
+ print( crman("\rmessage 2222"), end='' )
27
+ print( crman("\rmessage 33"), end='' )
28
+ print( crman("\rmessage 1"), end='' )
29
+ print( crman("... and more"), end='' )
30
+
31
+ --> message 1... and more
32
+ """
33
+
34
+ def __init__(self):
35
+ """ See help(CRMan) """
36
+ self._current = ""
37
+
38
+ def __call__(self, message : str) -> str:
39
+ """
40
+ Convert 'message' containing '\r' and '\n' into a printable string which ensures that '\r' string do not lead to printed artifacts.
41
+ Afterwards, the object will retain any text not terminated by '\n'
42
+
43
+ Parameters
44
+ ----------
45
+ message : str
46
+ message containing \r and \n.
47
+
48
+ Returns
49
+ -------
50
+ Printable string.
51
+ """
52
+ if message is None:
53
+ return
54
+
55
+ lines = message.split('\n')
56
+ output = ""
57
+
58
+ # first line
59
+ line = lines[0]
60
+ icr = line.rfind('\r')
61
+ if icr == -1:
62
+ line = self._current + line
63
+ else:
64
+ line = line[icr+1:]
65
+ if len(self._current) > 0:
66
+ output += '\r' + ' '*len(self._current) + '\r' + '\33[2K' + '\r'
67
+ output += line
68
+ self._current = line
69
+
70
+ if len(lines) > 1:
71
+ output += '\n'
72
+ self._current = ""
73
+
74
+ # intermediate lines
75
+ for line in lines[1:-1]:
76
+ # support multiple '\r', but in practise only the last one will be printed
77
+ icr = line.rfind('\r')
78
+ line = line if icr==-1 else line[icr+1:]
79
+ output += line + '\n'
80
+
81
+ # final line
82
+ line = lines[-1]
83
+ if len(line) > 0:
84
+ icr = line.rfind('\r')
85
+ line = line if icr==-1 else line[icr+1:]
86
+ output += line
87
+ self._current += line
88
+
89
+ return output
90
+
91
+ def reset(self):
92
+ """ Reset object """
93
+ self._current = ""
94
+
95
+ def write(self, text, end='', flush=True, channel : Callable = None ):
96
+ """
97
+ Write to stdout using \r and \n translations.
98
+ The 'end' and 'flush' parameters mirror those of print()
99
+ """
100
+ text = self(text+end)
101
+ if channel is None:
102
+ print( text, end='', flush=flush )
103
+ else:
104
+ channel( text, flush=flush )
105
+ return self
cdxcore/deferred.py ADDED
@@ -0,0 +1,220 @@
1
+ """
2
+ deferred
3
+ Deferred action wrapping
4
+ Hans Buehler 2022
5
+ """
6
+ from .util import fmt_list
7
+ from .logger import Logger
8
+ _log = Logger(__file__)
9
+
10
+ class Deferred(object):
11
+ """
12
+ Defer an action such as function calls, item access, and attribute access to a later stage.
13
+ This is used in dynaplot:
14
+
15
+ fig = figure() # the DynanicFig returned by figure() is derived from Deferred
16
+ ax = fig.add_subplot() # Deferred call for add_subplot()
17
+ lns = ax.plot( x, y )[0] # This is a deferred call: plot() and then [] are iteratively deferred.
18
+ fig.render() # renders the figure and executes first plot() then [0]
19
+ lns.set_ydata( y2 ) # we can now access the resulting Line2D object via the Deferred wrapper
20
+ fig.render() # update graph
21
+
22
+ Typically, a code user would create a class which can defer actions by deriving from this class.
23
+ For example assume there is a class A which we want to defer.
24
+
25
+ class A(object):
26
+ def __init__(self, x):
27
+ self.x = x
28
+ def a_function(self, y):
29
+ return self.x * y
30
+ def __getitem__(self, y):
31
+ return self.x * y
32
+ def __call__(self, y):
33
+ return self.x * y
34
+
35
+ class DeferredA( Deferred ):
36
+ def __init__( self ):
37
+ Deferred.__init__( info = "A" ) # give me a name
38
+ def create( self, *kargs_A, **kwargs_A ):
39
+ # deferred creation
40
+ a = A(*kargs_A,**kwargs_A)
41
+ self._dereference( a )
42
+
43
+ deferred_A = DeferredA()
44
+ fx = deferred_A.a_function( 1. ) # will defer call to 'a_function'
45
+ ix = deferred_A[2.] # will defer index access
46
+ cl = deferred_A(3.) # will defer __call__
47
+
48
+ deferred_A.create( 1, x=2 ) # actually create A
49
+ # This will trigger execution of all deferred actions
50
+ # in order.
51
+
52
+ Not that Deferred() works iteratively, e.g. all values returned from a function call, __call__, or __getitem__ are themselves
53
+ deferred automatically. Deferred() is also able to defer item and attribute assignments.
54
+
55
+ See cdxbasics.dynaplot.DynamicFig as an example.
56
+ """
57
+
58
+ TYPE_SELF = 0
59
+ TYPE_CALL = 1
60
+ TYPE_ITEM = 2
61
+ TYPE_SET_ITEM = 4
62
+ TYPE_ATTR = 3
63
+ TYPE_SET_ATTR = 5
64
+
65
+ TYPES = [ TYPE_SELF, TYPE_CALL, TYPE_ITEM, TYPE_SET_ITEM, TYPE_ATTR, TYPE_SET_ATTR ]
66
+
67
+ def __init__(self, info : str, *, typ : int = TYPE_SELF, ref = None):
68
+ """
69
+ Initialize a deferred action.
70
+ Typically, a code user would derive from this class and initialize __init__
71
+ with only the first 'info' argument giving their class a good name for error
72
+ messages.
73
+ See cdxbasics.dynaplot.DynamicFig as an example
74
+
75
+ Parameters
76
+ ----------
77
+ info : str
78
+ Description of the underlying deferred parent operation/class for error messages.
79
+ typ : int
80
+ one of the TYPE_ values describing the type of action to be deferred.
81
+ ref :
82
+ argument for the action, e.g.
83
+ TYPE_SELF: None
84
+ TYPE_CALL: tuple of (argc, argv)
85
+ TYPE_ITEM: key for []
86
+ TYPE_ATTR: string name of the attribute
87
+ """
88
+ _log.verify( typ in Deferred.TYPES, "'type' must be in %s, found %ld", fmt_list(Deferred.TYPES), typ )
89
+
90
+ if typ == Deferred.TYPE_CALL:
91
+ assert isinstance(ref,tuple) and len(ref) == 2, "Internal error: tuple of size 2 expected. Found %s" % str(ref)
92
+ self._ref = ( list(ref[0]), dict(ref[1]) )
93
+ elif typ == Deferred.TYPE_ITEM:
94
+ self._ref = ref
95
+ elif typ == Deferred.TYPE_SET_ITEM:
96
+ assert isinstance(ref, tuple) and len(ref) == 2, "Internal error: tuple of size 2 expected. Found %s" % str(ref)
97
+ self._ref = ref
98
+ elif typ == Deferred.TYPE_ATTR:
99
+ self._ref = str(ref)
100
+ elif typ == Deferred.TYPE_SET_ATTR:
101
+ assert isinstance(ref, tuple) and len(ref) == 2, "Internal error: tuple of size 2 expected. Found %s" % str(ref)
102
+ self._ref = ref
103
+ else:
104
+ _log.verify( ref is None, "'ref' must be none for TYPE_SELF")
105
+ self._ref = None
106
+
107
+ self._type = typ
108
+ self._info = info
109
+ self._live = None
110
+ self._was_executed = False
111
+ self._caught = []
112
+
113
+ @property
114
+ def cdx_deferred_result(self):
115
+ """ Returns the result of the deferred action """
116
+ if not self._was_executed: _log.throw( "Deferred action %s has not been executed yet", self._info )
117
+ return self._live
118
+
119
+ def _dereference(self, owner):
120
+ """
121
+ Execute deferred action with 'owner' as the object the action is to be performed upon.
122
+ If the current type is TYPE_SELF then the result is simply 'owner'
123
+ """
124
+ # execute the deferred action
125
+ if self._was_executed:
126
+ _log.throw("Deferred action %s has already been executed", self._info )
127
+
128
+ if self._type == Deferred.TYPE_CALL:
129
+ try:
130
+ live = owner( *self._ref[0], **self._ref[1] )
131
+ except Exception as e:
132
+ _log.error("Error resolving deferred call to '%s': %s; "
133
+ "positional arguments: %s; "
134
+ "keyword arguments: %s", self._info, e, str(self._ref[0])[:100], str(self._ref[1])[:100])
135
+ raise e
136
+
137
+ elif self._type == Deferred.TYPE_ITEM:
138
+ try:
139
+ live = owner[ self._ref ]
140
+ except Exception as e:
141
+ _log.error("Error resolving deferred item access to '%s', trying to access item '%s': %s", self._info, str(self._ref)[:100], e)
142
+ raise e
143
+
144
+ elif self._type == Deferred.TYPE_SET_ITEM:
145
+ try:
146
+ owner[ self._ref[0] ] = self._ref[1]
147
+ live = None
148
+ except Exception as e:
149
+ _log.error("Error resolving deferred item assignment for '%s': tried to set item '%s' to '%s': %s", self._info, str(self._ref[0])[:100], str(self._ref[1])[:100], e)
150
+ raise e
151
+
152
+ elif self._type == Deferred.TYPE_ATTR:
153
+ try:
154
+ live = getattr( owner, self._ref )
155
+ except Exception as e:
156
+ _log.error("Error resolving deferred attribute access to '%s', trying to read attribute '%s': %s", self._info, str(self._ref)[:100], e)
157
+ raise e
158
+
159
+ elif self._type == Deferred.TYPE_SET_ATTR:
160
+ try:
161
+ owner.__setattr__( self._ref[0], self._ref[1] )
162
+ live = None
163
+ except Exception as e:
164
+ _log.error("Error resolving deferred attribute assignment to '%s': tried to set attribute '%s' to '%s': %s", self._info, str(self._ref[0])[:100], str(self._ref[1])[:100], e)
165
+ raise e
166
+
167
+ else:
168
+ # TYPE_SELF
169
+ live = owner
170
+
171
+ self._live = live
172
+ self._was_executed = True
173
+
174
+ # execute all deferred calls for this object
175
+ for catch in self._caught:
176
+ catch._dereference( live )
177
+ self._caught = None
178
+
179
+ def __call__(self, *kargs, **kwargs):
180
+ """ Deferred call () """
181
+ if self._was_executed:
182
+ return self.cdx_deferred_result(*kargs, **kwargs)
183
+ deferred = Deferred( typ=Deferred.TYPE_CALL, ref=(kargs,kwargs), info="%s(%s)" % (self._info, "..." if len(kwargs)+len(kargs)>0 else "") )
184
+ self._caught.append( deferred )
185
+ return deferred
186
+
187
+ def __getitem__(self, key):
188
+ """ Deferred reading item [] """
189
+ if self._was_executed:
190
+ return self.cdx_deferred_result[key]
191
+ deferred = Deferred( typ=Deferred.TYPE_ITEM, ref=key, info="%s[%s]" % (self._info, str(key)))
192
+ self._caught.append( deferred )
193
+ return deferred
194
+
195
+ def __setitem__(self, key, value):
196
+ """ Deferred item assignment [] """
197
+ if self._was_executed:
198
+ self.cdx_deferred_result[key] = value
199
+ else:
200
+ deferred = Deferred( typ=Deferred.TYPE_SET_ITEM, ref=(key, value), info="%s[%s] = %s" % (self._info, str(key), str(value)[:100]))
201
+ self._caught.append( deferred )
202
+
203
+ def __getattr__(self, attr):
204
+ """ Deferred attribute access """
205
+ attr = str(attr)
206
+ if self._was_executed:
207
+ return getattr(self.cdx_deferred_result,attr)
208
+ deferred = Deferred( typ=Deferred.TYPE_ATTR, ref=attr, info="%s.%s" % (self._info,attr))
209
+ self._caught.append( deferred )
210
+ return deferred
211
+
212
+ def __setattr_(self, attr, value):
213
+ """ Deferred attribute access """
214
+ attr = str(attr)
215
+ if self._was_executed:
216
+ self.cdx_deferred_result.__setattr__(attr, value)
217
+ else:
218
+ deferred = Deferred( typ=Deferred.TYPE_SET_ATTR, ref=(attr,value), info="%s.%s = %s" % (self._info,attr,str(value)[:100]))
219
+ self._caught.append( deferred )
220
+