omlish 0.0.0.dev236__py3-none-any.whl → 0.0.0.dev237__py3-none-any.whl

Sign up to get free protection for your applications and to get access to all the features.
omlish/__about__.py CHANGED
@@ -1,5 +1,5 @@
1
- __version__ = '0.0.0.dev236'
2
- __revision__ = '492e9f7254561d4c5576a6df3dfb94fdf94d9f7a'
1
+ __version__ = '0.0.0.dev237'
2
+ __revision__ = 'e8c58292f1ec88727fd832f6496545435d992e9d'
3
3
 
4
4
 
5
5
  #
omlish/configs/classes.py CHANGED
@@ -1,53 +1,31 @@
1
1
  import typing as ta
2
- import weakref
3
2
 
4
3
  from .. import check
5
4
  from .. import dataclasses as dc
6
5
  from .. import lang
7
6
 
8
7
 
9
- class Config(
10
- dc.Data,
11
- lang.Abstract,
12
- frozen=True,
13
- reorder=True,
14
- confer=frozenset([
15
- 'frozen',
16
- 'reorder',
17
- 'confer',
18
- ]),
19
- ):
20
- pass
8
+ ConfigurableConfigT = ta.TypeVar('ConfigurableConfigT', bound='Configurable.Config')
21
9
 
22
10
 
23
- ConfigT = ta.TypeVar('ConfigT', bound='Config')
11
+ class Configurable(ta.Generic[ConfigurableConfigT], lang.Abstract):
12
+ @dc.dataclass(frozen=True, kw_only=True)
13
+ class Config:
14
+ """Does not use any dc metaclasses to preserve typechecking."""
24
15
 
25
- _CONFIG_CLS_MAP: ta.MutableMapping[type[Config], type['Configurable']] = weakref.WeakValueDictionary()
26
-
27
-
28
- class Configurable(ta.Generic[ConfigT], lang.Abstract):
29
-
30
- # FIXME: https://github.com/python/mypy/issues/5144
31
- Config: ta.ClassVar[type[ConfigT]] # type: ignore # noqa
16
+ configurable_cls: ta.ClassVar[type['Configurable']]
32
17
 
33
18
  def __init_subclass__(cls, **kwargs: ta.Any) -> None:
34
19
  super().__init_subclass__(**kwargs)
35
20
 
36
- cfg_cls = check.issubclass(cls.__dict__['Config'], Config)
37
- check.not_in(cfg_cls, _CONFIG_CLS_MAP)
38
- _CONFIG_CLS_MAP[cfg_cls] = cls
21
+ if not lang.is_abstract_class(cls):
22
+ check.in_('Config', cls.__dict__)
23
+ cfg_cls = check.issubclass(cls.Config, Configurable.Config)
24
+ check.not_in('configurable_cls', cfg_cls.__dict__)
25
+ check.state(dc.is_immediate_dataclass(cfg_cls))
26
+ cfg_cls.configurable_cls = cls
39
27
 
40
- def __init__(self, config: ConfigT) -> None:
28
+ def __init__(self, config: ConfigurableConfigT) -> None:
41
29
  super().__init__()
42
30
 
43
- self._config: ConfigT = check.isinstance(config, self.Config)
44
-
45
-
46
- def get_impl(cfg: type[Config] | Config) -> type[Configurable]:
47
- if isinstance(cfg, type):
48
- cfg_cls = check.issubclass(cfg, Config) # noqa
49
- elif isinstance(cfg, Config):
50
- cfg_cls = type(cfg)
51
- else:
52
- raise TypeError(cfg)
53
- return _CONFIG_CLS_MAP[cfg_cls]
31
+ self._config: ConfigurableConfigT = check.isinstance(config, self.Config) # type: ignore[assignment]
@@ -13,6 +13,10 @@ from .matching import ( # noqa
13
13
  matched_config_rewrite as matched_rewrite,
14
14
  )
15
15
 
16
+ from .merging import ( # noqa
17
+ merge_configs as merge,
18
+ )
19
+
16
20
  from .names import ( # noqa
17
21
  build_config_named_children as build_named_children,
18
22
  )
@@ -0,0 +1,33 @@
1
+ # ruff: noqa: UP006 UP007
2
+ # @omlish-lite
3
+ """
4
+ TODO:
5
+ - smarter merging than just dumb dict-squashing
6
+ """
7
+ import typing as ta
8
+
9
+ from ..types import ConfigMap
10
+
11
+
12
+ ##
13
+
14
+
15
+ def merge_configs(*ms: ConfigMap) -> ConfigMap:
16
+ def rec(o, i):
17
+ for k, v in i.items():
18
+ try:
19
+ e = o[k]
20
+ except KeyError:
21
+ o[k] = v
22
+ else:
23
+ if isinstance(e, ta.Mapping) and isinstance(v, ta.Mapping):
24
+ rec(e, v) # noqa
25
+ else:
26
+ if isinstance(v, ta.Mapping):
27
+ v = dict(v)
28
+ o[k] = v
29
+
30
+ o: dict = {}
31
+ for i in ms:
32
+ rec(o, i)
33
+ return o
@@ -0,0 +1,92 @@
1
+ # ruff: noqa: UP006 UP007
2
+ # @omlish-lite
3
+ """
4
+ TODO:
5
+ - cascading?
6
+ - caching?
7
+ - purging?
8
+ - wishlist: piecewise config parts, unmarshal, registered via manifests
9
+ """
10
+ import abc
11
+ import os.path
12
+ import typing as ta
13
+
14
+ from ..lite.check import check
15
+ from ..os.mangle import mangle_path
16
+ from .formats import DEFAULT_CONFIG_LOADERS
17
+ from .formats import ConfigLoader
18
+
19
+
20
+ ShadowConfig = ta.Mapping[str, ta.Any] # ta.TypeAlias
21
+
22
+
23
+ ##
24
+
25
+
26
+ class ShadowConfigs(abc.ABC):
27
+ @abc.abstractmethod
28
+ def get_shadow_config(self, path: str) -> ta.Optional[ShadowConfig]:
29
+ raise NotImplementedError
30
+
31
+
32
+ class FileShadowConfigs(ShadowConfigs, abc.ABC):
33
+ @abc.abstractmethod
34
+ def get_shadow_config_file_path(self, path: str) -> str:
35
+ raise NotImplementedError
36
+
37
+
38
+ ##
39
+
40
+
41
+ class NopShadowConfigs(ShadowConfigs):
42
+ def get_shadow_config(self, path: str) -> ta.Optional[ShadowConfig]:
43
+ return None
44
+
45
+
46
+ ##
47
+
48
+
49
+ class MangledFilesShadowConfigs(FileShadowConfigs):
50
+ def __init__(
51
+ self,
52
+ path: str,
53
+ *,
54
+ create: bool = False,
55
+ ext: str = 'yml',
56
+ loader: ta.Optional[ConfigLoader] = None,
57
+ ) -> None: # noqa
58
+ super().__init__()
59
+
60
+ check.arg(not ext.startswith('.'))
61
+ check.non_empty_str(ext)
62
+
63
+ if loader is None:
64
+ for dl in DEFAULT_CONFIG_LOADERS:
65
+ if dl.match_file(f'.{ext}'):
66
+ loader = dl
67
+ break
68
+ else:
69
+ raise KeyError(ext)
70
+
71
+ self._path = path
72
+ self._create = create
73
+ self._ext = ext
74
+ self._loader = loader
75
+
76
+ def get_shadow_config_file_path(self, path: str) -> str:
77
+ mangled = '.'.join([mangle_path(os.path.abspath(path)), self._ext])
78
+ return os.path.abspath(os.path.join(self._path, mangled))
79
+
80
+ def get_shadow_config(self, path: str) -> ta.Optional[ShadowConfig]:
81
+ file_path = self.get_shadow_config_file_path(path)
82
+
83
+ if self._create:
84
+ os.makedirs(self._path, exist_ok=True)
85
+
86
+ try:
87
+ with open(file_path) as f:
88
+ content = f.read()
89
+ except FileNotFoundError:
90
+ return None
91
+
92
+ return self._loader.load_str(content).as_map()
omlish/daemons/daemon.py CHANGED
@@ -38,14 +38,9 @@ log = logging.getLogger(__name__)
38
38
 
39
39
 
40
40
  class Daemon:
41
- """
42
- Instances of these should most likely be effectively singletons, but that's up to the user.
43
- """
44
-
45
41
  @dc.dataclass(frozen=True, kw_only=True)
46
42
  class Config:
47
- target: Target
48
- spawning: Spawning
43
+ spawning: Spawning | None = None
49
44
 
50
45
  #
51
46
 
@@ -68,11 +63,20 @@ class Daemon:
68
63
  def __post_init__(self) -> None:
69
64
  check.isinstance(self.pid_file, (str, None))
70
65
 
71
- def __init__(self, config: Config) -> None:
66
+ def __init__(
67
+ self,
68
+ target: Target,
69
+ config: Config = Config(),
70
+ ) -> None:
72
71
  super().__init__()
73
72
 
73
+ self._target = target
74
74
  self._config = config
75
75
 
76
+ @property
77
+ def target(self) -> Target:
78
+ return self._target
79
+
76
80
  @property
77
81
  def config(self) -> Config:
78
82
  return self._config
@@ -124,8 +128,8 @@ class Daemon:
124
128
 
125
129
  def launch_no_wait(self) -> None:
126
130
  launcher = Launcher(
127
- target=self._config.target,
128
- spawning=self._config.spawning,
131
+ target=self._target,
132
+ spawning=check.not_none(self._config.spawning),
129
133
 
130
134
  pid_file=self._config.pid_file,
131
135
  reparent_process=self._config.reparent_process,
@@ -0,0 +1,34 @@
1
+ import abc
2
+ import typing as ta
3
+
4
+ from .. import check
5
+ from .. import lang
6
+ from ..configs.classes import Configurable
7
+
8
+
9
+ ServiceConfigT = ta.TypeVar('ServiceConfigT', bound='Service.Config')
10
+
11
+
12
+ ##
13
+
14
+
15
+ class Service(Configurable[ServiceConfigT], lang.Abstract):
16
+ class Config(Configurable.Config, lang.Abstract):
17
+ pass
18
+
19
+ @classmethod
20
+ def from_config(cls, config: Config) -> 'Service':
21
+ return check.isinstance(config.configurable_cls(config), cls)
22
+
23
+ #
24
+
25
+ @abc.abstractmethod
26
+ def _run(self) -> None:
27
+ raise NotImplementedError
28
+
29
+ def run(self) -> None:
30
+ self._run()
31
+
32
+ @classmethod
33
+ def run_config(cls, config: Config) -> None:
34
+ return cls.from_config(config).run()
omlish/daemons/targets.py CHANGED
@@ -6,6 +6,7 @@ import typing as ta
6
6
  from .. import check
7
7
  from .. import dataclasses as dc
8
8
  from .. import lang
9
+ from .services import Service
9
10
 
10
11
 
11
12
  if ta.TYPE_CHECKING:
@@ -18,7 +19,22 @@ else:
18
19
 
19
20
 
20
21
  class Target(dc.Case):
21
- pass
22
+ @classmethod
23
+ def of(cls, obj: ta.Any) -> 'Target':
24
+ if isinstance(obj, Target):
25
+ return obj
26
+
27
+ elif isinstance(obj, str):
28
+ return NameTarget(obj)
29
+
30
+ elif callable(obj):
31
+ return FnTarget(obj)
32
+
33
+ elif isinstance(obj, Service.Config):
34
+ return ServiceConfigTarget(obj)
35
+
36
+ else:
37
+ raise TypeError(obj)
22
38
 
23
39
 
24
40
  class TargetRunner(abc.ABC):
@@ -36,14 +52,18 @@ def target_runner_for(target: Target) -> TargetRunner:
36
52
 
37
53
 
38
54
  class FnTarget(Target):
39
- fn: ta.Callable[[], None]
55
+ fn: ta.Callable[[], ta.Any]
40
56
 
41
57
 
42
58
  class FnTargetRunner(TargetRunner, dc.Frozen):
43
59
  target: FnTarget
44
60
 
45
61
  def run(self) -> None:
46
- self.target.fn()
62
+ obj = self.target.fn()
63
+ if obj is not None:
64
+ tgt = Target.of(obj)
65
+ tr = target_runner_for(tgt)
66
+ tr.run()
47
67
 
48
68
 
49
69
  @target_runner_for.register
@@ -57,6 +77,23 @@ def _(target: FnTarget) -> FnTargetRunner:
57
77
  class NameTarget(Target):
58
78
  name: str
59
79
 
80
+ @classmethod
81
+ def for_obj(
82
+ cls,
83
+ obj: ta.Any,
84
+ *,
85
+ globals: ta.Mapping[str, ta.Any] | None = None, # noqa
86
+ no_module_name_lookup: bool = False,
87
+ ) -> 'NameTarget':
88
+ if globals is None:
89
+ globals = obj.__globals__ # noqa
90
+ if not no_module_name_lookup:
91
+ mn = lang.get_real_module_name(globals)
92
+ else:
93
+ mn = globals['__name__']
94
+ qn = obj.__qualname__
95
+ return NameTarget('.'.join([mn, qn]))
96
+
60
97
 
61
98
  class NameTargetRunner(TargetRunner, dc.Frozen):
62
99
  target: NameTarget
@@ -67,7 +104,14 @@ class NameTargetRunner(TargetRunner, dc.Frozen):
67
104
  runpy._run_module_as_main(name) # type: ignore # noqa
68
105
  else:
69
106
  obj = lang.import_attr(self.target.name)
70
- obj()
107
+ tgt = Target.of(obj)
108
+ tr = target_runner_for(tgt)
109
+ tr.run()
110
+
111
+
112
+ @target_runner_for.register
113
+ def _(target: NameTarget) -> NameTargetRunner:
114
+ return NameTargetRunner(target)
71
115
 
72
116
 
73
117
  ##
@@ -76,14 +120,38 @@ class NameTargetRunner(TargetRunner, dc.Frozen):
76
120
  class ExecTarget(Target):
77
121
  cmd: ta.Sequence[str] = dc.xfield(coerce=check.of_not_isinstance(str))
78
122
 
123
+ cwd: str | None = None
124
+
79
125
 
80
126
  class ExecTargetRunner(TargetRunner, dc.Frozen):
81
127
  target: ExecTarget
82
128
 
83
129
  def run(self) -> None:
130
+ if (cwd := self.target.cwd) is not None:
131
+ os.chdir(os.path.expanduser(cwd))
132
+
84
133
  os.execl(*self.target.cmd)
85
134
 
86
135
 
87
136
  @target_runner_for.register
88
137
  def _(target: ExecTarget) -> ExecTargetRunner:
89
138
  return ExecTargetRunner(target)
139
+
140
+
141
+ ##
142
+
143
+
144
+ class ServiceConfigTarget(Target):
145
+ cfg: Service.Config
146
+
147
+
148
+ class ServiceConfigTargetRunner(TargetRunner, dc.Frozen):
149
+ target: ServiceConfigTarget
150
+
151
+ def run(self) -> None:
152
+ Service.run_config(self.target.cfg)
153
+
154
+
155
+ @target_runner_for.register
156
+ def _(target: ServiceConfigTarget) -> ServiceConfigTargetRunner:
157
+ return ServiceConfigTargetRunner(target)
omlish/lang/__init__.py CHANGED
@@ -135,6 +135,7 @@ from .generators import ( # noqa
135
135
 
136
136
  from .imports import ( # noqa
137
137
  can_import,
138
+ get_real_module_name,
138
139
  import_all,
139
140
  import_attr,
140
141
  import_module,
omlish/lang/imports.py CHANGED
@@ -384,3 +384,18 @@ def proxy_init(
384
384
  raise Exception(f'Wrong init name: {pi.name_package=} != {init_name_package=}')
385
385
 
386
386
  pi.add(package, attrs)
387
+
388
+
389
+ ##
390
+
391
+
392
+ def get_real_module_name(globals: ta.Mapping[str, ta.Any]) -> str: # noqa
393
+ module = sys.modules[globals['__name__']]
394
+
395
+ if module.__spec__ and module.__spec__.name:
396
+ return module.__spec__.name
397
+
398
+ if module.__package__:
399
+ return module.__package__
400
+
401
+ raise RuntimeError("Can't determine real module name")
omlish/os/filemodes.py CHANGED
@@ -77,6 +77,48 @@ class FileMode:
77
77
  )
78
78
  )
79
79
 
80
+ @classmethod
81
+ def from_flags(cls, i: int) -> 'FileMode':
82
+ if i & os.O_RDWR:
83
+ i &= ~os.O_RDWR
84
+ read = write = True
85
+ elif i & os.O_WRONLY:
86
+ i &= ~os.O_WRONLY
87
+ write = True
88
+ read = False
89
+ else:
90
+ read = True
91
+ write = False
92
+
93
+ create = False
94
+ if i & os.O_CREAT:
95
+ i &= ~os.O_CREAT
96
+ create = True
97
+
98
+ exists: str | None = None
99
+ if i & os.O_TRUNC:
100
+ i &= ~os.O_TRUNC
101
+ exists = check.replacing_none(exists, 'truncate')
102
+ if i & os.O_EXCL:
103
+ i &= ~os.O_EXCL
104
+ exists = check.replacing_none(exists, 'fail')
105
+ if i & os.O_APPEND:
106
+ i &= ~os.O_APPEND
107
+ exists = check.replacing_none(exists, 'append')
108
+ if exists is None:
109
+ exists = 'beginning'
110
+
111
+ if i:
112
+ raise ValueError(i)
113
+
114
+ return FileMode(
115
+ read=read,
116
+ write=write,
117
+ create=create,
118
+ exists=exists, # type: ignore
119
+ binary=True,
120
+ )
121
+
80
122
  #
81
123
 
82
124
  def render(self) -> str:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: omlish
3
- Version: 0.0.0.dev236
3
+ Version: 0.0.0.dev237
4
4
  Summary: omlish
5
5
  Author: wrmsr
6
6
  License: BSD-3-Clause
@@ -1,5 +1,5 @@
1
1
  omlish/.manifests.json,sha256=vQTAIvR8OblSq-uP2GUfnbei0RnmAnM5j0T1-OToh9E,8253
2
- omlish/__about__.py,sha256=LvhY-W_-OyVINq69cnzbsy9yqgwd-YJ0WXl4tSRUHDI,3380
2
+ omlish/__about__.py,sha256=0tS5CFbCYIp6LwAIjSNK3lhW-DW7VWodegiD0bAa7U8,3380
3
3
  omlish/__init__.py,sha256=SsyiITTuK0v74XpKV8dqNaCmjOlan1JZKrHQv5rWKPA,253
4
4
  omlish/c3.py,sha256=ubu7lHwss5V4UznbejAI0qXhXahrU01MysuHOZI9C4U,8116
5
5
  omlish/cached.py,sha256=UI-XTFBwA6YXWJJJeBn-WkwBkfzDjLBBaZf4nIJA9y0,510
@@ -163,24 +163,27 @@ omlish/concurrent/futures.py,sha256=J2s9wYURUskqRJiBbAR0PNEAp1pXbIMYldOVBTQduQY,
163
163
  omlish/concurrent/threadlets.py,sha256=JfirbTDJgy9Ouokz_VmHeAAPS7cih8qMUJrN-owwXD4,2423
164
164
  omlish/configs/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
165
165
  omlish/configs/all.py,sha256=kziwjzUBkf8AT0w7Pq7JX2jtkQVOQ5R1wJyn6hfTN5k,1055
166
- omlish/configs/classes.py,sha256=GLbB8xKjHjjoUQRCUQm3nEjM8z1qNTx9gPV7ODSt5dg,1317
166
+ omlish/configs/classes.py,sha256=oCCkGzokn807mAlpRVe9F_JKZo8x6JcIVmo2sBsm7T8,1080
167
167
  omlish/configs/formats.py,sha256=RJw4Rzp7vlTd5YyAvpAoruQnk45v8dGPtPWwqH7aYyE,5301
168
168
  omlish/configs/nginx.py,sha256=XuX9yyb0_MwkJ8esKiMS9gFkqHUPza_uCprhnWykNy8,2051
169
+ omlish/configs/shadow.py,sha256=-R5nbevC4pFgqDPYOCCIqNcusgXsMWlRIUOEG0HBoJg,2228
169
170
  omlish/configs/types.py,sha256=t5_32MVRSKxbxor1hl1wRGKYm75F6Atisd_RYsN5ELU,103
170
171
  omlish/configs/processing/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
171
- omlish/configs/processing/all.py,sha256=roIQ7EZJXSzsyUxNHNJaZsXcD2jdT46Ly1XXfG8xEcI,722
172
+ omlish/configs/processing/all.py,sha256=L23FOGn24As3jcB3B11R7PcHWXbYjkItrNq5ZEylPjA,784
172
173
  omlish/configs/processing/flattening.py,sha256=1duZH5zbrGuoYqaJco5LN5qx6e76QP5pHbIyItkQsFY,5021
173
174
  omlish/configs/processing/inheritance.py,sha256=lFD8eWRE0cG0Z3-eCu7pMsP2skWhUNaoAtWi9fNfn8k,1218
174
175
  omlish/configs/processing/matching.py,sha256=R64RxpPB1uX5Ztvvk2dQ2xi_xwlaxkxQgZwtDVR9spY,1514
176
+ omlish/configs/processing/merging.py,sha256=PqpxHLaC6XKetsewS_x0Bh-uXoI2ukZfIKFUAAN5dkg,691
175
177
  omlish/configs/processing/names.py,sha256=weHmaTclzgM9lUn3aBtw-kwZ3mc2N-CZlFg3Kd_UsKo,1093
176
178
  omlish/configs/processing/rewriting.py,sha256=v7PfHtuTn5v_5Y6Au7oMN2Z0nxAMy1iYyO5CXnTvZhs,4226
177
179
  omlish/configs/processing/strings.py,sha256=qFS2oh6z02IaM_q4lTKLdufzkJqAJ6J-Qjrz5S-QJoM,826
178
180
  omlish/daemons/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
179
- omlish/daemons/daemon.py,sha256=CfcWdJB6HrGccHAKW_L2weZK84B6FlZER2zMsfqMVog,3348
181
+ omlish/daemons/daemon.py,sha256=ykdbCPbpxKrdZVZc892SnedTdftTAYt_YdDpYchKcUE,3410
180
182
  omlish/daemons/launching.py,sha256=mhtkuAO16STcznUl3rrX9pacfrKbPQRCP2AllKL4B70,3664
181
183
  omlish/daemons/reparent.py,sha256=UaG2X6VJHJPOlUwHPNRH3aWGgF0Fg771jjO9IRPLlyY,280
184
+ omlish/daemons/services.py,sha256=XRmYBYNLUumnxaXCr2B7ScNEUlM8oM5FSS0bdWheDNs,719
182
185
  omlish/daemons/spawning.py,sha256=cx00xeqSrfhlFbjCtKqaBHvMuHwB9hdjuKNHzAAo_dw,4030
183
- omlish/daemons/targets.py,sha256=scq6BYgzi0H2apfgD74U0D8_msQuYAel3Qiij74YWVo,1501
186
+ omlish/daemons/targets.py,sha256=L2BRWsv473X7dE4H7afA8TgTiaevCuZPab9Olh-AKUc,3248
184
187
  omlish/daemons/waiting.py,sha256=RfgD1L33QQVbD2431dkKZGE4w6DUcGvYeRXXi8puAP4,1676
185
188
  omlish/dataclasses/__init__.py,sha256=D7I6ZJjEFeLN2re8oOZ_7JKWiG2plrPnaUFq3iEXYmQ,1553
186
189
  omlish/dataclasses/utils.py,sha256=N2seT8cJtfOv-41D7F3E-q4us-FCTQmnxxPv3dt1OcI,3796
@@ -392,7 +395,7 @@ omlish/iterators/iterators.py,sha256=ghI4dO6WPyyFOLTIIMaHQ_IOy2xXaFpGPqveZ5YGIBU
392
395
  omlish/iterators/recipes.py,sha256=53mkexitMhkwXQZbL6DrhpT0WePQ_56uXd5Jaw3DfzI,467
393
396
  omlish/iterators/tools.py,sha256=Pi4ybXytUXVZ3xwK89xpPImQfYYId9p1vIFQvVqVLqA,2551
394
397
  omlish/iterators/unique.py,sha256=0jAX3kwzVfRNhe0Tmh7kVP_Q2WBIn8POo_O-rgFV0rQ,1390
395
- omlish/lang/__init__.py,sha256=bE8ra_QM5M4k8v3qFtreTjQswaZClIOaqXt9fE43MD0,4079
398
+ omlish/lang/__init__.py,sha256=rS9JPXiHchs2_9QG7JCx9pbsYpgpe3ANUumDBb8hSFA,4105
396
399
  omlish/lang/cached.py,sha256=tQaqMu1LID0q4NSTk5vPXsgxIBWSFAmjs5AhQoEHoCQ,7833
397
400
  omlish/lang/clsdct.py,sha256=sJYadm-fwzti-gsi98knR5qQUxriBmOqQE_qz3RopNk,1743
398
401
  omlish/lang/cmp.py,sha256=5vbzWWbqdzDmNKAGL19z6ZfUKe5Ci49e-Oegf9f4BsE,1346
@@ -402,7 +405,7 @@ omlish/lang/descriptors.py,sha256=njkYDS1gn5p4-3v1jr-s_srauC7tvvt571RjE7Q4LXE,66
402
405
  omlish/lang/exceptions.py,sha256=qJBo3NU1mOWWm-NhQUHCY5feYXR3arZVyEHinLsmRH4,47
403
406
  omlish/lang/functions.py,sha256=0ql9EXA_gEEhvUVzMJCjVhEnVtHecsLKmfmAXuQqeGY,4388
404
407
  omlish/lang/generators.py,sha256=5LX17j-Ej3QXhwBgZvRTm_dq3n9veC4IOUcVmvSu2vU,5243
405
- omlish/lang/imports.py,sha256=FMPz1lIDmuoapM88uabXeZbeJX1uCT8pkHeukC3X4qc,10129
408
+ omlish/lang/imports.py,sha256=nKPTHVW5gUpT_Ok8QsPyj7b-ToE7nWN2ev9MsI4pRCA,10465
406
409
  omlish/lang/iterables.py,sha256=HOjcxOwyI5bBApDLsxRAGGhTTmw7fdZl2kEckxRVl-0,1994
407
410
  omlish/lang/maybes.py,sha256=dAgrUoAhCgyrHRqa73CkaGnpXwGc-o9n-NIThrNXnbU,3416
408
411
  omlish/lang/objects.py,sha256=65XsD7UtblRdNe2ID1-brn_QvRkJhBIk5nyZWcQNeqU,4574
@@ -515,7 +518,7 @@ omlish/os/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
515
518
  omlish/os/atomics.py,sha256=KhWNeh4mzU3M-TF0v8uR6hUqMfZJW42MeyIK9Jl6R0k,5246
516
519
  omlish/os/deathsig.py,sha256=hk9Yq2kyDdI-cI7OQH7mOfpRbOKzY_TfPKEqgrjVYbA,641
517
520
  omlish/os/fcntl.py,sha256=riQf9iEEEIC28lJp8ud06MU56w2XJHJ9nBFtck_hdhc,1501
518
- omlish/os/filemodes.py,sha256=njxd9cQChKSi_5-oiAG9OJOzj8CvnyedWWheOWM0uHA,3476
521
+ omlish/os/filemodes.py,sha256=eVT60kokup40-N31r2acHJ2ip3d9HmqY8jCGIsKpKqc,4576
519
522
  omlish/os/files.py,sha256=WJ_42vsZIZukQURN3TTccp-n74ZNhbux_ps3TLbHj18,1106
520
523
  omlish/os/forkhooks.py,sha256=yjodOvs90ClXskv5oBIJbHn0Y7dzajLmZmOpRMKbyxM,5656
521
524
  omlish/os/journald.py,sha256=2nI8Res1poXkbLc31--MPUlzYMESnCcPUkIxDOCjZW0,3903
@@ -708,9 +711,9 @@ omlish/text/indent.py,sha256=YjtJEBYWuk8--b9JU_T6q4yxV85_TR7VEVr5ViRCFwk,1336
708
711
  omlish/text/minja.py,sha256=jZC-fp3Xuhx48ppqsf2Sf1pHbC0t8XBB7UpUUoOk2Qw,5751
709
712
  omlish/text/parts.py,sha256=JkNZpyR2tv2CNcTaWJJhpQ9E4F0yPR8P_YfDbZfMtwQ,6182
710
713
  omlish/text/random.py,sha256=jNWpqiaKjKyTdMXC-pWAsSC10AAP-cmRRPVhm59ZWLk,194
711
- omlish-0.0.0.dev236.dist-info/LICENSE,sha256=B_hVtavaA8zCYDW99DYdcpDLKz1n3BBRjZrcbv8uG8c,1451
712
- omlish-0.0.0.dev236.dist-info/METADATA,sha256=lXce_EjsToJ0ULgvK8E_0UWQaRWN4nb30AkWMyseA5I,4176
713
- omlish-0.0.0.dev236.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
714
- omlish-0.0.0.dev236.dist-info/entry_points.txt,sha256=Lt84WvRZJskWCAS7xnQGZIeVWksprtUHj0llrvVmod8,35
715
- omlish-0.0.0.dev236.dist-info/top_level.txt,sha256=pePsKdLu7DvtUiecdYXJ78iO80uDNmBlqe-8hOzOmfs,7
716
- omlish-0.0.0.dev236.dist-info/RECORD,,
714
+ omlish-0.0.0.dev237.dist-info/LICENSE,sha256=B_hVtavaA8zCYDW99DYdcpDLKz1n3BBRjZrcbv8uG8c,1451
715
+ omlish-0.0.0.dev237.dist-info/METADATA,sha256=xNNscWsZ6ZOLDE0vOj3YCOrR50thYq82_KawDOui9OA,4176
716
+ omlish-0.0.0.dev237.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
717
+ omlish-0.0.0.dev237.dist-info/entry_points.txt,sha256=Lt84WvRZJskWCAS7xnQGZIeVWksprtUHj0llrvVmod8,35
718
+ omlish-0.0.0.dev237.dist-info/top_level.txt,sha256=pePsKdLu7DvtUiecdYXJ78iO80uDNmBlqe-8hOzOmfs,7
719
+ omlish-0.0.0.dev237.dist-info/RECORD,,