python-misc-utils 0.2__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.
- py_misc_utils/__init__.py +0 -0
- py_misc_utils/abs_timeout.py +12 -0
- py_misc_utils/alog.py +311 -0
- py_misc_utils/app_main.py +179 -0
- py_misc_utils/archive_streamer.py +112 -0
- py_misc_utils/assert_checks.py +118 -0
- py_misc_utils/ast_utils.py +121 -0
- py_misc_utils/async_manager.py +189 -0
- py_misc_utils/break_control.py +63 -0
- py_misc_utils/buffered_iterator.py +35 -0
- py_misc_utils/cached_file.py +507 -0
- py_misc_utils/call_limiter.py +26 -0
- py_misc_utils/call_result_selector.py +13 -0
- py_misc_utils/cleanups.py +85 -0
- py_misc_utils/cmd.py +97 -0
- py_misc_utils/compression.py +116 -0
- py_misc_utils/cond_waiter.py +13 -0
- py_misc_utils/context_base.py +18 -0
- py_misc_utils/context_managers.py +67 -0
- py_misc_utils/core_utils.py +577 -0
- py_misc_utils/daemon_process.py +252 -0
- py_misc_utils/data_cache.py +46 -0
- py_misc_utils/date_utils.py +90 -0
- py_misc_utils/debug.py +24 -0
- py_misc_utils/dyn_modules.py +50 -0
- py_misc_utils/dynamod.py +103 -0
- py_misc_utils/env_config.py +35 -0
- py_misc_utils/executor.py +239 -0
- py_misc_utils/file_overwrite.py +29 -0
- py_misc_utils/fin_wrap.py +77 -0
- py_misc_utils/fp_utils.py +47 -0
- py_misc_utils/fs/__init__.py +0 -0
- py_misc_utils/fs/file_fs.py +127 -0
- py_misc_utils/fs/ftp_fs.py +242 -0
- py_misc_utils/fs/gcs_fs.py +196 -0
- py_misc_utils/fs/http_fs.py +241 -0
- py_misc_utils/fs/s3_fs.py +417 -0
- py_misc_utils/fs_base.py +133 -0
- py_misc_utils/fs_utils.py +207 -0
- py_misc_utils/gcs_fs.py +169 -0
- py_misc_utils/gen_indices.py +54 -0
- py_misc_utils/gfs.py +371 -0
- py_misc_utils/git_repo.py +77 -0
- py_misc_utils/global_namespace.py +110 -0
- py_misc_utils/http_async_fetcher.py +139 -0
- py_misc_utils/http_server.py +196 -0
- py_misc_utils/http_utils.py +143 -0
- py_misc_utils/img_utils.py +20 -0
- py_misc_utils/infix_op.py +20 -0
- py_misc_utils/inspect_utils.py +205 -0
- py_misc_utils/iostream.py +21 -0
- py_misc_utils/iter_file.py +117 -0
- py_misc_utils/key_wrap.py +46 -0
- py_misc_utils/lazy_import.py +25 -0
- py_misc_utils/lockfile.py +164 -0
- py_misc_utils/mem_size.py +64 -0
- py_misc_utils/mirror_from.py +72 -0
- py_misc_utils/mmap.py +16 -0
- py_misc_utils/module_utils.py +196 -0
- py_misc_utils/moving_average.py +19 -0
- py_misc_utils/msgpack_streamer.py +26 -0
- py_misc_utils/multi_wait.py +24 -0
- py_misc_utils/multiprocessing.py +102 -0
- py_misc_utils/named_array.py +224 -0
- py_misc_utils/no_break.py +46 -0
- py_misc_utils/no_except.py +32 -0
- py_misc_utils/np_ml_framework.py +184 -0
- py_misc_utils/np_utils.py +346 -0
- py_misc_utils/ntuple_utils.py +38 -0
- py_misc_utils/num_utils.py +54 -0
- py_misc_utils/obj.py +73 -0
- py_misc_utils/object_cache.py +100 -0
- py_misc_utils/object_tracker.py +88 -0
- py_misc_utils/ordered_set.py +71 -0
- py_misc_utils/osfd.py +27 -0
- py_misc_utils/packet.py +22 -0
- py_misc_utils/parquet_streamer.py +69 -0
- py_misc_utils/pd_utils.py +254 -0
- py_misc_utils/periodic_task.py +61 -0
- py_misc_utils/pickle_wrap.py +121 -0
- py_misc_utils/pipeline.py +98 -0
- py_misc_utils/remap_pickle.py +50 -0
- py_misc_utils/resource_manager.py +155 -0
- py_misc_utils/rnd_utils.py +56 -0
- py_misc_utils/run_once.py +19 -0
- py_misc_utils/scheduler.py +135 -0
- py_misc_utils/select_params.py +300 -0
- py_misc_utils/signal.py +141 -0
- py_misc_utils/skl_utils.py +270 -0
- py_misc_utils/split.py +147 -0
- py_misc_utils/state.py +53 -0
- py_misc_utils/std_module.py +56 -0
- py_misc_utils/stream_dataframe.py +176 -0
- py_misc_utils/streamed_file.py +144 -0
- py_misc_utils/tempdir.py +79 -0
- py_misc_utils/template_replace.py +51 -0
- py_misc_utils/tensor_stream.py +269 -0
- py_misc_utils/thread_context.py +33 -0
- py_misc_utils/throttle.py +30 -0
- py_misc_utils/time_trigger.py +18 -0
- py_misc_utils/timegen.py +11 -0
- py_misc_utils/traceback.py +49 -0
- py_misc_utils/tracking_executor.py +91 -0
- py_misc_utils/transform_array.py +42 -0
- py_misc_utils/uncompress.py +35 -0
- py_misc_utils/url_fetcher.py +157 -0
- py_misc_utils/utils.py +538 -0
- py_misc_utils/varint.py +50 -0
- py_misc_utils/virt_array.py +52 -0
- py_misc_utils/weak_call.py +33 -0
- py_misc_utils/work_results.py +100 -0
- py_misc_utils/writeback_file.py +43 -0
- python_misc_utils-0.2.dist-info/METADATA +36 -0
- python_misc_utils-0.2.dist-info/RECORD +117 -0
- python_misc_utils-0.2.dist-info/WHEEL +5 -0
- python_misc_utils-0.2.dist-info/licenses/LICENSE +13 -0
- python_misc_utils-0.2.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,577 @@
|
|
|
1
|
+
# NOTE: This module is for APIs which has no local dependecies!
|
|
2
|
+
import array
|
|
3
|
+
import collections
|
|
4
|
+
import copy
|
|
5
|
+
import inspect
|
|
6
|
+
import os
|
|
7
|
+
import re
|
|
8
|
+
import sys
|
|
9
|
+
import threading
|
|
10
|
+
import types
|
|
11
|
+
import yaml
|
|
12
|
+
|
|
13
|
+
_NONE = object()
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def ident(x):
|
|
17
|
+
return x
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def noop(*args, **kwargs):
|
|
21
|
+
pass
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def object_context(sobj, **kwargs):
|
|
25
|
+
ctx_args = vars(sobj).copy()
|
|
26
|
+
ctx_args.update(**kwargs)
|
|
27
|
+
|
|
28
|
+
return types.SimpleNamespace(**ctx_args)
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def args(*uargs, **kwargs):
|
|
32
|
+
return uargs, kwargs
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def is_builtin_function(obj):
|
|
36
|
+
return isinstance(obj, types.BuiltinFunctionType)
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def isdict(value):
|
|
40
|
+
return isinstance(value, collections.abc.Mapping)
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def refcount(obj):
|
|
44
|
+
# Discard 2 frame references (our own, and the sys.getrefcount() one).
|
|
45
|
+
return sys.getrefcount(obj) - 2
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def denone(**kwargs):
|
|
49
|
+
return {k: v for k, v in kwargs.items() if v is not None}
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def vfind(mem, b):
|
|
53
|
+
# Hmmm, a memoryview() should really have a find() API...
|
|
54
|
+
m = re.search(b, mem)
|
|
55
|
+
|
|
56
|
+
return m.start() if m is not None else -1
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def seqfirst(s):
|
|
60
|
+
return next(iter(s))
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def iter_next(it, defval=None):
|
|
64
|
+
try:
|
|
65
|
+
return next(it)
|
|
66
|
+
except StopIteration:
|
|
67
|
+
return defval
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def enum_values(obj):
|
|
71
|
+
if isdict(obj):
|
|
72
|
+
for k, v in obj.items():
|
|
73
|
+
yield k, v
|
|
74
|
+
else:
|
|
75
|
+
for k, v in vars(obj):
|
|
76
|
+
yield k, v
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
def idx_expand(data, idx, filler=None):
|
|
80
|
+
if idx >= len(data):
|
|
81
|
+
data = data + [filler] * (idx + 1 - len(data))
|
|
82
|
+
|
|
83
|
+
return data
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
_SEQUENCE_TYPES = (list, tuple, types.GeneratorType)
|
|
87
|
+
|
|
88
|
+
def is_sequence(v):
|
|
89
|
+
return isinstance(v, _SEQUENCE_TYPES)
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
def expand(data, n):
|
|
93
|
+
return data if is_sequence(data) else (data,) * n
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
def range_split(n, split, minsize, reverse=False):
|
|
97
|
+
splits = list(range(0, n, split))
|
|
98
|
+
if len(splits) > 1 and (n - splits[-1]) < minsize:
|
|
99
|
+
splits.pop()
|
|
100
|
+
|
|
101
|
+
rsplits = []
|
|
102
|
+
for i, base in enumerate(splits):
|
|
103
|
+
top = splits[i + 1] if (i + 1) < len(splits) else n
|
|
104
|
+
rsplits.append((base, top))
|
|
105
|
+
|
|
106
|
+
return tuple(reversed(rsplits)) if reverse else tuple(rsplits)
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
def partition(data, n):
|
|
110
|
+
parts = []
|
|
111
|
+
for i in range(n):
|
|
112
|
+
parts.append(tuple(data[j] for j in range(i, len(data), n)))
|
|
113
|
+
|
|
114
|
+
return tuple(parts)
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
def is_iterator(obj):
|
|
118
|
+
return hasattr(obj, '__iter__') and hasattr(obj, '__next__')
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
def as_iterator(obj):
|
|
122
|
+
return obj if is_iterator(obj) else iter([obj])
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
def dmerge(*args):
|
|
126
|
+
mdict = dict()
|
|
127
|
+
for d in args:
|
|
128
|
+
mdict.update(d)
|
|
129
|
+
|
|
130
|
+
return mdict
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
def dict_extract(d, prefix=None, rx=None):
|
|
134
|
+
if rx is None:
|
|
135
|
+
rx = f'{prefix}(.*)'
|
|
136
|
+
xd = dict()
|
|
137
|
+
for k, v in d.items():
|
|
138
|
+
m = re.match(rx, k)
|
|
139
|
+
if m:
|
|
140
|
+
xd[m.group(1)] = v
|
|
141
|
+
|
|
142
|
+
return xd
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
def dget(sdict, name, defval, dtype=None):
|
|
146
|
+
v = sdict.get(name, _NONE)
|
|
147
|
+
if v is _NONE:
|
|
148
|
+
return defval
|
|
149
|
+
|
|
150
|
+
if dtype is None and defval is not None:
|
|
151
|
+
dtype = type(defval)
|
|
152
|
+
|
|
153
|
+
return dtype(v) if v is not None and dtype is not None else v
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
def to_type(v, vtype):
|
|
157
|
+
if isinstance(v, str):
|
|
158
|
+
if vtype in (list, tuple):
|
|
159
|
+
m = re.match(r'\s*\((.*)\)\s*$', v)
|
|
160
|
+
if m:
|
|
161
|
+
v = f'[{m.group(1)}]'
|
|
162
|
+
elif not re.match(r'\s*\[.*\]\s*$', v):
|
|
163
|
+
v = f'[{v}]'
|
|
164
|
+
|
|
165
|
+
v = yaml.safe_load(v)
|
|
166
|
+
|
|
167
|
+
return vtype(v)
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
def cast(v, vtype):
|
|
171
|
+
return to_type(v, vtype) if v is not None else None
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
def infer_value(v, vtype=None):
|
|
175
|
+
return yaml.safe_load(v) if vtype is None else to_type(v, vtype)
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
def splitstrip(data, sep):
|
|
179
|
+
return tuple(s.strip() for s in data.split(sep))
|
|
180
|
+
|
|
181
|
+
|
|
182
|
+
def separate(data, sep, reverse=False, filler=_NONE):
|
|
183
|
+
pos = data.rfind(sep) if reverse else data.find(sep)
|
|
184
|
+
if pos < 0:
|
|
185
|
+
if filler is not _NONE:
|
|
186
|
+
return data.strip(), filler
|
|
187
|
+
else:
|
|
188
|
+
return data.strip(),
|
|
189
|
+
|
|
190
|
+
return data[: pos].strip(), data[pos + 1:].strip()
|
|
191
|
+
|
|
192
|
+
|
|
193
|
+
def root_module(modname):
|
|
194
|
+
return separate(modname, '.')[0]
|
|
195
|
+
|
|
196
|
+
|
|
197
|
+
def parent_module(modname):
|
|
198
|
+
return separate(modname, '.', reverse=True)[0]
|
|
199
|
+
|
|
200
|
+
|
|
201
|
+
def ns_lookup(key, mappings):
|
|
202
|
+
kparts = key.split('.')
|
|
203
|
+
for ns in mappings:
|
|
204
|
+
for part in kparts:
|
|
205
|
+
if isdict(ns):
|
|
206
|
+
ns = ns.get(part)
|
|
207
|
+
else:
|
|
208
|
+
ns = getattr(ns, part, None)
|
|
209
|
+
if ns is None:
|
|
210
|
+
break
|
|
211
|
+
|
|
212
|
+
if ns is not None:
|
|
213
|
+
return ns
|
|
214
|
+
|
|
215
|
+
|
|
216
|
+
def index_select(arr, idx):
|
|
217
|
+
return arr[idx] if isinstance(idx, slice) else [arr[i] for i in idx]
|
|
218
|
+
|
|
219
|
+
|
|
220
|
+
def lindex(l, e, start=0, end=None):
|
|
221
|
+
try:
|
|
222
|
+
return l.index(e, start, end if end is not None else len(l))
|
|
223
|
+
except ValueError:
|
|
224
|
+
return -1
|
|
225
|
+
|
|
226
|
+
|
|
227
|
+
def append_if_missing(arr, elem):
|
|
228
|
+
if elem not in arr:
|
|
229
|
+
arr.append(elem)
|
|
230
|
+
|
|
231
|
+
|
|
232
|
+
def size_str(size):
|
|
233
|
+
syms = ('B', 'KB', 'MB', 'GB', 'TB')
|
|
234
|
+
|
|
235
|
+
for i, sym in enumerate(syms):
|
|
236
|
+
if size < 1024:
|
|
237
|
+
return f'{size} {sym}' if i == 0 else f'{size:.2f} {sym}'
|
|
238
|
+
|
|
239
|
+
size /= 1024
|
|
240
|
+
|
|
241
|
+
return f'{size * 1024:.2f} {syms[-1]}'
|
|
242
|
+
|
|
243
|
+
|
|
244
|
+
def maybe_call(obj, name, *args, **kwargs):
|
|
245
|
+
fn = getattr(obj, name, None)
|
|
246
|
+
|
|
247
|
+
return fn(*args, **kwargs) if callable(fn) else None
|
|
248
|
+
|
|
249
|
+
|
|
250
|
+
def maybe_call_dv(obj, name, defval, *args, **kwargs):
|
|
251
|
+
fn = getattr(obj, name, None)
|
|
252
|
+
|
|
253
|
+
return fn(*args, **kwargs) if callable(fn) else defval
|
|
254
|
+
|
|
255
|
+
|
|
256
|
+
def unique(data):
|
|
257
|
+
udata = collections.defaultdict(lambda: array.array('L'))
|
|
258
|
+
for i, v in enumerate(data):
|
|
259
|
+
udata[v].append(i)
|
|
260
|
+
|
|
261
|
+
return udata
|
|
262
|
+
|
|
263
|
+
|
|
264
|
+
def enum_max(cls):
|
|
265
|
+
return max(x for x in cls)
|
|
266
|
+
|
|
267
|
+
|
|
268
|
+
def enum_bits(cls):
|
|
269
|
+
return enum_max(cls).bit_length()
|
|
270
|
+
|
|
271
|
+
|
|
272
|
+
def signature(v):
|
|
273
|
+
if isdict(v):
|
|
274
|
+
vdata = dict()
|
|
275
|
+
for k in sorted(v.keys()):
|
|
276
|
+
vdata[k] = signature(v[k])
|
|
277
|
+
|
|
278
|
+
return vdata
|
|
279
|
+
elif isinstance(v, (list, tuple)):
|
|
280
|
+
vdata = [signature(e) for e in v]
|
|
281
|
+
|
|
282
|
+
return type(v)(vdata)
|
|
283
|
+
|
|
284
|
+
return type(v)
|
|
285
|
+
|
|
286
|
+
|
|
287
|
+
def equal_signature(a, b, subcls=True):
|
|
288
|
+
if isdict(a):
|
|
289
|
+
if not isdict(b) or len(a) != len(b):
|
|
290
|
+
return False
|
|
291
|
+
|
|
292
|
+
for k, t in a.items():
|
|
293
|
+
tb = b.get(k)
|
|
294
|
+
if tb is None or not equal_signature(t, tb, subcls=subcls):
|
|
295
|
+
return False
|
|
296
|
+
|
|
297
|
+
return True
|
|
298
|
+
elif isinstance(a, (list, tuple)):
|
|
299
|
+
if type(a) != type(b) or len(a) != len(b):
|
|
300
|
+
return False
|
|
301
|
+
|
|
302
|
+
for ea, eb in zip(a, b):
|
|
303
|
+
if not equal_signature(ea, eb, subcls=subcls):
|
|
304
|
+
return False
|
|
305
|
+
|
|
306
|
+
return True
|
|
307
|
+
|
|
308
|
+
return issubclass(a, b) or issubclass(b, a) if subcls else a == b
|
|
309
|
+
|
|
310
|
+
|
|
311
|
+
def genhash(v):
|
|
312
|
+
if isdict(v):
|
|
313
|
+
vdata = []
|
|
314
|
+
for k in sorted(v.keys()):
|
|
315
|
+
vdata.append(genhash(k))
|
|
316
|
+
vdata.append(genhash(v[k]))
|
|
317
|
+
|
|
318
|
+
return hash((type(v), tuple(vdata)))
|
|
319
|
+
elif isinstance(v, (list, tuple)):
|
|
320
|
+
vdata = [genhash(e) for e in v]
|
|
321
|
+
|
|
322
|
+
return hash((type(v), tuple(vdata)))
|
|
323
|
+
|
|
324
|
+
return hash((type(v), v))
|
|
325
|
+
|
|
326
|
+
|
|
327
|
+
def norm_slice(start, stop, size):
|
|
328
|
+
if start is None:
|
|
329
|
+
start = 0
|
|
330
|
+
elif start < 0:
|
|
331
|
+
start = size + start
|
|
332
|
+
if stop is None:
|
|
333
|
+
stop = size
|
|
334
|
+
elif stop < 0:
|
|
335
|
+
stop = size + stop
|
|
336
|
+
|
|
337
|
+
return start, stop
|
|
338
|
+
|
|
339
|
+
|
|
340
|
+
def run_async(fn, *args, **kwargs):
|
|
341
|
+
thread = threading.Thread(target=fn, args=args, kwargs=kwargs, daemon=True)
|
|
342
|
+
thread.start()
|
|
343
|
+
|
|
344
|
+
return thread
|
|
345
|
+
|
|
346
|
+
|
|
347
|
+
def bisect_right(x, key, hi, lo=0):
|
|
348
|
+
tas.check_ge(lo, 0)
|
|
349
|
+
while lo < hi:
|
|
350
|
+
mid = (lo + hi) // 2
|
|
351
|
+
if x < key(mid):
|
|
352
|
+
hi = mid
|
|
353
|
+
else:
|
|
354
|
+
lo = mid + 1
|
|
355
|
+
|
|
356
|
+
return lo
|
|
357
|
+
|
|
358
|
+
|
|
359
|
+
def bisect_left(x, key, hi, lo=0):
|
|
360
|
+
tas.check_ge(lo, 0)
|
|
361
|
+
while lo < hi:
|
|
362
|
+
mid = (lo + hi) // 2
|
|
363
|
+
if key(mid) < x:
|
|
364
|
+
lo = mid + 1
|
|
365
|
+
else:
|
|
366
|
+
hi = mid
|
|
367
|
+
|
|
368
|
+
return lo
|
|
369
|
+
|
|
370
|
+
|
|
371
|
+
def rewrited_exception(ex, msg):
|
|
372
|
+
xmsg = f'{ex}{msg}'
|
|
373
|
+
|
|
374
|
+
return ex.__class__(xmsg).with_traceback(ex.__traceback__)
|
|
375
|
+
|
|
376
|
+
|
|
377
|
+
def obj_from_dict(cls, data):
|
|
378
|
+
args, kwargs = [], dict()
|
|
379
|
+
slots = getattr(cls, '__slots__', None)
|
|
380
|
+
if slots is not None:
|
|
381
|
+
kwargs = {k: data.get(k) for k in slots}
|
|
382
|
+
else:
|
|
383
|
+
sig = inspect.signature(cls)
|
|
384
|
+
|
|
385
|
+
for n, p in sig.parameters.items():
|
|
386
|
+
if p.kind == p.POSITIONAL_ONLY:
|
|
387
|
+
args.append(data.get(n))
|
|
388
|
+
else:
|
|
389
|
+
kwargs[n] = data.get(n)
|
|
390
|
+
|
|
391
|
+
return cls(*args, **kwargs)
|
|
392
|
+
|
|
393
|
+
|
|
394
|
+
def clone(obj):
|
|
395
|
+
clone_fn = getattr(obj, 'clone', None)
|
|
396
|
+
|
|
397
|
+
return clone_fn() if callable(clone_fn) else copy.copy(obj)
|
|
398
|
+
|
|
399
|
+
|
|
400
|
+
def is_namedtuple(obj):
|
|
401
|
+
return isinstance(obj, tuple) and hasattr(obj, '_asdict') and hasattr(obj, '_fields')
|
|
402
|
+
|
|
403
|
+
|
|
404
|
+
def new_with(obj, **kwargs):
|
|
405
|
+
if is_namedtuple(obj):
|
|
406
|
+
return obj._replace(**kwargs)
|
|
407
|
+
|
|
408
|
+
nobj = copy.copy(obj)
|
|
409
|
+
if isdict(nobj):
|
|
410
|
+
nobj.update(kwargs)
|
|
411
|
+
else:
|
|
412
|
+
for k, v in kwargs.items():
|
|
413
|
+
setattr(nobj, k, v)
|
|
414
|
+
|
|
415
|
+
return nobj
|
|
416
|
+
|
|
417
|
+
|
|
418
|
+
def make_ntuple(ntc, args):
|
|
419
|
+
targs = []
|
|
420
|
+
for f in ntc._fields:
|
|
421
|
+
fv = args.get(f, _NONE)
|
|
422
|
+
if fv is _NONE:
|
|
423
|
+
fv = ntc._field_defaults.get(f)
|
|
424
|
+
|
|
425
|
+
targs.append(fv)
|
|
426
|
+
|
|
427
|
+
return ntc._make(targs)
|
|
428
|
+
|
|
429
|
+
|
|
430
|
+
class StringTable:
|
|
431
|
+
|
|
432
|
+
def __init__(self):
|
|
433
|
+
self._tbl = dict()
|
|
434
|
+
|
|
435
|
+
def __len__(self):
|
|
436
|
+
return len(self._tbl)
|
|
437
|
+
|
|
438
|
+
def add(self, s):
|
|
439
|
+
x = self._tbl.get(s)
|
|
440
|
+
if x is None:
|
|
441
|
+
self._tbl[s] = x = s
|
|
442
|
+
|
|
443
|
+
return x
|
|
444
|
+
|
|
445
|
+
|
|
446
|
+
class ValueList(list):
|
|
447
|
+
pass
|
|
448
|
+
|
|
449
|
+
def dict_add(ddict, name, value):
|
|
450
|
+
ivalue = ddict.get(name, _NONE)
|
|
451
|
+
if ivalue is not _NONE:
|
|
452
|
+
if isinstance(ivalue, ValueList):
|
|
453
|
+
ivalue.append(value)
|
|
454
|
+
else:
|
|
455
|
+
ddict[name] = ValueList((ivalue, value))
|
|
456
|
+
else:
|
|
457
|
+
ddict[name] = value
|
|
458
|
+
|
|
459
|
+
|
|
460
|
+
def dict_update_append(d, **kwargs):
|
|
461
|
+
for k, v in kwargs.items():
|
|
462
|
+
dict_add(d, k, v)
|
|
463
|
+
|
|
464
|
+
|
|
465
|
+
def enum_dict_values(ddict, name):
|
|
466
|
+
ivalue = ddict.get(name, _NONE)
|
|
467
|
+
if ivalue is not _NONE:
|
|
468
|
+
if isinstance(ivalue, ValueList):
|
|
469
|
+
for value in ivalue:
|
|
470
|
+
yield value
|
|
471
|
+
else:
|
|
472
|
+
yield ivalue
|
|
473
|
+
|
|
474
|
+
|
|
475
|
+
def enum_unique(data, skip=None):
|
|
476
|
+
uskip = set(skip or ())
|
|
477
|
+
for x in data:
|
|
478
|
+
if x not in uskip:
|
|
479
|
+
uskip.add(x)
|
|
480
|
+
yield x
|
|
481
|
+
|
|
482
|
+
|
|
483
|
+
def get_property(obj, name, defval=None):
|
|
484
|
+
p = getattr(obj, name, _NONE)
|
|
485
|
+
if p is _NONE:
|
|
486
|
+
return defval
|
|
487
|
+
|
|
488
|
+
return p() if callable(p) else p
|
|
489
|
+
|
|
490
|
+
|
|
491
|
+
def data_rewrite(v, rwfn):
|
|
492
|
+
rwv = rwfn(v)
|
|
493
|
+
if rwv is not None:
|
|
494
|
+
return rwv
|
|
495
|
+
elif isinstance(v, (list, tuple)):
|
|
496
|
+
new_values, rewritten = [], False
|
|
497
|
+
for x in v:
|
|
498
|
+
new_obj = data_rewrite(x, rwfn)
|
|
499
|
+
new_values.append(new_obj)
|
|
500
|
+
rewritten = rewritten or new_obj is not x
|
|
501
|
+
|
|
502
|
+
return type(v)(new_values) if rewritten else v
|
|
503
|
+
elif isdict(v):
|
|
504
|
+
new_values, rewritten = [], False
|
|
505
|
+
for k, x in v.items():
|
|
506
|
+
new_k = data_rewrite(k, rwfn)
|
|
507
|
+
new_x = data_rewrite(x, rwfn)
|
|
508
|
+
new_values.append((new_k, new_x))
|
|
509
|
+
rewritten = rewritten or new_k is not k or new_x is not x
|
|
510
|
+
|
|
511
|
+
return type(v)(new_values) if rewritten else v
|
|
512
|
+
elif hasattr(v, '__dict__'):
|
|
513
|
+
new_values, rewritten = [], False
|
|
514
|
+
for k, x in v.__dict__.items():
|
|
515
|
+
new_k = data_rewrite(k, rwfn)
|
|
516
|
+
new_x = data_rewrite(x, rwfn)
|
|
517
|
+
new_values.append((new_k, new_x))
|
|
518
|
+
rewritten = rewritten or new_k is not k or new_x is not x
|
|
519
|
+
|
|
520
|
+
if rewritten:
|
|
521
|
+
new_obj = copy.copy(v)
|
|
522
|
+
new_obj.__dict__.update(**dict(new_values))
|
|
523
|
+
|
|
524
|
+
return new_obj
|
|
525
|
+
|
|
526
|
+
return v
|
|
527
|
+
else:
|
|
528
|
+
return v
|
|
529
|
+
|
|
530
|
+
|
|
531
|
+
def recurse_apply(obj, name, fn):
|
|
532
|
+
if not fn(obj, name):
|
|
533
|
+
if isinstance(obj, (list, tuple)):
|
|
534
|
+
for i, v in enumerate(obj):
|
|
535
|
+
recurse_apply(v, f'{name}[{i}]', fn)
|
|
536
|
+
elif isdict(obj):
|
|
537
|
+
for k, v in obj.items():
|
|
538
|
+
recurse_apply(v, f'{name}.{k}', fn)
|
|
539
|
+
elif hasattr(obj, '__dict__'):
|
|
540
|
+
for k, v in obj.__dict__.items():
|
|
541
|
+
recurse_apply(v, f'{name}.{k}', fn)
|
|
542
|
+
|
|
543
|
+
|
|
544
|
+
def compute_shape(data):
|
|
545
|
+
sp = get_property(data, 'shape')
|
|
546
|
+
if sp is not None:
|
|
547
|
+
return tuple(sp)
|
|
548
|
+
shape = []
|
|
549
|
+
if hasattr(data, '__len__'):
|
|
550
|
+
shape.append(len(data))
|
|
551
|
+
if shape[0] > 0 and hasattr(data, '__getitem__'):
|
|
552
|
+
shape.extend(compute_shape(data[0]))
|
|
553
|
+
|
|
554
|
+
return tuple(shape)
|
|
555
|
+
|
|
556
|
+
|
|
557
|
+
class RevGen:
|
|
558
|
+
|
|
559
|
+
def __init__(self, fmt=None, revbase=0):
|
|
560
|
+
self._fmt = fmt or '{name}_{ver}'
|
|
561
|
+
self._revbase = revbase
|
|
562
|
+
self._revdb = dict()
|
|
563
|
+
|
|
564
|
+
def getver(self, name, defval=None):
|
|
565
|
+
return self._revdb.get(name, defval)
|
|
566
|
+
|
|
567
|
+
def newver(self, name):
|
|
568
|
+
ver = self._revdb.get(name, self._revbase)
|
|
569
|
+
self._revdb[name] = ver + 1
|
|
570
|
+
|
|
571
|
+
return ver
|
|
572
|
+
|
|
573
|
+
def newname(self, name, shortzero=False):
|
|
574
|
+
ver = self.newver(name)
|
|
575
|
+
|
|
576
|
+
return self._fmt.format(name=name, ver=ver) if ver != 0 or not shortzero else name
|
|
577
|
+
|