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,117 @@
|
|
|
1
|
+
import bisect
|
|
2
|
+
import os
|
|
3
|
+
|
|
4
|
+
from . import alog
|
|
5
|
+
from . import assert_checks as tas
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class IterFile:
|
|
9
|
+
|
|
10
|
+
def __init__(self, data_gen):
|
|
11
|
+
offset, blocks, offsets = 0, [], []
|
|
12
|
+
for block in data_gen:
|
|
13
|
+
blocks.append(block)
|
|
14
|
+
offsets.append(offset)
|
|
15
|
+
offset += len(block)
|
|
16
|
+
|
|
17
|
+
offsets.append(offset)
|
|
18
|
+
|
|
19
|
+
self._blocks = tuple(blocks)
|
|
20
|
+
self._offsets = tuple(offsets)
|
|
21
|
+
self._size = offset
|
|
22
|
+
self._offset = 0
|
|
23
|
+
self._block = None
|
|
24
|
+
self._block_offset = 0
|
|
25
|
+
|
|
26
|
+
def close(self):
|
|
27
|
+
self._blocks = None
|
|
28
|
+
|
|
29
|
+
@property
|
|
30
|
+
def closed(self):
|
|
31
|
+
return self._blocks is None
|
|
32
|
+
|
|
33
|
+
def seek(self, pos, whence=os.SEEK_SET):
|
|
34
|
+
if whence == os.SEEK_SET:
|
|
35
|
+
offset = pos
|
|
36
|
+
elif whence == os.SEEK_CUR:
|
|
37
|
+
offset = self._offset + pos
|
|
38
|
+
elif whence == os.SEEK_END:
|
|
39
|
+
offset = self._size + pos
|
|
40
|
+
else:
|
|
41
|
+
alog.xraise(ValueError, f'Invalid seek mode: {whence}')
|
|
42
|
+
|
|
43
|
+
tas.check_le(offset, self._size, msg=f'Offset out of range')
|
|
44
|
+
tas.check_ge(offset, 0, msg=f'Offset out of range')
|
|
45
|
+
|
|
46
|
+
self._offset = offset
|
|
47
|
+
|
|
48
|
+
return offset
|
|
49
|
+
|
|
50
|
+
def tell(self):
|
|
51
|
+
return self._offset
|
|
52
|
+
|
|
53
|
+
def _ensure_buffer(self, offset):
|
|
54
|
+
boffset = offset - self._block_offset
|
|
55
|
+
if self._block is None or boffset < 0 or boffset >= len(self._block):
|
|
56
|
+
pos = bisect.bisect(self._offsets, offset) - 1
|
|
57
|
+
if pos >= len(self._blocks):
|
|
58
|
+
self._block_offset = self._size
|
|
59
|
+
self._block = b''
|
|
60
|
+
else:
|
|
61
|
+
self._block_offset = self._offsets[pos]
|
|
62
|
+
self._block = memoryview(self._blocks[pos])
|
|
63
|
+
|
|
64
|
+
boffset = offset - self._block_offset
|
|
65
|
+
|
|
66
|
+
return boffset
|
|
67
|
+
|
|
68
|
+
def read(self, size=-1):
|
|
69
|
+
if size < 0:
|
|
70
|
+
rsize = self._size - self._offset
|
|
71
|
+
else:
|
|
72
|
+
rsize = min(size, self._size - self._offset)
|
|
73
|
+
|
|
74
|
+
parts = []
|
|
75
|
+
while rsize > 0:
|
|
76
|
+
boffset = self._ensure_buffer(self._offset)
|
|
77
|
+
|
|
78
|
+
csize = min(rsize, len(self._block) - boffset)
|
|
79
|
+
parts.append(self._block[boffset: boffset + csize])
|
|
80
|
+
self._offset += csize
|
|
81
|
+
rsize -= csize
|
|
82
|
+
|
|
83
|
+
return b''.join(parts)
|
|
84
|
+
|
|
85
|
+
def read1(self, size=-1):
|
|
86
|
+
return self.read(size=size)
|
|
87
|
+
|
|
88
|
+
def peek(self, size=0):
|
|
89
|
+
if size > 0:
|
|
90
|
+
boffset = self._ensure_buffer(self._offset)
|
|
91
|
+
csize = min(size, len(self._block) - boffset)
|
|
92
|
+
|
|
93
|
+
return self._block[boffset: boffset + csize].tobytes()
|
|
94
|
+
|
|
95
|
+
return b''
|
|
96
|
+
|
|
97
|
+
def flush(self):
|
|
98
|
+
pass
|
|
99
|
+
|
|
100
|
+
def readable(self):
|
|
101
|
+
return not self.closed
|
|
102
|
+
|
|
103
|
+
def seekable(self):
|
|
104
|
+
return not self.closed
|
|
105
|
+
|
|
106
|
+
def writable(self):
|
|
107
|
+
return False
|
|
108
|
+
|
|
109
|
+
def __enter__(self):
|
|
110
|
+
return self
|
|
111
|
+
|
|
112
|
+
def __exit__(self, *exc):
|
|
113
|
+
self.close()
|
|
114
|
+
|
|
115
|
+
return False
|
|
116
|
+
|
|
117
|
+
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
from . import utils as ut
|
|
2
|
+
|
|
3
|
+
_KW_TEMPLATE = '''
|
|
4
|
+
class $CLASS:
|
|
5
|
+
|
|
6
|
+
def __init__(self, $KEY, **kwargs):
|
|
7
|
+
self.$KEY = $KEY
|
|
8
|
+
for k, v in kwargs.items():
|
|
9
|
+
setattr(self, k, v)
|
|
10
|
+
|
|
11
|
+
def __lt__(self, other):
|
|
12
|
+
return self.$KEY < other.$KEY
|
|
13
|
+
|
|
14
|
+
def __le__(self, other):
|
|
15
|
+
return self.$KEY <= other.$KEY
|
|
16
|
+
|
|
17
|
+
def __gt__(self, other):
|
|
18
|
+
return self.$KEY > other.$KEY
|
|
19
|
+
|
|
20
|
+
def __ge__(self, other):
|
|
21
|
+
return self.$KEY >= other.$KEY
|
|
22
|
+
|
|
23
|
+
def __eq__(self, other):
|
|
24
|
+
return self.$KEY == other.$KEY
|
|
25
|
+
|
|
26
|
+
def __ne__(self, other):
|
|
27
|
+
return self.$KEY != other.$KEY
|
|
28
|
+
|
|
29
|
+
def __hash__(self):
|
|
30
|
+
return hash(self.$KEY)
|
|
31
|
+
|
|
32
|
+
def __repr__(self):
|
|
33
|
+
args = self.__dict__.copy()
|
|
34
|
+
args.pop('$KEY', None)
|
|
35
|
+
|
|
36
|
+
return f'$KEY=[{self.$KEY}] : {args}'
|
|
37
|
+
'''
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def key_wrap(cname, key_name):
|
|
41
|
+
replaces = dict(CLASS=cname, KEY=key_name)
|
|
42
|
+
|
|
43
|
+
results, = ut.compile(_KW_TEMPLATE, cname, vals=replaces)
|
|
44
|
+
|
|
45
|
+
return results
|
|
46
|
+
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import importlib.util
|
|
2
|
+
import inspect
|
|
3
|
+
import re
|
|
4
|
+
import sys
|
|
5
|
+
|
|
6
|
+
from . import traceback as tb
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def lazy_import(modname, package=None):
|
|
10
|
+
if package is not None and re.match(r'\.+$', package):
|
|
11
|
+
parent_frame = tb.get_frame(1)
|
|
12
|
+
parent_packages = inspect.getmodule(parent_frame).__package__.split('.')
|
|
13
|
+
package_path = parent_packages[: len(parent_packages) - len(package) + 1]
|
|
14
|
+
modname = '.'.join(package_path + [modname])
|
|
15
|
+
package = None
|
|
16
|
+
|
|
17
|
+
spec = importlib.util.find_spec(modname, package=package)
|
|
18
|
+
loader = importlib.util.LazyLoader(spec.loader)
|
|
19
|
+
spec.loader = loader
|
|
20
|
+
module = importlib.util.module_from_spec(spec)
|
|
21
|
+
sys.modules[modname] = module
|
|
22
|
+
loader.exec_module(module)
|
|
23
|
+
|
|
24
|
+
return module
|
|
25
|
+
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
import hashlib
|
|
2
|
+
import os
|
|
3
|
+
import psutil
|
|
4
|
+
import time
|
|
5
|
+
import yaml
|
|
6
|
+
|
|
7
|
+
from . import alog
|
|
8
|
+
from . import assert_checks as tas
|
|
9
|
+
from . import fs_utils as fsu
|
|
10
|
+
from . import obj
|
|
11
|
+
from . import osfd
|
|
12
|
+
from . import rnd_utils as rngu
|
|
13
|
+
from . import tempdir as tmpd
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
_LOCKDIR = tmpd.fastfs_dir(name='.locks')
|
|
17
|
+
|
|
18
|
+
def _lockfile(name):
|
|
19
|
+
lhash = hashlib.sha1(name.encode()).hexdigest()
|
|
20
|
+
|
|
21
|
+
return os.path.join(_LOCKDIR, lhash)
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class Meta(obj.Obj):
|
|
25
|
+
pass
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
_CMDLINE = list(psutil.Process().cmdline())
|
|
29
|
+
_ACQUIRE_TIMEOUT = float(os.getenv('LOCKF_AQTIMEO', 0.5))
|
|
30
|
+
_CHECK_TIMEOUT = float(os.getenv('LOCKF_CKTIMEO', 5.0))
|
|
31
|
+
|
|
32
|
+
class LockFile:
|
|
33
|
+
|
|
34
|
+
def __init__(self, name, acquire_timeout=None, check_timeout=None):
|
|
35
|
+
acquire_timeout = _ACQUIRE_TIMEOUT if acquire_timeout is None else acquire_timeout
|
|
36
|
+
check_timeout = _CHECK_TIMEOUT if check_timeout is None else check_timeout
|
|
37
|
+
|
|
38
|
+
self._name = name
|
|
39
|
+
self._lockfile = _lockfile(name)
|
|
40
|
+
self._acquire_timeout = rngu.uniform(acquire_timeout, pct=0.2)
|
|
41
|
+
self._check_timeout = rngu.uniform(check_timeout, pct=0.2)
|
|
42
|
+
|
|
43
|
+
def _mkmeta(self):
|
|
44
|
+
tag = dict(pid=os.getpid(), cmdline=_CMDLINE, time=time.time())
|
|
45
|
+
stag = yaml.dump(tag, default_flow_style=False)
|
|
46
|
+
|
|
47
|
+
return stag.encode()
|
|
48
|
+
|
|
49
|
+
def _parse_meta(self, data):
|
|
50
|
+
if data:
|
|
51
|
+
try:
|
|
52
|
+
tag = yaml.safe_load(data.decode())
|
|
53
|
+
|
|
54
|
+
return Meta(**tag)
|
|
55
|
+
except:
|
|
56
|
+
pass
|
|
57
|
+
|
|
58
|
+
def _alive_pid(self, meta):
|
|
59
|
+
is_alive = False
|
|
60
|
+
if psutil.pid_exists(meta.pid):
|
|
61
|
+
try:
|
|
62
|
+
proc = psutil.Process(meta.pid)
|
|
63
|
+
cmdline = list(proc.cmdline())
|
|
64
|
+
|
|
65
|
+
is_alive = cmdline == meta.cmdline and proc.create_time() <= meta.time
|
|
66
|
+
except psutil.NoSuchProcess:
|
|
67
|
+
pass
|
|
68
|
+
|
|
69
|
+
if not is_alive:
|
|
70
|
+
alog.warning(f'Process {meta.pid} ({meta.cmdline}) holding lock on ' \
|
|
71
|
+
f'{self._name} seems vanished')
|
|
72
|
+
|
|
73
|
+
return is_alive
|
|
74
|
+
|
|
75
|
+
def acquire(self, timeout=None):
|
|
76
|
+
quit_time = timeout + time.time() if timeout is not None else None
|
|
77
|
+
check_time = time.time() + self._check_timeout
|
|
78
|
+
while True:
|
|
79
|
+
try:
|
|
80
|
+
with osfd.OsFd(self._lockfile, os.O_WRONLY | os.O_CREAT | os.O_EXCL) as fd:
|
|
81
|
+
os.write(fd, self._mkmeta())
|
|
82
|
+
|
|
83
|
+
return True
|
|
84
|
+
except OSError:
|
|
85
|
+
time.sleep(self._acquire_timeout)
|
|
86
|
+
|
|
87
|
+
now = time.time()
|
|
88
|
+
if now >= check_time:
|
|
89
|
+
check_time = now + self._check_timeout
|
|
90
|
+
meta = self._locking_meta()
|
|
91
|
+
if meta is None or not self._alive_pid(meta):
|
|
92
|
+
if self._try_force_lock(meta):
|
|
93
|
+
return True
|
|
94
|
+
|
|
95
|
+
if quit_time is not None and now >= quit_time:
|
|
96
|
+
alog.debug(f'Giving up on lock {self._name} after {timeout} seconds')
|
|
97
|
+
return False
|
|
98
|
+
|
|
99
|
+
def release(self):
|
|
100
|
+
meta = self._locking_meta()
|
|
101
|
+
if meta is not None:
|
|
102
|
+
if meta.pid == os.getpid() and meta.cmdline == _CMDLINE:
|
|
103
|
+
try:
|
|
104
|
+
os.remove(self._lockfile)
|
|
105
|
+
return True
|
|
106
|
+
except OSError:
|
|
107
|
+
pass
|
|
108
|
+
else:
|
|
109
|
+
alog.warning(f'Trying to release lock on {self._name} by pid {os.getpid()} but ' \
|
|
110
|
+
f'it was held by {meta.pid} ({meta.cmdline})')
|
|
111
|
+
else:
|
|
112
|
+
alog.warning(f'Trying to release lock on {self._name} from pid {os.getpid()} but ' \
|
|
113
|
+
f'no lock metadata was found')
|
|
114
|
+
|
|
115
|
+
return False
|
|
116
|
+
|
|
117
|
+
def _try_force_lock(self, meta):
|
|
118
|
+
if meta is not None:
|
|
119
|
+
alog.warning(f'Trying to override gone process {meta.pid} on {self._name}')
|
|
120
|
+
|
|
121
|
+
upath = f'{self._lockfile}.ENFORCER'
|
|
122
|
+
created = result = False
|
|
123
|
+
try:
|
|
124
|
+
with osfd.OsFd(upath, os.O_WRONLY | os.O_CREAT | os.O_EXCL) as fd:
|
|
125
|
+
created = True
|
|
126
|
+
|
|
127
|
+
with osfd.OsFd(self._lockfile, os.O_RDWR) as fd:
|
|
128
|
+
data = fsu.readall(fd)
|
|
129
|
+
|
|
130
|
+
lmeta = self._parse_meta(data)
|
|
131
|
+
if lmeta is None or (meta is not None and lmeta == meta):
|
|
132
|
+
os.lseek(fd, 0, os.SEEK_SET)
|
|
133
|
+
os.truncate(fd, 0)
|
|
134
|
+
os.write(fd, self._mkmeta())
|
|
135
|
+
result = True
|
|
136
|
+
|
|
137
|
+
alog.info(f'Successfull override on {self._name}')
|
|
138
|
+
except OSError:
|
|
139
|
+
pass
|
|
140
|
+
finally:
|
|
141
|
+
if created:
|
|
142
|
+
os.remove(upath)
|
|
143
|
+
|
|
144
|
+
return result
|
|
145
|
+
|
|
146
|
+
def _locking_meta(self):
|
|
147
|
+
try:
|
|
148
|
+
with osfd.OsFd(self._lockfile, os.O_RDONLY) as fd:
|
|
149
|
+
data = fsu.readall(fd)
|
|
150
|
+
|
|
151
|
+
return self._parse_meta(data)
|
|
152
|
+
except OSError:
|
|
153
|
+
pass
|
|
154
|
+
|
|
155
|
+
def __enter__(self):
|
|
156
|
+
self.acquire()
|
|
157
|
+
|
|
158
|
+
return self
|
|
159
|
+
|
|
160
|
+
def __exit__(self, *exc):
|
|
161
|
+
self.release()
|
|
162
|
+
|
|
163
|
+
return False
|
|
164
|
+
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import array
|
|
2
|
+
import sys
|
|
3
|
+
import threading
|
|
4
|
+
|
|
5
|
+
import numpy as np
|
|
6
|
+
|
|
7
|
+
from . import core_utils as cu
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
_LOCK = threading.Lock()
|
|
11
|
+
_TYPES_SIZE = dict()
|
|
12
|
+
|
|
13
|
+
def register(otype, sizefn):
|
|
14
|
+
with _LOCK:
|
|
15
|
+
_TYPES_SIZE[otype] = sizefn
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def _get_sizefn(otype):
|
|
19
|
+
with _LOCK:
|
|
20
|
+
return _TYPES_SIZE.get(otype)
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def std_sizefn(obj):
|
|
24
|
+
return sys.getsizeof(obj)
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
_SIZE_AWARE = {
|
|
28
|
+
float,
|
|
29
|
+
int,
|
|
30
|
+
str,
|
|
31
|
+
bytes,
|
|
32
|
+
bytearray,
|
|
33
|
+
array.array,
|
|
34
|
+
np.ndarray,
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
def _get_size(obj, seen):
|
|
38
|
+
oid = id(obj)
|
|
39
|
+
if oid in seen:
|
|
40
|
+
return 0
|
|
41
|
+
seen.add(oid)
|
|
42
|
+
|
|
43
|
+
otype = type(obj)
|
|
44
|
+
if sizefn := _get_sizefn(otype):
|
|
45
|
+
size = sizefn(obj)
|
|
46
|
+
else:
|
|
47
|
+
size = sys.getsizeof(obj)
|
|
48
|
+
if otype not in _SIZE_AWARE:
|
|
49
|
+
if cu.isdict(obj):
|
|
50
|
+
size += sum(_get_size(v, seen) + _get_size(k, seen) for k, v in obj.items())
|
|
51
|
+
elif ustg := getattr(obj, 'untyped_storage', None):
|
|
52
|
+
# Handle PyTorch tensors.
|
|
53
|
+
size += sys.getsizeof(ustg())
|
|
54
|
+
elif hasattr(obj, '__dict__'):
|
|
55
|
+
size += _get_size(obj.__dict__, seen)
|
|
56
|
+
elif hasattr(obj, '__iter__'):
|
|
57
|
+
size += sum(_get_size(x, seen) for x in obj)
|
|
58
|
+
|
|
59
|
+
return size
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def get_size(obj):
|
|
63
|
+
return _get_size(obj, set())
|
|
64
|
+
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import functools
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
# Copied from fsstat.
|
|
5
|
+
# Used to forward calls from a class, to a memeber of the class itself.
|
|
6
|
+
# Example:
|
|
7
|
+
# @mirror_from(
|
|
8
|
+
# 'stream',
|
|
9
|
+
# [
|
|
10
|
+
# 'read',
|
|
11
|
+
# 'seek',
|
|
12
|
+
# 'write',
|
|
13
|
+
# ],
|
|
14
|
+
# )
|
|
15
|
+
# class MyStream:
|
|
16
|
+
# def __init__(self, stream):
|
|
17
|
+
# self.stream = stream
|
|
18
|
+
#
|
|
19
|
+
def mirror_from(origin_name, methods):
|
|
20
|
+
|
|
21
|
+
def origin_getter(method, obj):
|
|
22
|
+
origin = getattr(obj, origin_name)
|
|
23
|
+
|
|
24
|
+
return getattr(origin, method)
|
|
25
|
+
|
|
26
|
+
def wrapper(cls):
|
|
27
|
+
for method in methods:
|
|
28
|
+
wrapped_method = functools.partial(origin_getter, method)
|
|
29
|
+
setattr(cls, method, property(wrapped_method))
|
|
30
|
+
|
|
31
|
+
return cls
|
|
32
|
+
|
|
33
|
+
return wrapper
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def mirror_attributes(src, dest, attributes):
|
|
37
|
+
for attr in attributes:
|
|
38
|
+
setattr(dest, attr, getattr(src, attr))
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def _mirrored_field(name):
|
|
42
|
+
return f'_mirrored_{name}'
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def mirror_all(src, dest, excludes=(), includes=(), name=None):
|
|
46
|
+
excludes = set(excludes)
|
|
47
|
+
for attr in dir(dest):
|
|
48
|
+
if not attr.startswith('_'):
|
|
49
|
+
excludes.add(attr)
|
|
50
|
+
|
|
51
|
+
includes = set(includes)
|
|
52
|
+
mirrored = []
|
|
53
|
+
for attr in dir(src):
|
|
54
|
+
if (not attr.startswith('_') or attr in includes) and attr not in excludes:
|
|
55
|
+
setattr(dest, attr, getattr(src, attr))
|
|
56
|
+
mirrored.append(attr)
|
|
57
|
+
|
|
58
|
+
if name is not None:
|
|
59
|
+
setattr(dest, _mirrored_field(name), tuple(mirrored))
|
|
60
|
+
|
|
61
|
+
return tuple(mirrored)
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def unmirror(dest, attributes=None, name=None):
|
|
65
|
+
if name is not None:
|
|
66
|
+
mfield = _mirrored_field(name)
|
|
67
|
+
attributes = getattr(dest, mfield, None)
|
|
68
|
+
delattr(dest, mfield)
|
|
69
|
+
|
|
70
|
+
for attr in attributes or ():
|
|
71
|
+
delattr(dest, attr)
|
|
72
|
+
|
py_misc_utils/mmap.py
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import mmap
|
|
2
|
+
import os
|
|
3
|
+
|
|
4
|
+
from . import alog
|
|
5
|
+
from . import osfd
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def file_view(path):
|
|
9
|
+
with osfd.OsFd(path, os.O_RDONLY) as fd:
|
|
10
|
+
mm = mmap.mmap(fd, 0, access=mmap.ACCESS_READ)
|
|
11
|
+
|
|
12
|
+
# We can close the fd, but we cannot close the mmap. When the memoryview
|
|
13
|
+
# will be garbage collected, the buffer protocol used by memoryview will
|
|
14
|
+
# decref the mmap object which will be automatically released.
|
|
15
|
+
return memoryview(mm)
|
|
16
|
+
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
import functools
|
|
2
|
+
import importlib
|
|
3
|
+
import importlib.util
|
|
4
|
+
import inspect
|
|
5
|
+
import os
|
|
6
|
+
import sys
|
|
7
|
+
|
|
8
|
+
from . import alog
|
|
9
|
+
from . import assert_checks as tas
|
|
10
|
+
from . import core_utils as cu
|
|
11
|
+
from . import fs_utils as fsu
|
|
12
|
+
from . import utils as ut
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
_PYINIT = '__init__.py'
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def split_module_name(name):
|
|
19
|
+
pos = name.rfind('.')
|
|
20
|
+
if pos < 0:
|
|
21
|
+
return '', name
|
|
22
|
+
|
|
23
|
+
return name[: pos], name[pos + 1:]
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def add_sys_path(path):
|
|
27
|
+
cu.append_if_missing(sys.path, path)
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def find_module_parent(path):
|
|
31
|
+
parts = fsu.path_split(path)
|
|
32
|
+
modname = fsu.drop_ext(parts.pop(), '.py')
|
|
33
|
+
|
|
34
|
+
for i in range(len(parts)):
|
|
35
|
+
ipath = os.path.join(*parts[: i + 1], _PYINIT)
|
|
36
|
+
if os.path.isfile(ipath):
|
|
37
|
+
return ipath, parts[i + 1:] + [modname]
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def find_module(path):
|
|
41
|
+
apath = os.path.abspath(path)
|
|
42
|
+
|
|
43
|
+
for modname, module in sys.modules.items():
|
|
44
|
+
mpath = module_file(module)
|
|
45
|
+
if mpath is not None and mpath == apath:
|
|
46
|
+
return modname, module
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def install_module(modname, module):
|
|
50
|
+
xmodule = sys.modules.get(modname)
|
|
51
|
+
if xmodule is not None:
|
|
52
|
+
modfile, xmodfile = module_file(module), module_file(xmodule)
|
|
53
|
+
tas.check_eq(modfile, xmodfile,
|
|
54
|
+
msg=f'Module "{modname}" already defined at "{xmodfile}"')
|
|
55
|
+
else:
|
|
56
|
+
alog.debug(f'Installing module at "{module_file(module)}" with "{modname}" name')
|
|
57
|
+
sys.modules[modname] = module
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def load_module(path, modname=None, install=None, add_syspath=None):
|
|
61
|
+
install = install or True
|
|
62
|
+
add_syspath = add_syspath or False
|
|
63
|
+
|
|
64
|
+
name_and_module = find_module(path)
|
|
65
|
+
if name_and_module is not None:
|
|
66
|
+
fmodname, module = name_and_module
|
|
67
|
+
alog.debug(f'Found existing module "{fmodname}" for "{path}"')
|
|
68
|
+
if install and modname is not None and modname != fmodname:
|
|
69
|
+
install_module(modname, module)
|
|
70
|
+
|
|
71
|
+
return module
|
|
72
|
+
|
|
73
|
+
apath = os.path.abspath(path)
|
|
74
|
+
|
|
75
|
+
parent = find_module_parent(apath) if os.path.basename(apath) != _PYINIT else None
|
|
76
|
+
if parent is not None:
|
|
77
|
+
init_path, mod_path = parent
|
|
78
|
+
|
|
79
|
+
parent_module = load_module(init_path,
|
|
80
|
+
install=True,
|
|
81
|
+
add_syspath=add_syspath)
|
|
82
|
+
|
|
83
|
+
for i in range(len(mod_path) - 1):
|
|
84
|
+
partial = mod_path[: i + 1]
|
|
85
|
+
ipath = os.path.join(os.path.dirname(parent_module.__file__), *partial, _PYINIT)
|
|
86
|
+
if os.path.isfile(ipath):
|
|
87
|
+
imodname = parent_module.__name__ + '.' + '.'.join(partial)
|
|
88
|
+
load_module(ipath, modname=imodname, install=True)
|
|
89
|
+
|
|
90
|
+
imodname = parent_module.__name__ + '.' + '.'.join(mod_path)
|
|
91
|
+
alog.debug(f'Importing sub-module "{imodname}"')
|
|
92
|
+
module = importlib.import_module(imodname)
|
|
93
|
+
else:
|
|
94
|
+
pathdir = syspath = os.path.dirname(apath)
|
|
95
|
+
|
|
96
|
+
if modname is None:
|
|
97
|
+
modname = fsu.drop_ext(os.path.basename(apath), '.py')
|
|
98
|
+
if modname == '__init__':
|
|
99
|
+
syspath, modname = os.path.split(pathdir)
|
|
100
|
+
|
|
101
|
+
alog.debug(f'Installing module "{apath}" with name "{modname}" (syspath is "{syspath}")')
|
|
102
|
+
|
|
103
|
+
if add_syspath:
|
|
104
|
+
add_sys_path(syspath)
|
|
105
|
+
|
|
106
|
+
modspec = importlib.util.spec_from_file_location(
|
|
107
|
+
modname, apath,
|
|
108
|
+
submodule_search_locations=[pathdir])
|
|
109
|
+
module = importlib.util.module_from_spec(modspec)
|
|
110
|
+
|
|
111
|
+
if install:
|
|
112
|
+
install_module(modname, module)
|
|
113
|
+
|
|
114
|
+
modspec.loader.exec_module(module)
|
|
115
|
+
|
|
116
|
+
return module
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
def import_module(name_or_path,
|
|
120
|
+
modname=None,
|
|
121
|
+
install=None,
|
|
122
|
+
add_syspath=None,
|
|
123
|
+
package=None):
|
|
124
|
+
if os.path.isfile(name_or_path):
|
|
125
|
+
module = load_module(name_or_path,
|
|
126
|
+
modname=modname,
|
|
127
|
+
install=install,
|
|
128
|
+
add_syspath=add_syspath)
|
|
129
|
+
else:
|
|
130
|
+
alog.debug(f'Loading module "{name_or_path}')
|
|
131
|
+
module = importlib.import_module(name_or_path, package=package)
|
|
132
|
+
|
|
133
|
+
if modname is not None and install in (True, None):
|
|
134
|
+
install_module(modname, module)
|
|
135
|
+
|
|
136
|
+
return module
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
def _module_getter(dot_path, obj):
|
|
140
|
+
for name in dot_path.split('.'):
|
|
141
|
+
try:
|
|
142
|
+
obj = getattr(obj, name)
|
|
143
|
+
except AttributeError:
|
|
144
|
+
if inspect.ismodule(obj):
|
|
145
|
+
obj = importlib.import_module(obj.__name__ + '.' + name)
|
|
146
|
+
else:
|
|
147
|
+
raise
|
|
148
|
+
|
|
149
|
+
return obj
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
def module_getter(dot_path):
|
|
153
|
+
return functools.partial(_module_getter, dot_path)
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
def import_module_names(modname, names=None):
|
|
157
|
+
if names is None:
|
|
158
|
+
npos = modname.find('.')
|
|
159
|
+
tas.check_gt(npos, 0)
|
|
160
|
+
names = [modname[npos + 1:]]
|
|
161
|
+
modname = modname[: npos]
|
|
162
|
+
else:
|
|
163
|
+
names = ut.expand_strings(names)
|
|
164
|
+
|
|
165
|
+
module = importlib.import_module(modname)
|
|
166
|
+
|
|
167
|
+
return tuple(module_getter(name)(module) for name in names)
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
def rel_import_module(path, ppath,
|
|
171
|
+
modname=None,
|
|
172
|
+
install=None,
|
|
173
|
+
add_syspath=None):
|
|
174
|
+
ipath = os.path.abspath(os.path.join(os.path.dirname(ppath), f'{path}.py'))
|
|
175
|
+
|
|
176
|
+
return import_module(ipath, modname=modname, install=install, add_syspath=add_syspath)
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
def maybe_import(modname, package=None):
|
|
180
|
+
try:
|
|
181
|
+
return importlib.import_module(modname, package=package)
|
|
182
|
+
except:
|
|
183
|
+
alog.spam(f'Unable to load module "{modname}" in package "{package}"')
|
|
184
|
+
|
|
185
|
+
|
|
186
|
+
def module_file(module):
|
|
187
|
+
return getattr(module, '__file__', None)
|
|
188
|
+
|
|
189
|
+
|
|
190
|
+
def clone_module(modname):
|
|
191
|
+
specs = importlib.util.find_spec(modname)
|
|
192
|
+
module = importlib.util.module_from_spec(specs)
|
|
193
|
+
specs.loader.exec_module(module)
|
|
194
|
+
|
|
195
|
+
return module
|
|
196
|
+
|