omlish 0.0.0.dev235__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.dev235'
2
- __revision__ = '4afeb0f0acadfd7890e6e6309be41fe7e65c3185'
1
+ __version__ = '0.0.0.dev237'
2
+ __revision__ = 'e8c58292f1ec88727fd832f6496545435d992e9d'
3
3
 
4
4
 
5
5
  #
omlish/argparse/all.py CHANGED
@@ -4,6 +4,7 @@ import argparse
4
4
  from .cli import ( # noqa
5
5
  ArgparseArg as Arg,
6
6
  argparse_arg as arg,
7
+ argparse_arg_ as arg_,
7
8
 
8
9
  ArgparseCmdFn as CmdFn,
9
10
  ArgparseCmd as Cmd,
omlish/argparse/cli.py CHANGED
@@ -1,6 +1,9 @@
1
1
  # ruff: noqa: UP006 UP007
2
2
  # @omlish-lite
3
3
  """
4
+ FIXME:
5
+ - exit_on_error lol
6
+
4
7
  TODO:
5
8
  - default command
6
9
  - auto match all underscores to hyphens
@@ -44,6 +47,10 @@ def argparse_arg(*args, **kwargs) -> ArgparseArg:
44
47
  return ArgparseArg(args, kwargs)
45
48
 
46
49
 
50
+ def argparse_arg_(*args, **kwargs) -> ta.Any:
51
+ return argparse_arg(*args, **kwargs)
52
+
53
+
47
54
  #
48
55
 
49
56
 
omlish/codecs/base.py CHANGED
@@ -12,6 +12,7 @@ from .. import dataclasses as dc
12
12
  from .. import lang
13
13
  from .. import reflect as rfl
14
14
  from ..funcs import pairs as fps
15
+ from ..manifests.base import ModAttrManifest
15
16
 
16
17
 
17
18
  I = ta.TypeVar('I')
@@ -87,9 +88,7 @@ class Codec:
87
88
 
88
89
 
89
90
  @dc.dataclass(frozen=True, kw_only=True)
90
- class LazyLoadedCodec:
91
- mod_name: str
92
- attr_name: str
91
+ class LazyLoadedCodec(ModAttrManifest):
93
92
  name: str
94
93
  aliases: ta.Collection[str] | None = None
95
94
 
omlish/codecs/registry.py CHANGED
@@ -1,5 +1,4 @@
1
1
  import contextlib
2
- import importlib
3
2
  import threading
4
3
  import typing as ta
5
4
 
@@ -74,8 +73,7 @@ class CodecRegistry:
74
73
  codec_or_lazy = self._by_name[name]
75
74
 
76
75
  if isinstance(codec_or_lazy, LazyLoadedCodec):
77
- mod = importlib.import_module(codec_or_lazy.mod_name)
78
- codec = check.isinstance(getattr(mod, codec_or_lazy.attr_name), Codec)
76
+ codec = check.isinstance(codec_or_lazy.load(), Codec)
79
77
  self._by_name[name] = codec
80
78
  self._post_load(codec)
81
79
  else:
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
@@ -15,6 +15,7 @@ TODO:
15
15
  - timebomb
16
16
  - pickle protocol, revision / venv check, multiprocessing manager support
17
17
  """
18
+ import itertools
18
19
  import logging
19
20
  import os.path
20
21
  import time
@@ -39,8 +40,7 @@ log = logging.getLogger(__name__)
39
40
  class Daemon:
40
41
  @dc.dataclass(frozen=True, kw_only=True)
41
42
  class Config:
42
- target: Target
43
- spawning: Spawning
43
+ spawning: Spawning | None = None
44
44
 
45
45
  #
46
46
 
@@ -63,11 +63,20 @@ class Daemon:
63
63
  def __post_init__(self) -> None:
64
64
  check.isinstance(self.pid_file, (str, None))
65
65
 
66
- def __init__(self, config: Config) -> None:
66
+ def __init__(
67
+ self,
68
+ target: Target,
69
+ config: Config = Config(),
70
+ ) -> None:
67
71
  super().__init__()
68
72
 
73
+ self._target = target
69
74
  self._config = config
70
75
 
76
+ @property
77
+ def target(self) -> Target:
78
+ return self._target
79
+
71
80
  @property
72
81
  def config(self) -> Config:
73
82
  return self._config
@@ -85,7 +94,7 @@ class Daemon:
85
94
  inheritable=False,
86
95
  )
87
96
 
88
- def is_running(self) -> bool:
97
+ def is_pidfile_locked(self) -> bool:
89
98
  check.state(self.has_pidfile)
90
99
 
91
100
  if not os.path.isfile(check.non_empty_str(self._config.pid_file)):
@@ -96,22 +105,31 @@ class Daemon:
96
105
 
97
106
  #
98
107
 
99
- def wait_sync(self, timeout: lang.TimeoutLike = lang.Timeout.Default) -> None:
108
+ def wait_sync(
109
+ self,
110
+ timeout: lang.TimeoutLike = lang.Timeout.Default,
111
+ *,
112
+ max_tries: int | None = None,
113
+ ) -> None:
100
114
  if self._config.wait is None:
101
115
  return
102
116
 
103
117
  timeout = lang.Timeout.of(timeout, self._config.wait_timeout)
104
118
  waiter = waiter_for(self._config.wait)
105
- while not waiter.do_wait():
119
+ for i in itertools.count():
120
+ if max_tries is not None and i >= max_tries:
121
+ raise TimeoutError
106
122
  timeout()
123
+ if waiter.do_wait():
124
+ break
107
125
  time.sleep(self._config.wait_sleep_s or 0.)
108
126
 
109
127
  #
110
128
 
111
129
  def launch_no_wait(self) -> None:
112
130
  launcher = Launcher(
113
- target=self._config.target,
114
- spawning=self._config.spawning,
131
+ target=self._target,
132
+ spawning=check.not_none(self._config.spawning),
115
133
 
116
134
  pid_file=self._config.pid_file,
117
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")
@@ -0,0 +1,39 @@
1
+ # ruff: noqa: UP006 UP007
2
+ import dataclasses as dc
3
+ import typing as ta
4
+
5
+
6
+ NameAliasesManifestT = ta.TypeVar('NameAliasesManifestT', bound='NameAliasesManifest')
7
+
8
+
9
+ ##
10
+
11
+
12
+ @dc.dataclass(frozen=True)
13
+ class ModAttrManifest:
14
+ mod_name: str
15
+ attr_name: str
16
+
17
+ def load(self) -> ta.Any:
18
+ importlib = __import__('importlib')
19
+ mod = importlib.import_module(self.mod_name)
20
+ return getattr(mod, self.attr_name)
21
+
22
+
23
+ ##
24
+
25
+
26
+ @dc.dataclass(frozen=True)
27
+ class NameAliasesManifest:
28
+ name: str
29
+ aliases: ta.Optional[ta.Collection[str]] = None
30
+
31
+ @classmethod
32
+ def build_name_dict(cls, objs: ta.Iterable[NameAliasesManifestT]) -> ta.Dict[str, NameAliasesManifestT]:
33
+ dct: ta.Dict[str, NameAliasesManifestT] = {}
34
+ for o in objs:
35
+ for n in (o.name, *(o.aliases or ())):
36
+ if n in dct:
37
+ raise KeyError(n)
38
+ dct[n] = o
39
+ return dct
omlish/manifests/load.py CHANGED
@@ -196,11 +196,11 @@ class ManifestLoader:
196
196
 
197
197
  def discover_pkgs(self) -> ta.Sequence[str]:
198
198
  # This is a fat dep so do it late.
199
- import importlib.metadata
199
+ importlib_metadata = __import__('importlib.metadata').metadata
200
200
 
201
201
  return [
202
202
  ep.value
203
- for ep in importlib.metadata.entry_points(group=self.ENTRY_POINT_GROUP)
203
+ for ep in importlib_metadata.entry_points(group=self.ENTRY_POINT_GROUP)
204
204
  ]
205
205
 
206
206
  def scan_pkg_root(self, root: str) -> ta.Sequence[str]:
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:
omlish/os/mangle.py ADDED
@@ -0,0 +1,25 @@
1
+ # ruff: noqa: UP006 UP007
2
+ # @omlish-lite
3
+
4
+
5
+ #
6
+
7
+
8
+ def mangle_path(path: str) -> str:
9
+ if not path.startswith('/'):
10
+ raise ValueError('Only absolute Unix paths are supported')
11
+ return (
12
+ path
13
+ .replace('_', '__')
14
+ .replace('/', '_')
15
+ )
16
+
17
+
18
+ def unmangle_path(mangled: str) -> str:
19
+ if '/' in mangled:
20
+ raise ValueError("Mangled paths should not contain '/'")
21
+ return (
22
+ mangled
23
+ .replace('_', '/')
24
+ .replace('//', '_')
25
+ )
@@ -2,15 +2,7 @@
2
2
  # @omlish-lite
3
3
  """
4
4
  TODO:
5
- - reliable pid retrieval
6
- - contents are *ignored*, just advisory
7
- - check double-check:
8
- - 1) get pid of flock holder
9
- - 2) get pidfd to that
10
- - 3) recheck current pid of flock holder == that pid
11
- - racy as to if it's a different actual process as initial check, just with same pid, but due to 'identity' / semantic
12
- meaning of the named pidfile the processes are considered equivalent
13
- - read_checked(), contextmanager
5
+ - 'json pids', with code version? '.json.pid'? '.jpid'?
14
6
  """
15
7
  import fcntl
16
8
  import os
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: omlish
3
- Version: 0.0.0.dev235
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=mOMhMeHgYWv96pndE14EVx-D5bReVhtSMXcqy2QLvnE,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
@@ -86,8 +86,8 @@ omlish/antlr/_runtime/xpath/XPath.py,sha256=KSL1SH3VAeRDZCe4dAD7xmUdfk-j434ypZKR
86
86
  omlish/antlr/_runtime/xpath/XPathLexer.py,sha256=WvGKQjQnu7pX5C4CFKtsCzba2B2W6ie4ivtWLvlgymM,3509
87
87
  omlish/antlr/_runtime/xpath/__init__.py,sha256=lMd_BbXYdlDhZQN_q0TKN978XW5G0pq618F0NaLkpFE,71
88
88
  omlish/argparse/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
89
- omlish/argparse/all.py,sha256=2qhVgFTq3w_tJ_okEnw_D-2k4jlJD7EcM5hNEow6M2M,1030
90
- omlish/argparse/cli.py,sha256=nMn4D2Oce2HNGGVuc_EtJuNSqoIfNdzWd_eKFBSfxEs,8586
89
+ omlish/argparse/all.py,sha256=NeeMM5MIebY7XDAHaCxUzeesEoUYwsf5i9PrBUcO1cI,1057
90
+ omlish/argparse/cli.py,sha256=vVLlhJPt0PKaNfIBrXTzz9cHyy2dFEY8hlQ3GvtKbwc,8704
91
91
  omlish/asyncs/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
92
92
  omlish/asyncs/all.py,sha256=uUz9ziKh4_QrgmdhKFMgq6j7mFbiZd3LiogguDCQsGI,587
93
93
  omlish/asyncs/anyio.py,sha256=gfpx-D8QGmUfhnQxHEaHXcAP8zSMQjcGw4COFTGNnHI,8021
@@ -127,11 +127,11 @@ omlish/bootstrap/main.py,sha256=yZhOHDDlj4xB5a89dRdT8z58FsqqnpoBg1-tvY2CJe4,5903
127
127
  omlish/bootstrap/marshal.py,sha256=ZxdAeMNd2qXRZ1HUK89HmEhz8tqlS9OduW34QBscKw0,516
128
128
  omlish/bootstrap/sys.py,sha256=0F0uThMsYdjqUtzrYHr4Xsh_MjscxgWl149i_3tDOqo,8787
129
129
  omlish/codecs/__init__.py,sha256=-FDwRJFGagg-fZyQ8wup4GPuR6gHpmaChzthlykn-kY,876
130
- omlish/codecs/base.py,sha256=uFrDvP7SZdeBbpuUzGHvyOdFNAmhjkBG2cVaVydRsjw,2221
130
+ omlish/codecs/base.py,sha256=FLWf5jgxuyZk-7jH0oC5RzJ5Ls5FLxP0hAowrBVEvqo,2246
131
131
  omlish/codecs/bytes.py,sha256=jlZ87OmZ52HhQDNyL87R3OIviK2qV5iU2jZYOTOLWjk,2157
132
132
  omlish/codecs/chain.py,sha256=DrBi5vbaFfObfoppo6alwOmyW2XbrH2051cjExwr2Gs,527
133
133
  omlish/codecs/funcs.py,sha256=p4imNt7TobyZVXWC-WhntHVu9KfJrO4QwdtPRh-cVOk,850
134
- omlish/codecs/registry.py,sha256=yDkDpRJXD7owFCa4b0R4MMf3Le6KxknNBewjSrehepw,4000
134
+ omlish/codecs/registry.py,sha256=2FnO5YP7ui1LzkguwESY0MP3WIdwgPTIJTM_4RyTOEg,3896
135
135
  omlish/codecs/standard.py,sha256=eiZ4u9ep0XrA4Z_D1zJI0vmWyuN8HLrX4Se_r_Cq_ZM,60
136
136
  omlish/codecs/text.py,sha256=JzrdwMpQPo2NBBg3K1EZszzQy5vEWmd82SIerJd4yeQ,5723
137
137
  omlish/collections/__init__.py,sha256=ddzZS3C2rvDo65hspO9KlFsrinElxPWvYEgVyBUZdb0,2164
@@ -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=lnMgGrPeZ3pjhuXPU-7DX2XGEdNG0kP6HAejCQPSGtk,2976
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
@@ -461,7 +464,8 @@ omlish/logs/standard.py,sha256=FbKdF2Z4Na5i2TNwKn0avLJXyICe2JKsPufjvKCHGn0,3162
461
464
  omlish/logs/timing.py,sha256=XrFUHIPT4EHDujLKbGs9fGFMmoM3NEP8xPRaESJr7bQ,1513
462
465
  omlish/logs/utils.py,sha256=mzHrZ9ji75p5A8qR29eUr05CBAHMb8J753MSkID_VaQ,393
463
466
  omlish/manifests/__init__.py,sha256=P2B0dpT8D7l5lJwRGPA92IcQj6oeXfd90X5-q9BJrKg,51
464
- omlish/manifests/load.py,sha256=u84sAHhDgHefTDK8S50W5czRnqfwm0obqiOzNBCBP9Q,6603
467
+ omlish/manifests/base.py,sha256=1LGglMh7UUU7JWIqPH_JfIm6czGETibpScEqKI5mSMg,920
468
+ omlish/manifests/load.py,sha256=0ZjtsYL2EIKYd_MF3brKTPYdRZmtWdwomx2cFjjFV2M,6640
465
469
  omlish/manifests/types.py,sha256=d8bv5tknCJqclRfxCpao_8XxHo2yofhLpVHQTB-MfNw,260
466
470
  omlish/marshal/__init__.py,sha256=00D3S6qwUld1TUWd67hVHuNcrj3c_FAFSkCVXgGWT-s,2607
467
471
  omlish/marshal/base.py,sha256=tJ4iNuD7cW2GpGMznOhkAf2hugqp2pF2em0FaQcekrk,6740
@@ -514,11 +518,12 @@ omlish/os/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
514
518
  omlish/os/atomics.py,sha256=KhWNeh4mzU3M-TF0v8uR6hUqMfZJW42MeyIK9Jl6R0k,5246
515
519
  omlish/os/deathsig.py,sha256=hk9Yq2kyDdI-cI7OQH7mOfpRbOKzY_TfPKEqgrjVYbA,641
516
520
  omlish/os/fcntl.py,sha256=riQf9iEEEIC28lJp8ud06MU56w2XJHJ9nBFtck_hdhc,1501
517
- omlish/os/filemodes.py,sha256=njxd9cQChKSi_5-oiAG9OJOzj8CvnyedWWheOWM0uHA,3476
521
+ omlish/os/filemodes.py,sha256=eVT60kokup40-N31r2acHJ2ip3d9HmqY8jCGIsKpKqc,4576
518
522
  omlish/os/files.py,sha256=WJ_42vsZIZukQURN3TTccp-n74ZNhbux_ps3TLbHj18,1106
519
523
  omlish/os/forkhooks.py,sha256=yjodOvs90ClXskv5oBIJbHn0Y7dzajLmZmOpRMKbyxM,5656
520
524
  omlish/os/journald.py,sha256=2nI8Res1poXkbLc31--MPUlzYMESnCcPUkIxDOCjZW0,3903
521
525
  omlish/os/linux.py,sha256=whJ6scwMKSFBdXiVhJW0BCpJV4jOGMr-a_a3Bhwz6Ls,18938
526
+ omlish/os/mangle.py,sha256=dRp5uBxPuM6g52GnxuBoJUdUKW3CVVRr2ZwvQPHHxnA,489
522
527
  omlish/os/paths.py,sha256=hqPiyg_eYaRoIVPdAeX4oeLEV4Kpln_XsH0tHvbOf8Q,844
523
528
  omlish/os/signals.py,sha256=FtzkovLb58N3vNdfxflUeXWFCqqKzseCjk5kBdWT-ds,267
524
529
  omlish/os/sizes.py,sha256=ohkALLvqSqBX4iR-7DMKJ4pfOCRdZXV8htH4QywUNM0,152
@@ -531,7 +536,7 @@ omlish/os/pidfiles/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuF
531
536
  omlish/os/pidfiles/__main__.py,sha256=AF8TwjK4xgHVnoLAP9dIWgKvT0vGhHJlfDW0tKZ7tx4,200
532
537
  omlish/os/pidfiles/cli.py,sha256=2SSsP4O3VdpsDIMAkWgWSjh_YNIPzCD9l5LNN2qrIjo,2074
533
538
  omlish/os/pidfiles/manager.py,sha256=qSEwNaWT1KOAnU0KxliwvU_uowme5jyf1FyIPsGwnTY,2391
534
- omlish/os/pidfiles/pidfile.py,sha256=yy14NCjvsCdXzlJjZzkw4vbzjt_FXEaUS5SbYT3dGY0,4277
539
+ omlish/os/pidfiles/pidfile.py,sha256=nU3ONJydi3dPfmLl56DOXR7viJ4TAI8eL-iwR2XoB6s,3899
535
540
  omlish/os/pidfiles/pinning.py,sha256=v9RlJ4BnJZcaZZXiiRqbmzLluaSOkeeEb_WrbKEClBQ,6643
536
541
  omlish/reflect/__init__.py,sha256=Er2yBHibVO16hFNA1szQF2_f43Y3HRCBWtS-fjsOIYc,798
537
542
  omlish/reflect/inspect.py,sha256=WCo2YpBYauKw6k758FLlZ_H4Q05rgVPs96fEv9w6zHQ,1538
@@ -706,9 +711,9 @@ omlish/text/indent.py,sha256=YjtJEBYWuk8--b9JU_T6q4yxV85_TR7VEVr5ViRCFwk,1336
706
711
  omlish/text/minja.py,sha256=jZC-fp3Xuhx48ppqsf2Sf1pHbC0t8XBB7UpUUoOk2Qw,5751
707
712
  omlish/text/parts.py,sha256=JkNZpyR2tv2CNcTaWJJhpQ9E4F0yPR8P_YfDbZfMtwQ,6182
708
713
  omlish/text/random.py,sha256=jNWpqiaKjKyTdMXC-pWAsSC10AAP-cmRRPVhm59ZWLk,194
709
- omlish-0.0.0.dev235.dist-info/LICENSE,sha256=B_hVtavaA8zCYDW99DYdcpDLKz1n3BBRjZrcbv8uG8c,1451
710
- omlish-0.0.0.dev235.dist-info/METADATA,sha256=kGZm_s1Kmw1dsPChxxw0LPa1lbSL07NPMdgVyf8AnUs,4176
711
- omlish-0.0.0.dev235.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
712
- omlish-0.0.0.dev235.dist-info/entry_points.txt,sha256=Lt84WvRZJskWCAS7xnQGZIeVWksprtUHj0llrvVmod8,35
713
- omlish-0.0.0.dev235.dist-info/top_level.txt,sha256=pePsKdLu7DvtUiecdYXJ78iO80uDNmBlqe-8hOzOmfs,7
714
- omlish-0.0.0.dev235.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,,