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/logger.py
DELETED
|
@@ -1,319 +0,0 @@
|
|
|
1
|
-
"""
|
|
2
|
-
Basic log file logic
|
|
3
|
-
"""
|
|
4
|
-
|
|
5
|
-
from .util import _fmt, prnt, write
|
|
6
|
-
import sys as sys
|
|
7
|
-
import logging as logging
|
|
8
|
-
import traceback as traceback
|
|
9
|
-
import datetime as datetime
|
|
10
|
-
|
|
11
|
-
_prnt = prnt
|
|
12
|
-
_write = write
|
|
13
|
-
|
|
14
|
-
class Logger(object):
|
|
15
|
-
"""
|
|
16
|
-
Simple utility object to decorate loggers, plus:
|
|
17
|
-
- infor, warning, () also accept named argument formatting ie "The error is %(message)self" instead of positional %
|
|
18
|
-
- added Exceptn function which logs an error before returning an exception.
|
|
19
|
-
It also make sure an exception is only tracked once.
|
|
20
|
-
The point of this class is to be able to write
|
|
21
|
-
import base.logger as logger
|
|
22
|
-
_log = logger.getLogger(__file__)
|
|
23
|
-
...
|
|
24
|
-
_log.verify( some_condition_we_want_met, "Error: cannot find %s", message)
|
|
25
|
-
and it will keep a log of that exception.
|
|
26
|
-
|
|
27
|
-
Exceptions independent of logging level
|
|
28
|
-
|
|
29
|
-
verify( cond, text, *args, **kwargs )
|
|
30
|
-
If cond is not met, raise an exception with util.fmt( text, *args, **kwargs )
|
|
31
|
-
|
|
32
|
-
throw_if(cond, text, *args, **kwargs )
|
|
33
|
-
If cond is met, raise an exception with util.fmt( text, *args, **kwargs )
|
|
34
|
-
|
|
35
|
-
throw( text, *args, **kwargs )
|
|
36
|
-
Raise an exception with fmt( text, *args, **kwargs )
|
|
37
|
-
|
|
38
|
-
Unconditional logging
|
|
39
|
-
|
|
40
|
-
debug( text, *args, **kwargs )
|
|
41
|
-
info( text, *args, **kwargs )
|
|
42
|
-
warning( text, *args, **kwargs )
|
|
43
|
-
error( text, *args, **kwargs )
|
|
44
|
-
debug( text, *args, **kwargs )
|
|
45
|
-
|
|
46
|
-
Conditional logging functions, verify version
|
|
47
|
-
|
|
48
|
-
verify_debug( cond, text, *args, **kwargs )
|
|
49
|
-
verify_info( cond, text, *args, **kwargs )
|
|
50
|
-
verify_warning( cond, text, *args, **kwargs )
|
|
51
|
-
|
|
52
|
-
verify( cond, text, *args, **kwargs )
|
|
53
|
-
|
|
54
|
-
Conditional logging functions, if version
|
|
55
|
-
|
|
56
|
-
debug_if( text, *args, **kwargs )
|
|
57
|
-
info_if( text, *args, **kwargs )
|
|
58
|
-
warning_if( text, *args, **kwargs )
|
|
59
|
-
|
|
60
|
-
throw_if( text, *args, **kwargs )
|
|
61
|
-
|
|
62
|
-
prnt_if( text, *args, **kwargs ) # with EOL
|
|
63
|
-
write_if( text, *args, **kwargs ) # without EOL
|
|
64
|
-
"""
|
|
65
|
-
|
|
66
|
-
CRITICAL = logging.CRITICAL
|
|
67
|
-
ERROR = logging.ERROR
|
|
68
|
-
WARNING = logging.WARNING
|
|
69
|
-
INFO = logging.INFO
|
|
70
|
-
DEBUG = logging.DEBUG
|
|
71
|
-
NOTSET = logging.NOTSET
|
|
72
|
-
|
|
73
|
-
def __init__(self,topic : str):
|
|
74
|
-
assert topic !="", "Logger cannot be empty"
|
|
75
|
-
setupAppLogging() # ensure system is ready
|
|
76
|
-
i = topic.rfind('/')
|
|
77
|
-
if i == -1:
|
|
78
|
-
i = topic.rfind('\\')
|
|
79
|
-
if i != -1 and i<len(topic)-1:
|
|
80
|
-
topic = topic[i+1:]
|
|
81
|
-
self.logger = logging.getLogger(topic)
|
|
82
|
-
|
|
83
|
-
# Exception support
|
|
84
|
-
# -----------------
|
|
85
|
-
|
|
86
|
-
class LogException(Exception):
|
|
87
|
-
""" Placeholder exception class we use to identify our own exceptions """
|
|
88
|
-
|
|
89
|
-
def __init__(self,text : str):
|
|
90
|
-
Exception.__init__(self,text)
|
|
91
|
-
|
|
92
|
-
def Exceptn(self, text : str, *args, **kwargs ):
|
|
93
|
-
"""
|
|
94
|
-
Returns an exception object with 'text' % kwargs and stores an 'error' message
|
|
95
|
-
If an exception is present, it will be printed, too.
|
|
96
|
-
If the base logger logs 'debug' information, the call stack will be printed as well
|
|
97
|
-
|
|
98
|
-
Usage:
|
|
99
|
-
raise _log.Exceptn("Something happened")
|
|
100
|
-
"""
|
|
101
|
-
text = _fmt(text,args,kwargs)
|
|
102
|
-
(typ, val, trc) = sys.exc_info()
|
|
103
|
-
|
|
104
|
-
# are we already throwing our own exception?
|
|
105
|
-
if typ is Logger.LogException:
|
|
106
|
-
return val # --> already logged --> keep raising the exception but don't do anything
|
|
107
|
-
|
|
108
|
-
# new exception?
|
|
109
|
-
if typ is None:
|
|
110
|
-
assert val is None and trc is None, "*** Internal error"
|
|
111
|
-
self.error( text )
|
|
112
|
-
return Logger.LogException("*** LogException: " + text)
|
|
113
|
-
|
|
114
|
-
# another exception is being thrown.
|
|
115
|
-
# we re-cast this as one of our own.
|
|
116
|
-
if text[-1] == ".":
|
|
117
|
-
text = text + " " + str(val)
|
|
118
|
-
else:
|
|
119
|
-
text = text + ". " + str(val)
|
|
120
|
-
|
|
121
|
-
# in debug, add trace information
|
|
122
|
-
if self.logger.getEffectiveLevel() <= logging.WARNING:
|
|
123
|
-
text = text.rstrip()
|
|
124
|
-
txt = traceback.format_exception(typ,val,trc,limit = 100)
|
|
125
|
-
for t in txt:
|
|
126
|
-
text += "\n " + t[:-1]
|
|
127
|
-
self.error( text )
|
|
128
|
-
|
|
129
|
-
# return an exception with the corresponding text
|
|
130
|
-
return Logger.LogException("*** LogException: " + text)
|
|
131
|
-
|
|
132
|
-
# logging() replacemets
|
|
133
|
-
# ---------------------
|
|
134
|
-
|
|
135
|
-
def debug(self, text, *args, **kwargs ):
|
|
136
|
-
""" Reports debug information with new style formatting """
|
|
137
|
-
if self.logger.getEffectiveLevel() <= logging.DEBUG and len(text) > 0:
|
|
138
|
-
self.logger.debug(_fmt(text,args,kwargs))
|
|
139
|
-
|
|
140
|
-
def info(self, text, *args, **kwargs ):
|
|
141
|
-
""" Reports information with new style formatting """
|
|
142
|
-
if self.logger.getEffectiveLevel() <= logging.INFO and len(text) > 0:
|
|
143
|
-
self.logger.info(_fmt(text,args,kwargs))
|
|
144
|
-
|
|
145
|
-
def warning(self, text, *args, **kwargs ):
|
|
146
|
-
""" Reports a warning with new style formatting """
|
|
147
|
-
if self.logger.getEffectiveLevel() <= logging.WARNING and len(text) > 0:
|
|
148
|
-
self.logger.warning(_fmt(text,args,kwargs))
|
|
149
|
-
warn = warning
|
|
150
|
-
|
|
151
|
-
def error(self, text, *args, **kwargs ):
|
|
152
|
-
""" Reports an error with new style formatting """
|
|
153
|
-
if self.logger.getEffectiveLevel() <= logging.ERROR and len(text) > 0:
|
|
154
|
-
self.logger.error(_fmt(text,args,kwargs))
|
|
155
|
-
|
|
156
|
-
def critical(self, text, *args, **kwargs ):
|
|
157
|
-
""" Reports a critial occcurance with new style formatting """
|
|
158
|
-
if self.logger.getEffectiveLevel() <= logging.CRITICAL and len(text) > 0:
|
|
159
|
-
self.logger.critical(_fmt(text,args,kwargs))
|
|
160
|
-
|
|
161
|
-
def throw( self, text, *args, **kwargs ):
|
|
162
|
-
""" Raise an exception """
|
|
163
|
-
raise self.Exceptn(text,*args,**kwargs)
|
|
164
|
-
|
|
165
|
-
@staticmethod
|
|
166
|
-
def prnt( text, *args, **kwargs ):
|
|
167
|
-
""" Simple print, with EOL """
|
|
168
|
-
_prnt(_fmt(text,args,kwargs))
|
|
169
|
-
|
|
170
|
-
@staticmethod
|
|
171
|
-
def write(text, *args, **kwargs ):
|
|
172
|
-
""" Simple print, without EOL """
|
|
173
|
-
_write(_fmt(text,args,kwargs))
|
|
174
|
-
|
|
175
|
-
# run time utilities with validity check
|
|
176
|
-
# --------------------------------------
|
|
177
|
-
|
|
178
|
-
def verify(self, cond, text, *args, **kwargs ):
|
|
179
|
-
"""
|
|
180
|
-
Verifies 'cond'. Raises an exception if 'cond' is not met with the specified text.
|
|
181
|
-
Usage:
|
|
182
|
-
_log.verify( i>0, "i must be positive, found %d", i)
|
|
183
|
-
"""
|
|
184
|
-
if not cond:
|
|
185
|
-
self.throw(text,*args,**kwargs)
|
|
186
|
-
|
|
187
|
-
def verify_warning(self, cond, text, *args, **kwargs ):
|
|
188
|
-
"""
|
|
189
|
-
Verifies 'cond'. If true, writes a warning and then continues
|
|
190
|
-
Usage
|
|
191
|
-
_log.verify_warning( i>0, "i must be positive, found %d", i)
|
|
192
|
-
"""
|
|
193
|
-
if not cond:
|
|
194
|
-
self.warning(text, *args, **kwargs )
|
|
195
|
-
|
|
196
|
-
verify_warn = verify_warning
|
|
197
|
-
|
|
198
|
-
def verify_info(self, cond, text, *args, **kwargs ):
|
|
199
|
-
""" Verifies 'cond'. If true, writes log information and then continues """
|
|
200
|
-
if not cond:
|
|
201
|
-
self.info(text, *args, **kwargs )
|
|
202
|
-
|
|
203
|
-
def verify_debug(self, cond, text, *args, **kwargs ):
|
|
204
|
-
""" Verifies 'cond'. If true, writes log debug and then continues """
|
|
205
|
-
if not cond:
|
|
206
|
-
self.debug(text, *args, **kwargs )
|
|
207
|
-
|
|
208
|
-
# if-action
|
|
209
|
-
# ---------
|
|
210
|
-
|
|
211
|
-
def throw_if(self, cond, text, *args, **kwargs ):
|
|
212
|
-
""" Raises an exception if 'cond' is true. This is the reverse of verify() """
|
|
213
|
-
if cond:
|
|
214
|
-
raise self.Exceptn(text,*args,**kwargs)
|
|
215
|
-
raise_if = throw_if
|
|
216
|
-
|
|
217
|
-
def warning_if(self, cond, text, *args, **kwargs ):
|
|
218
|
-
""" If 'cond' is true, writes a warning. Opposite condition than verify_warning """
|
|
219
|
-
if cond:
|
|
220
|
-
self.warning(text,*args,**kwargs)
|
|
221
|
-
warn_if = warning_if
|
|
222
|
-
|
|
223
|
-
def info_if(self, cond, text, *args, **kwargs ):
|
|
224
|
-
""" If 'cond' is true, writes information 'info'. Opposite condition than verify_info """
|
|
225
|
-
if cond:
|
|
226
|
-
self.info(text,*args,**kwargs)
|
|
227
|
-
|
|
228
|
-
def debug_if(self, cond, text, *args, **kwargs ):
|
|
229
|
-
""" If 'cond' is true, writes debug 'info'. Opposite condition than verify_debug """
|
|
230
|
-
if cond:
|
|
231
|
-
self.debug(text,*args,**kwargs)
|
|
232
|
-
|
|
233
|
-
@staticmethod
|
|
234
|
-
def prnt_if(cond, text, *args, **kwargs ):
|
|
235
|
-
""" If 'cond' is True, prnt() text. Python 2.7 """
|
|
236
|
-
if cond:
|
|
237
|
-
Logger.prnt(text,*args,**kwargs)
|
|
238
|
-
|
|
239
|
-
@staticmethod
|
|
240
|
-
def write_if(cond, text, *args, **kwargs ):
|
|
241
|
-
""" If 'cond' is True, prnt() text """
|
|
242
|
-
if cond:
|
|
243
|
-
Logger.write(text,*args,**kwargs)
|
|
244
|
-
|
|
245
|
-
# interface into logging
|
|
246
|
-
# ----------------------
|
|
247
|
-
|
|
248
|
-
def setLevel(self, level):
|
|
249
|
-
""" logging.setLevel """
|
|
250
|
-
self.logger.setLevel(level)
|
|
251
|
-
|
|
252
|
-
def getEffectiveLevel(self):
|
|
253
|
-
""" logging.getEffectiveLevel """
|
|
254
|
-
return self.logger.getEffectiveLevel()
|
|
255
|
-
|
|
256
|
-
# ====================================================================================================
|
|
257
|
-
# setupAppLogging
|
|
258
|
-
# ---------------
|
|
259
|
-
# Defines logging at stderr and file level.
|
|
260
|
-
# ====================================================================================================
|
|
261
|
-
|
|
262
|
-
GLOBAL_LOG_DATA = "cdxbasics.logger"
|
|
263
|
-
|
|
264
|
-
class RootLog(object):
|
|
265
|
-
def __init__(self, root):
|
|
266
|
-
self.handler = root
|
|
267
|
-
self.fileName = None
|
|
268
|
-
|
|
269
|
-
rootLog = RootLog( logging.getLogger() ) # root level logger
|
|
270
|
-
|
|
271
|
-
def setupAppLogging( levelPrint : int = logging.ERROR,
|
|
272
|
-
levelFile : int = None
|
|
273
|
-
):
|
|
274
|
-
"""
|
|
275
|
-
Application wide logging control - basically sets a log file path
|
|
276
|
-
This function will only called once.
|
|
277
|
-
Put this function into your root import
|
|
278
|
-
|
|
279
|
-
Parameters
|
|
280
|
-
----------
|
|
281
|
-
levelPrint : printing level https://docs.python.org/3/library/logging.html
|
|
282
|
-
levelFile : file level. Set to None to not write to file.
|
|
283
|
-
"""
|
|
284
|
-
|
|
285
|
-
data = globals().get(GLOBAL_LOG_DATA,None)
|
|
286
|
-
if data is None:
|
|
287
|
-
# logging for std derror
|
|
288
|
-
logging.basicConfig(level=min(levelPrint,levelFile) if not levelFile is None else levelPrint)
|
|
289
|
-
fmtt = logging.Formatter(fmt="%(asctime)self %(levelname)-10s: %(message)self" )
|
|
290
|
-
stdErr = logging.StreamHandler(sys.stdout)
|
|
291
|
-
stdErr.setLevel(levelPrint )
|
|
292
|
-
rootLog.handler.addHandler( stdErr )
|
|
293
|
-
data = {'strm':stdErr }
|
|
294
|
-
|
|
295
|
-
if not levelFile is None:
|
|
296
|
-
# file system
|
|
297
|
-
import os
|
|
298
|
-
import os.path
|
|
299
|
-
import tempfile
|
|
300
|
-
stamp = datetime.datetime.now().strftime("%Y-%m-%d_%S%M%H")
|
|
301
|
-
pid = os.getpid() # note: process name is 'python.exe'
|
|
302
|
-
tmpDir = tempfile.gettempdir()
|
|
303
|
-
logFile = os.path.join(tmpDir,"py_logger_" + stamp + "_" + str(pid) + ".log")
|
|
304
|
-
|
|
305
|
-
fileE = None
|
|
306
|
-
try:
|
|
307
|
-
fileE = logging.FileHandler(logFile)
|
|
308
|
-
fileE.setFormatter(fmtt)
|
|
309
|
-
fileE.setLevel( levelFile )
|
|
310
|
-
rootLog.handler.addHandler( fileE )
|
|
311
|
-
data = {'strm':stdErr, 'file':fileE, 'logFileName':logFile }
|
|
312
|
-
except:
|
|
313
|
-
pass
|
|
314
|
-
|
|
315
|
-
globals()[GLOBAL_LOG_DATA] = data
|
|
316
|
-
|
|
317
|
-
rootLog.fileName = data.get('logFileName', None)
|
|
318
|
-
return data
|
|
319
|
-
|
cdxcore/prettydict.py
DELETED
|
@@ -1,388 +0,0 @@
|
|
|
1
|
-
"""
|
|
2
|
-
prettyict
|
|
3
|
-
Dictionaries with member synthax access
|
|
4
|
-
Hans Buehler 2022
|
|
5
|
-
"""
|
|
6
|
-
|
|
7
|
-
from collections import OrderedDict
|
|
8
|
-
from sortedcontainers import SortedDict
|
|
9
|
-
import dataclasses as dataclasses
|
|
10
|
-
from dataclasses import Field
|
|
11
|
-
import types as types
|
|
12
|
-
from collections.abc import Mapping
|
|
13
|
-
|
|
14
|
-
class PrettyDict(dict):
|
|
15
|
-
"""
|
|
16
|
-
Dictionary which allows accessing its members with member notation, e.g.
|
|
17
|
-
pdct = PrettyDict()
|
|
18
|
-
pdct.x = 1
|
|
19
|
-
x = pdct.x
|
|
20
|
-
|
|
21
|
-
Functions will be made members, i.e the following works as expected
|
|
22
|
-
def mult_x(self, a):
|
|
23
|
-
return self.x * a
|
|
24
|
-
pdct.mult_x = mult_x
|
|
25
|
-
pdct.mult_x(2) --> 2
|
|
26
|
-
|
|
27
|
-
To assign a static member use []:
|
|
28
|
-
def mult(a,b):
|
|
29
|
-
return a*b
|
|
30
|
-
pdct['mult'] = mult
|
|
31
|
-
pdct.mult(1,3) --> 3
|
|
32
|
-
|
|
33
|
-
IMPORTANT
|
|
34
|
-
Attributes starting with '__' are handled as standard attributes.
|
|
35
|
-
In other words,
|
|
36
|
-
pdct = PrettyDict()
|
|
37
|
-
pdct.__x = 1
|
|
38
|
-
_ = pdct['__x'] <- throws an exception
|
|
39
|
-
This allows re-use of general operator handling.
|
|
40
|
-
"""
|
|
41
|
-
|
|
42
|
-
def __getattr__(self, key : str):
|
|
43
|
-
""" Equyivalent to self[key] """
|
|
44
|
-
if key[:2] == "__": raise AttributeError(key) # you cannot treat private members as dictionary members
|
|
45
|
-
return self[key]
|
|
46
|
-
def __delattr__(self, key : str):
|
|
47
|
-
""" Equyivalent to del self[key] """
|
|
48
|
-
if key[:2] == "__": raise AttributeError(key) # you cannot treat private members as dictionary members
|
|
49
|
-
del self[key]
|
|
50
|
-
def __setattr__(self, key : str, value):
|
|
51
|
-
""" Equivalent to self[key] = value """
|
|
52
|
-
if key[:2] == "__":
|
|
53
|
-
return dict.__setattr__(self, key, value)
|
|
54
|
-
if isinstance(value,types.FunctionType):
|
|
55
|
-
# bind function to this object
|
|
56
|
-
value = types.MethodType(value,self)
|
|
57
|
-
elif isinstance(value,types.MethodType):
|
|
58
|
-
# re-point the method to the current instance
|
|
59
|
-
value = types.MethodType(value.__func__,self)
|
|
60
|
-
self[key] = value
|
|
61
|
-
def __call__(self, key : str, *default):
|
|
62
|
-
""" Equivalent of self.get(key,default) """
|
|
63
|
-
if len(default) > 1:
|
|
64
|
-
raise NotImplementedError("Cannot pass more than one default parameter.")
|
|
65
|
-
return self.get(key,default[0]) if len(default) == 1 else self.get(key)
|
|
66
|
-
|
|
67
|
-
def as_field(self) -> Field:
|
|
68
|
-
"""
|
|
69
|
-
Returns a PrettyDictField wrapper around self for use in dataclasses
|
|
70
|
-
See PrettyDictField documentation for an example
|
|
71
|
-
"""
|
|
72
|
-
return PrettyDictField(self)
|
|
73
|
-
|
|
74
|
-
class PrettyOrderedDict(OrderedDict):
|
|
75
|
-
"""
|
|
76
|
-
Ordered dictionary which allows accessing its members with member notation, e.g.
|
|
77
|
-
pdct = PrettyDict()
|
|
78
|
-
pdct.x = 1
|
|
79
|
-
x = pdct.x
|
|
80
|
-
|
|
81
|
-
Functions will be made members, i.e the following works as expected
|
|
82
|
-
def mult_x(self, a):
|
|
83
|
-
return self.x * a
|
|
84
|
-
pdct.mult_x = mult_x
|
|
85
|
-
pdct.mult_x(2) --> 2
|
|
86
|
-
|
|
87
|
-
To assign a static member use []:
|
|
88
|
-
def mult(a,b):
|
|
89
|
-
return a*b
|
|
90
|
-
pdct['mult'] = mult
|
|
91
|
-
pdct.mult(1,3) --> 3
|
|
92
|
-
|
|
93
|
-
IMPORTANT
|
|
94
|
-
Attributes starting with '__' are handled as standard attributes.
|
|
95
|
-
In other words,
|
|
96
|
-
pdct = PrettyOrderedDict()
|
|
97
|
-
pdct.__x = 1
|
|
98
|
-
_ = pdct['__x'] <- throws an exception
|
|
99
|
-
This allows re-use of general operator handling.
|
|
100
|
-
"""
|
|
101
|
-
|
|
102
|
-
def __getattr__(self, key : str):
|
|
103
|
-
""" Equyivalent to self[key] """
|
|
104
|
-
if key[:2] == "__": raise AttributeError(key) # you cannot treat private members as dictionary members
|
|
105
|
-
return self[key]
|
|
106
|
-
def __delattr__(self, key : str):
|
|
107
|
-
""" Equyivalent to del self[key] """
|
|
108
|
-
if key[:2] == "__": raise AttributeError(key) # you cannot treat private members as dictionary members
|
|
109
|
-
del self[key]
|
|
110
|
-
def __setattr__(self, key : str, value):
|
|
111
|
-
""" Equivalent to self[key] = value """
|
|
112
|
-
if key[:2] == "__":
|
|
113
|
-
return OrderedDict.__setattr__(self, key, value)
|
|
114
|
-
if isinstance(value,types.FunctionType):
|
|
115
|
-
# bind function to this object
|
|
116
|
-
value = types.MethodType(value,self)
|
|
117
|
-
elif isinstance(value,types.MethodType):
|
|
118
|
-
# re-point the method to the current instance
|
|
119
|
-
value = types.MethodType(value.__func__,self)
|
|
120
|
-
self[key] = value
|
|
121
|
-
def __call__(self, key : str, *default):
|
|
122
|
-
""" Equivalent of self.get(key,default) """
|
|
123
|
-
if len(default) > 1:
|
|
124
|
-
raise NotImplementedError("Cannot pass more than one default parameter.")
|
|
125
|
-
return self.get(key,default[0]) if len(default) == 1 else self.get(key)
|
|
126
|
-
|
|
127
|
-
# pickling
|
|
128
|
-
def __getstate__(self):
|
|
129
|
-
""" Return state to pickle """
|
|
130
|
-
return self.__dict__
|
|
131
|
-
def __setstate__(self, state):
|
|
132
|
-
""" Restore pickle """
|
|
133
|
-
self.__dict__.update(state)
|
|
134
|
-
|
|
135
|
-
def as_field(self) -> Field:
|
|
136
|
-
"""
|
|
137
|
-
Returns a PrettyDictField wrapper around 'self' for use in dataclasses
|
|
138
|
-
See PrettyDictField documentation for an example
|
|
139
|
-
"""
|
|
140
|
-
return PrettyDictField(self)
|
|
141
|
-
|
|
142
|
-
@property
|
|
143
|
-
def at_pos(self):
|
|
144
|
-
"""
|
|
145
|
-
Element access
|
|
146
|
-
|
|
147
|
-
at_pos[position] returns an element or elements at an ordinal position.
|
|
148
|
-
It returns a single element if 'position' refers to only one field.
|
|
149
|
-
If 'position' is a slice then the respecitve list of fields is returned
|
|
150
|
-
|
|
151
|
-
at_pos[position] = item assigns an item or an ordinal position
|
|
152
|
-
If 'position' refers to a single element, 'item' must be that item
|
|
153
|
-
If 'position' is a slice then 'item' must resolve to a list of the required size.
|
|
154
|
-
|
|
155
|
-
Key access
|
|
156
|
-
|
|
157
|
-
at_pos.keys[position] returns the key or keys at 'position'
|
|
158
|
-
|
|
159
|
-
at_pos.items[position] returns the tuple (key, element) or a list thereof for `position`
|
|
160
|
-
"""
|
|
161
|
-
class Access:
|
|
162
|
-
"""
|
|
163
|
-
Wrapper object to allow index access for at_pos
|
|
164
|
-
"""
|
|
165
|
-
def __init__(self):
|
|
166
|
-
self.__keys = None
|
|
167
|
-
|
|
168
|
-
def __getitem__(_, position):
|
|
169
|
-
key = _.keys[position]
|
|
170
|
-
return self[key] if not isinstance(key,list) else [ self[k] for k in key ]
|
|
171
|
-
def __setitem__(_, position, item ):
|
|
172
|
-
key = _.keys[position]
|
|
173
|
-
if not isinstance(key,list):
|
|
174
|
-
self[key] = item
|
|
175
|
-
else:
|
|
176
|
-
for k, i in zip(key, item):
|
|
177
|
-
self[k] = i
|
|
178
|
-
@property
|
|
179
|
-
def keys(_) -> list:
|
|
180
|
-
""" Returns the list of keys of the original dictionary """
|
|
181
|
-
if _.__keys is None:
|
|
182
|
-
_.__keys = list(self.keys())
|
|
183
|
-
return _.__keys
|
|
184
|
-
@property
|
|
185
|
-
def items(_) -> list:
|
|
186
|
-
""" Returns the list of keys of the original dictionary """
|
|
187
|
-
class ItemAccess(object):
|
|
188
|
-
def __getitem__(_x, position):
|
|
189
|
-
key = _.keys[position]
|
|
190
|
-
return (key, self[key]) if not isinstance(key,list) else [ (k,self[k]) for k in key ]
|
|
191
|
-
return ItemAccess()
|
|
192
|
-
|
|
193
|
-
return Access()
|
|
194
|
-
|
|
195
|
-
@property
|
|
196
|
-
def key_at_pos(self) -> list:
|
|
197
|
-
"""
|
|
198
|
-
key_at_pos returns the list of keys, hence key_at_pos[i] returns the ith key
|
|
199
|
-
"""
|
|
200
|
-
return list(self)
|
|
201
|
-
|
|
202
|
-
@property
|
|
203
|
-
def item_at_pos(self) -> list:
|
|
204
|
-
"""
|
|
205
|
-
key_at_pos returns the list of keys, hence key_at_pos[i] returns the ith key
|
|
206
|
-
"""
|
|
207
|
-
return list(self)
|
|
208
|
-
|
|
209
|
-
pdct = PrettyOrderedDict
|
|
210
|
-
|
|
211
|
-
class BETA_PrettySortedDict(SortedDict):
|
|
212
|
-
"""
|
|
213
|
-
*NOT WORKING WELL*
|
|
214
|
-
Sorted dictionary which allows accessing its members with member notation, e.g.
|
|
215
|
-
pdct = PrettyDict()
|
|
216
|
-
pdct.x = 1
|
|
217
|
-
x = pdct.x
|
|
218
|
-
|
|
219
|
-
Functions will be made members, i.e the following works as expected
|
|
220
|
-
def mult_x(self, a):
|
|
221
|
-
return self.x * a
|
|
222
|
-
pdct.mult_x = mult_x
|
|
223
|
-
pdct.mult_x(2) --> 2
|
|
224
|
-
|
|
225
|
-
To assign a static member use []:
|
|
226
|
-
def mult(a,b):
|
|
227
|
-
return a*b
|
|
228
|
-
pdct['mult'] = mult
|
|
229
|
-
pdct.mult(1,3) --> 3
|
|
230
|
-
|
|
231
|
-
IMPORTANT
|
|
232
|
-
Attributes starting with '_' (one underscore) are handled as standard attributes.
|
|
233
|
-
In other words,
|
|
234
|
-
pdct = PrettyOrderedDict()
|
|
235
|
-
pdct._x = 1
|
|
236
|
-
_ = pdct['_x'] <- throws an exception
|
|
237
|
-
This allows re-use of general operator handling.
|
|
238
|
-
The reason the sorted class disallow '_' (as opposed to the other two classes who merely disallow '__')
|
|
239
|
-
is that SortedDict() uses protected members.
|
|
240
|
-
"""
|
|
241
|
-
|
|
242
|
-
def __getattr__(self, key : str):
|
|
243
|
-
""" Equyivalent to self[key] """
|
|
244
|
-
if key[:1] == "_": raise AttributeError(key) # you cannot treat protected or private members as dictionary members
|
|
245
|
-
return self[key]
|
|
246
|
-
def __delattr__(self, key : str):
|
|
247
|
-
""" Equyivalent to del self[key] """
|
|
248
|
-
if key[:2] == "__": raise AttributeError(key) # you cannot treat private members as dictionary members
|
|
249
|
-
del self[key]
|
|
250
|
-
def __setattr__(self, key : str, value):
|
|
251
|
-
""" Equivalent to self[key] = value """
|
|
252
|
-
if key[:1] == "_":
|
|
253
|
-
return SortedDict.__setattr__(self, key, value)
|
|
254
|
-
if isinstance(value,types.FunctionType):
|
|
255
|
-
# bind function to this object
|
|
256
|
-
value = types.MethodType(value,self)
|
|
257
|
-
elif isinstance(value,types.MethodType):
|
|
258
|
-
# re-point the method to the current instance
|
|
259
|
-
value = types.MethodType(value.__func__,self)
|
|
260
|
-
self[key] = value
|
|
261
|
-
def __call__(self, key : str, *default):
|
|
262
|
-
""" Equivalent of self.get(key,default) """
|
|
263
|
-
if len(default) > 1:
|
|
264
|
-
raise NotImplementedError("Cannot pass more than one default parameter.")
|
|
265
|
-
return self.get(key,default[0]) if len(default) == 1 else self.get(key)
|
|
266
|
-
|
|
267
|
-
class PrettyDictField(object):
|
|
268
|
-
"""
|
|
269
|
-
Simplististc 'read only' wrapper for PrettyOrderedDict objects.
|
|
270
|
-
Useful for Flax
|
|
271
|
-
|
|
272
|
-
import dataclasses as dataclasses
|
|
273
|
-
import jax.numpy as jnp
|
|
274
|
-
import jax as jax
|
|
275
|
-
from options.cdxbasics.config import Config, ConfigField
|
|
276
|
-
import types as types
|
|
277
|
-
|
|
278
|
-
class A( nn.Module ):
|
|
279
|
-
pdct : PrettyOrderedDictField = PrettyOrderedDictField.Field()
|
|
280
|
-
|
|
281
|
-
def setup(self):
|
|
282
|
-
self.dense = nn.Dense(1)
|
|
283
|
-
|
|
284
|
-
def __call__(self, x):
|
|
285
|
-
a = self.pdct.a
|
|
286
|
-
return self.dense(x)*a
|
|
287
|
-
|
|
288
|
-
r = PrettyOrderedDict(a=1.)
|
|
289
|
-
a = A( r.as_field() )
|
|
290
|
-
|
|
291
|
-
key1, key2 = jax.random.split(jax.random.key(0))
|
|
292
|
-
x = jnp.zeros((10,10))
|
|
293
|
-
param = a.init( key1, x )
|
|
294
|
-
y = a.apply( param, x )
|
|
295
|
-
|
|
296
|
-
The class will traverse pretty dictionaries of pretty dictionaries correctly.
|
|
297
|
-
However, it has some limitations as it does not handle custom lists of pretty dicts.
|
|
298
|
-
"""
|
|
299
|
-
def __init__(self, pdct : PrettyOrderedDict = None, **kwargs):
|
|
300
|
-
""" Initialize with an input dictionary and potential overwrites """
|
|
301
|
-
if not pdct is None:
|
|
302
|
-
if type(pdct).__name__ == type(self).__name__ and len(kwargs) == 0:
|
|
303
|
-
# copy
|
|
304
|
-
self.__pdct = PrettyOrderedDict( pdct.__pdct )
|
|
305
|
-
return
|
|
306
|
-
if not isinstance(pdct, Mapping): raise ValueError("'pdct' must be a Mapping")
|
|
307
|
-
self.__pdct = PrettyOrderedDict(pdct)
|
|
308
|
-
self.__pdct.update(kwargs)
|
|
309
|
-
else:
|
|
310
|
-
self.__pdct = PrettyOrderedDict(**kwargs)
|
|
311
|
-
def rec(x):
|
|
312
|
-
for k, v in x.items():
|
|
313
|
-
if isinstance(v, (PrettyDict, PrettyOrderedDict)):
|
|
314
|
-
x[k] = PrettyDictField(v)
|
|
315
|
-
elif isinstance(v, Mapping):
|
|
316
|
-
rec(v)
|
|
317
|
-
rec(self.__pdct)
|
|
318
|
-
|
|
319
|
-
def as_dict(self) -> PrettyOrderedDict:
|
|
320
|
-
""" Return copy of underlying dictionary """
|
|
321
|
-
return PrettyOrderedDict( self.__pdct )
|
|
322
|
-
|
|
323
|
-
def as_field(self) -> Field:
|
|
324
|
-
"""
|
|
325
|
-
Returns a PrettyDictField wrapper around self for use in dataclasse
|
|
326
|
-
This function makes a (shallow enough) copy of the current field.
|
|
327
|
-
It is present so iterative applications of as_field() are convenient.
|
|
328
|
-
"""
|
|
329
|
-
return PrettyDictField(self)
|
|
330
|
-
|
|
331
|
-
@staticmethod
|
|
332
|
-
def default():
|
|
333
|
-
return PrettyDictField()
|
|
334
|
-
|
|
335
|
-
@staticmethod
|
|
336
|
-
def Field( default : PrettyOrderedDict = None, **kwargs):
|
|
337
|
-
"""
|
|
338
|
-
Returns a dataclasses.field for PrettyDictField
|
|
339
|
-
"""
|
|
340
|
-
if default is None and len(kwargs) == 0:
|
|
341
|
-
return dataclasses.field( default_factory=PrettyDictField )
|
|
342
|
-
|
|
343
|
-
if not default is None:
|
|
344
|
-
default.upate(kwargs)
|
|
345
|
-
else:
|
|
346
|
-
default = kwargs
|
|
347
|
-
def factory():
|
|
348
|
-
return PrettyDictField(default)
|
|
349
|
-
return dataclasses.field( default_factory=factory )
|
|
350
|
-
|
|
351
|
-
# mimic the underlying dictionary
|
|
352
|
-
# -------------------------------
|
|
353
|
-
|
|
354
|
-
def __getattr__(self, key):
|
|
355
|
-
if key[:2] == "__":
|
|
356
|
-
return object.__getattr__(self,key)
|
|
357
|
-
return self.__pdct.__getattr__(key)
|
|
358
|
-
def __getitem__(self, key):
|
|
359
|
-
return self.__pdct[key]
|
|
360
|
-
def __call__(self, *kargs, **kwargs):
|
|
361
|
-
return self.__pdct(*kargs, **kwargs)
|
|
362
|
-
def __eq__(self, other):
|
|
363
|
-
if type(other).__name__ == "PrettyOrderedDict":
|
|
364
|
-
return self.__pdct == other
|
|
365
|
-
else:
|
|
366
|
-
return self.__pdct == other.pdct
|
|
367
|
-
def keys(self):
|
|
368
|
-
return self.__pdct.keys()
|
|
369
|
-
def items(self):
|
|
370
|
-
return self.__pdct.items()
|
|
371
|
-
def values(self):
|
|
372
|
-
return self.__pdct.values()
|
|
373
|
-
def __hash__(self):
|
|
374
|
-
h = 0
|
|
375
|
-
for k, v in self.items():
|
|
376
|
-
h ^= hash(k) ^ hash(v)
|
|
377
|
-
return h
|
|
378
|
-
def __iter__(self):
|
|
379
|
-
return self.__pdct.__iter__()
|
|
380
|
-
def __contains__(self, key):
|
|
381
|
-
return self.__pdct.__contains__(key)
|
|
382
|
-
def __len__(self):
|
|
383
|
-
return self.__pdct.__len__()
|
|
384
|
-
def __str__(self):
|
|
385
|
-
return self.__pdct.__str__()
|
|
386
|
-
def __repr__(self):
|
|
387
|
-
return self.__pdct.__repr__()
|
|
388
|
-
|