ominfra 0.0.0.dev102__py3-none-any.whl → 0.0.0.dev103__py3-none-any.whl

Sign up to get free protection for your applications and to get access to all the features.
@@ -1410,7 +1410,7 @@ class DataclassObjMarshaler(ObjMarshaler):
1410
1410
  return {k: m.marshal(getattr(o, k)) for k, m in self.fs.items()}
1411
1411
 
1412
1412
  def unmarshal(self, o: ta.Any) -> ta.Any:
1413
- return self.ty(**{k: self.fs[k].unmarshal(v) for k, v in o.items() if self.nonstrict or k in self.fs})
1413
+ return self.ty(**{k: self.fs[k].unmarshal(v) for k, v in o.items() if not self.nonstrict or k in self.fs})
1414
1414
 
1415
1415
 
1416
1416
  @dc.dataclass(frozen=True)
@@ -0,0 +1,139 @@
1
+ # ruff: noqa: UP006 UP007
2
+ # @omlish-lite
3
+ """
4
+ TODO:
5
+ - implement stop lol
6
+ - collective heartbeat monitoring - ThreadWorkerGroups
7
+ - group -> 'context'? :|
8
+ - shared stop_event?
9
+ """
10
+ import abc
11
+ import dataclasses as dc
12
+ import threading
13
+ import time
14
+ import typing as ta
15
+
16
+ from omlish.lite.contextmanagers import ExitStacked
17
+ from omlish.lite.logs import log
18
+
19
+
20
+ T = ta.TypeVar('T')
21
+ ThreadWorkerT = ta.TypeVar('ThreadWorkerT', bound='ThreadWorker')
22
+
23
+
24
+ ##
25
+
26
+
27
+ class ThreadWorker(ExitStacked, abc.ABC):
28
+ def __init__(
29
+ self,
30
+ *,
31
+ stop_event: ta.Optional[threading.Event] = None,
32
+ ) -> None:
33
+ super().__init__()
34
+
35
+ if stop_event is None:
36
+ stop_event = threading.Event()
37
+ self._stop_event = stop_event
38
+
39
+ self._lock = threading.RLock()
40
+ self._thread: ta.Optional[threading.Thread] = None
41
+ self._last_heartbeat: ta.Optional[float] = None
42
+
43
+ #
44
+
45
+ def __enter__(self: ThreadWorkerT) -> ThreadWorkerT:
46
+ with self._lock:
47
+ return super().__enter__() # noqa
48
+
49
+ #
50
+
51
+ def should_stop(self) -> bool:
52
+ return self._stop_event.is_set()
53
+
54
+ class Stopping(Exception): # noqa
55
+ pass
56
+
57
+ #
58
+
59
+ @property
60
+ def last_heartbeat(self) -> ta.Optional[float]:
61
+ return self._last_heartbeat
62
+
63
+ def _heartbeat(
64
+ self,
65
+ *,
66
+ no_stop_check: bool = False,
67
+ ) -> None:
68
+ self._last_heartbeat = time.time()
69
+
70
+ if not no_stop_check and self.should_stop():
71
+ log.info('Stopping: %s', self)
72
+ raise ThreadWorker.Stopping
73
+
74
+ #
75
+
76
+ def has_started(self) -> bool:
77
+ return self._thread is not None
78
+
79
+ def is_alive(self) -> bool:
80
+ return (thr := self._thread) is not None and thr.is_alive()
81
+
82
+ def start(self) -> None:
83
+ with self._lock:
84
+ if self._thread is not None:
85
+ raise RuntimeError('Thread already started: %r', self)
86
+
87
+ thr = threading.Thread(target=self.__run)
88
+ self._thread = thr
89
+ thr.start()
90
+
91
+ #
92
+
93
+ def __run(self) -> None:
94
+ try:
95
+ self._run()
96
+ except ThreadWorker.Stopping:
97
+ log.exception('Thread worker stopped: %r', self)
98
+ except Exception: # noqa
99
+ log.exception('Error in worker thread: %r', self)
100
+ raise
101
+
102
+ @abc.abstractmethod
103
+ def _run(self) -> None:
104
+ raise NotImplementedError
105
+
106
+ #
107
+
108
+ def stop(self) -> None:
109
+ self._stop_event.set()
110
+
111
+ def join(self, timeout: ta.Optional[float] = None) -> None:
112
+ with self._lock:
113
+ if self._thread is None:
114
+ raise RuntimeError('Thread not started: %r', self)
115
+ self._thread.join(timeout)
116
+
117
+
118
+ ##
119
+
120
+
121
+ class ThreadWorkerGroup:
122
+ @dc.dataclass()
123
+ class State:
124
+ worker: ThreadWorker
125
+
126
+ def __init__(self) -> None:
127
+ super().__init__()
128
+
129
+ self._lock = threading.RLock()
130
+ self._states: ta.Dict[ThreadWorker, ThreadWorkerGroup.State] = {}
131
+
132
+ def add(self, *workers: ThreadWorker) -> 'ThreadWorkerGroup':
133
+ with self._lock:
134
+ for w in workers:
135
+ if w in self._states:
136
+ raise KeyError(w)
137
+ self._states[w] = ThreadWorkerGroup.State(w)
138
+
139
+ return self
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: ominfra
3
- Version: 0.0.0.dev102
3
+ Version: 0.0.0.dev103
4
4
  Summary: ominfra
5
5
  Author: wrmsr
6
6
  License: BSD-3-Clause
@@ -12,8 +12,8 @@ Classifier: Operating System :: OS Independent
12
12
  Classifier: Operating System :: POSIX
13
13
  Requires-Python: >=3.12
14
14
  License-File: LICENSE
15
- Requires-Dist: omdev ==0.0.0.dev102
16
- Requires-Dist: omlish ==0.0.0.dev102
15
+ Requires-Dist: omdev ==0.0.0.dev103
16
+ Requires-Dist: omlish ==0.0.0.dev103
17
17
  Provides-Extra: all
18
18
  Requires-Dist: paramiko ~=3.5 ; extra == 'all'
19
19
  Requires-Dist: asyncssh ~=2.18 ; extra == 'all'
@@ -3,19 +3,22 @@ ominfra/__about__.py,sha256=6i1AoruFYQCd-PyhhbDQDWY2d1tiQu9nkwWr-fXAqfY,705
3
3
  ominfra/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
4
4
  ominfra/cmds.py,sha256=E0AfnvEmnKntXWvmLW5L05_NeDpBET1VBXn7vV6EwBQ,2083
5
5
  ominfra/ssh.py,sha256=jQpc4WvkMckIfk4vILda8zFaeharRqc_6wxW50b0OjQ,5431
6
- ominfra/threadworker.py,sha256=8rnWvgKjPIAdctS6wxiUiEIzm-mORRkWVD94YghVj0g,1413
6
+ ominfra/threadworkers.py,sha256=QuRpz9Yjyb4F8_IjzqmL7eNCAmXZfy3XLl7QoVF7Ohw,3273
7
7
  ominfra/clouds/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
8
8
  ominfra/clouds/aws/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
9
9
  ominfra/clouds/aws/__main__.py,sha256=HXMoxEl9KHhv6zOOPQxiJAftfR2SjBqeVTYw-og9aFw,163
10
10
  ominfra/clouds/aws/auth.py,sha256=p50hnm8SU4CWAkLX0C4XCTAm7yAAg-HqcP1YvL5cW94,6205
11
11
  ominfra/clouds/aws/cli.py,sha256=OJVVLIwSy1378drkgP1ke_JltbyzBmnrB_Lom6A83os,510
12
12
  ominfra/clouds/aws/dataclasses.py,sha256=rKhtJKJ0JhMssU9n9CABX_JaUiokIboEATJ9TZgZQ6A,3868
13
- ominfra/clouds/aws/logs.py,sha256=7Cl9vjco6G-5wW06TmuzU9GZ-diFP8V26X2UZnfJD3U,5232
13
+ ominfra/clouds/aws/logs.py,sha256=z9ouU2IYXNHsl7_Whbjs1FGtlUwsEq0RV8LNrM_QNTE,5471
14
14
  ominfra/clouds/aws/metadata.py,sha256=XR1BuMdQheyeFjjA3MN8GCNWVAp5ahoPdbWXEmViutQ,2767
15
15
  ominfra/clouds/aws/journald2aws/__init__.py,sha256=Y3l4WY4JRi2uLG6kgbGp93fuGfkxkKwZDvhsa0Rwgtk,15
16
- ominfra/clouds/aws/journald2aws/main.py,sha256=d7jSbcaxjGgpVpDCPl1kJCOZg3S7g2n0Lf7gI0ZL3j0,9126
16
+ ominfra/clouds/aws/journald2aws/cursor.py,sha256=tQ7O6BHlEdaalbiI_Rqagj0aHfdtTQ_ZJwdOSRUjNvQ,1173
17
+ ominfra/clouds/aws/journald2aws/driver.py,sha256=8jiuEpOgKFSucpEJBBTBiSVg6L_tA4alUNK-I788HWU,5452
18
+ ominfra/clouds/aws/journald2aws/main.py,sha256=xFkEhkYKtFfW0XRfY0UaX_gd_FU66WTZOMCyiIaPY3E,2237
19
+ ominfra/clouds/aws/journald2aws/poster.py,sha256=hz1XuctW8GtLmfjhRvCFY6py52D4BzXHYny5XKFpHSA,2833
17
20
  ominfra/deploy/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
18
- ominfra/deploy/_executor.py,sha256=zHn4zAz6Ch1i5R_EdKTfJv_4SE0QPNuQEk7O1ptB_7A,32834
21
+ ominfra/deploy/_executor.py,sha256=w3qpyRtoIfpm9HgygGb4jo1Wx8-Nz8U3CXSuwBcTFl4,33258
19
22
  ominfra/deploy/configs.py,sha256=qi0kwT7G2NH7dXLOQic-u6R3yeadup_QtvrjwWIggbM,435
20
23
  ominfra/deploy/remote.py,sha256=6ACmpXU1uBdyGs3Xsp97ktKFq30cJlzN9LRWNUWlGY4,2144
21
24
  ominfra/deploy/executor/__init__.py,sha256=Y3l4WY4JRi2uLG6kgbGp93fuGfkxkKwZDvhsa0Rwgtk,15
@@ -30,7 +33,7 @@ ominfra/deploy/executor/concerns/systemd.py,sha256=MtsSEToEa1HNouern_JukcYTnypw_
30
33
  ominfra/deploy/executor/concerns/user.py,sha256=j5LDfQXquIp-eEM7t6aShsrYoQrM_ILXZycTmTcRVxA,686
31
34
  ominfra/deploy/executor/concerns/venv.py,sha256=jbRriqJHO4r9Zyo5Hfl_qVmcU6Qm6UgrouBroKcPn2g,775
32
35
  ominfra/deploy/poly/__init__.py,sha256=Y3l4WY4JRi2uLG6kgbGp93fuGfkxkKwZDvhsa0Rwgtk,15
33
- ominfra/deploy/poly/_main.py,sha256=atzVWMa2FZlJYODcRxu5zN5CHOLiVW9aIS9f012QKWA,23569
36
+ ominfra/deploy/poly/_main.py,sha256=EtMN9Cmn4plMLTXq3u_NHUUhaa8qjsyldN61AbjDQEg,24025
34
37
  ominfra/deploy/poly/base.py,sha256=Bd-CzUTaDvTRbdXKiTxMxs77WCEXItwNoBYCRnTk1u4,4167
35
38
  ominfra/deploy/poly/configs.py,sha256=9bzWdbxhOk_Q4KokDjmRz254KHnUU71Vl1frLlhQyU4,584
36
39
  ominfra/deploy/poly/deploy.py,sha256=tMYKslXLjstcv86siRt5j37USsS0Wd6lsfeGRE26zio,544
@@ -42,18 +45,18 @@ ominfra/deploy/poly/site.py,sha256=QJwDDJoVm2-kxi4bxIrp-mn4y2qDLuW3CAUax3W8gv8,2
42
45
  ominfra/deploy/poly/supervisor.py,sha256=zkl6VQBcAZaMAhyR9DbbbqULcgFCDZoe9S_vP-mMFQ8,2289
43
46
  ominfra/deploy/poly/venv.py,sha256=BoipDEa4NTeodjf3L57KJfq9eGKLagFNKwD8pS4yrzA,1552
44
47
  ominfra/journald/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
45
- ominfra/journald/genmessages.py,sha256=3lB3W_Xl4GLAutNu1KvihdtMvkkukYqrfsamcqOS204,1583
48
+ ominfra/journald/genmessages.py,sha256=rLTS-K2v7otNOtTz4RoOEVYCm0fQuuBzf47e0T61tA8,1857
46
49
  ominfra/journald/messages.py,sha256=2iMY4k63XGNcN3LPvBmmK55ftjupnNh8f_ijlW9mkhQ,2208
47
- ominfra/journald/tailer.py,sha256=4CzBTQcAzlW9OQ--61fIdClZVgbMQQnYe2OD1d5P7e4,36780
50
+ ominfra/journald/tailer.py,sha256=-CTQPkDQUBJJOpQCueFDsF5VmlfPSaSrbrGB505SUuM,36819
48
51
  ominfra/manage/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
49
52
  ominfra/manage/manage.py,sha256=BttL8LFEknHZE_h2Pt5dAqbfUkv6qy43WI0raXBZ1a8,151
50
53
  ominfra/pyremote/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
51
- ominfra/pyremote/_runcommands.py,sha256=2UVHaUJjmWP8jMZE79j2Qk1E5IOyVV1qNSUbdr-zKYM,26670
54
+ ominfra/pyremote/_runcommands.py,sha256=xA-wRpLtGha6ZZyatGokL9kyz43GOB_ZB_WLZcd8gFE,27094
52
55
  ominfra/pyremote/bootstrap.py,sha256=RvMO3YGaN1E4sgUi1JEtiPak8cjvqtc_vRCq1yqbeZg,3370
53
56
  ominfra/pyremote/runcommands.py,sha256=bviS0_TDIoZVAe4h-_iavbvJtVSFu8lnk7fQ5iasCWE,1571
54
57
  ominfra/scripts/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
55
- ominfra/scripts/journald2aws.py,sha256=AgOHKPujUo4SqTFoz_EiN-oJ-HwZJB-UNyCFJypsNus,91403
56
- ominfra/scripts/supervisor.py,sha256=iWTZQIrzqNjlmuDH489AlfXGb0i_0sfoYIYP7h1LH-E,115374
58
+ ominfra/scripts/journald2aws.py,sha256=4DzyNn8KjrBfHNtENgp7EVL569PLqzQnQu6PQXgqPgg,97251
59
+ ominfra/scripts/supervisor.py,sha256=2rf7CCgqpYY6Pt7Yq69n4V7CY-Sas_0MyDO3SIv2tPw,115378
57
60
  ominfra/supervisor/__init__.py,sha256=Y3l4WY4JRi2uLG6kgbGp93fuGfkxkKwZDvhsa0Rwgtk,15
58
61
  ominfra/supervisor/__main__.py,sha256=usW9jjq5JPe_2SL8H5PrjDdksO75MX85Ir0HFfb35eM,72
59
62
  ominfra/supervisor/compat.py,sha256=Y1d_pk4eN18AbVYjDHAXMMnPwOKTFpc7JDb1uClYMsQ,5064
@@ -73,9 +76,9 @@ ominfra/tailscale/api.py,sha256=C5-t_b6jZXUWcy5k8bXm7CFnk73pSdrlMOgGDeGVrpw,1370
73
76
  ominfra/tailscale/cli.py,sha256=DSGp4hn5xwOW-l_u_InKlSF6kIobxtUtVssf_73STs0,3567
74
77
  ominfra/tools/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
75
78
  ominfra/tools/listresources.py,sha256=4qVg5txsb10EHhvqXXeM6gJ2jx9LbroEnPydDv1uXs0,6176
76
- ominfra-0.0.0.dev102.dist-info/LICENSE,sha256=B_hVtavaA8zCYDW99DYdcpDLKz1n3BBRjZrcbv8uG8c,1451
77
- ominfra-0.0.0.dev102.dist-info/METADATA,sha256=204uohL25Cyu3_Op6fWaH6j4qlPBlc60pKcNBCjU8SU,742
78
- ominfra-0.0.0.dev102.dist-info/WHEEL,sha256=P9jw-gEje8ByB7_hXoICnHtVCrEwMQh-630tKvQWehc,91
79
- ominfra-0.0.0.dev102.dist-info/entry_points.txt,sha256=kgecQ2MgGrM9qK744BoKS3tMesaC3yjLnl9pa5CRczg,37
80
- ominfra-0.0.0.dev102.dist-info/top_level.txt,sha256=E-b2OHkk_AOBLXHYZQ2EOFKl-_6uOGd8EjeG-Zy6h_w,8
81
- ominfra-0.0.0.dev102.dist-info/RECORD,,
79
+ ominfra-0.0.0.dev103.dist-info/LICENSE,sha256=B_hVtavaA8zCYDW99DYdcpDLKz1n3BBRjZrcbv8uG8c,1451
80
+ ominfra-0.0.0.dev103.dist-info/METADATA,sha256=p_SRGaoGDeFXyv_pvHCMjqaDP6umkRhGCsrtly5uJQs,742
81
+ ominfra-0.0.0.dev103.dist-info/WHEEL,sha256=P9jw-gEje8ByB7_hXoICnHtVCrEwMQh-630tKvQWehc,91
82
+ ominfra-0.0.0.dev103.dist-info/entry_points.txt,sha256=kgecQ2MgGrM9qK744BoKS3tMesaC3yjLnl9pa5CRczg,37
83
+ ominfra-0.0.0.dev103.dist-info/top_level.txt,sha256=E-b2OHkk_AOBLXHYZQ2EOFKl-_6uOGd8EjeG-Zy6h_w,8
84
+ ominfra-0.0.0.dev103.dist-info/RECORD,,
ominfra/threadworker.py DELETED
@@ -1,67 +0,0 @@
1
- # ruff: noqa: UP007
2
- # @omlish-lite
3
- """
4
- TODO:
5
- - implement stop lol
6
- - collective heartbeat monitoring - ThreadWorkerGroups
7
- """
8
- import abc
9
- import threading
10
- import time
11
- import typing as ta
12
-
13
- from omlish.lite.logs import log
14
-
15
-
16
- class ThreadWorker(abc.ABC):
17
- def __init__(
18
- self,
19
- *,
20
- stop_event: ta.Optional[threading.Event] = None,
21
- ) -> None:
22
- super().__init__()
23
-
24
- if stop_event is None:
25
- stop_event = threading.Event()
26
- self._stop_event = stop_event
27
-
28
- self._thread: ta.Optional[threading.Thread] = None
29
-
30
- self._last_heartbeat: ta.Optional[float] = None
31
-
32
- #
33
-
34
- def should_stop(self) -> bool:
35
- return self._stop_event.is_set()
36
-
37
- #
38
-
39
- @property
40
- def last_heartbeat(self) -> ta.Optional[float]:
41
- return self._last_heartbeat
42
-
43
- def _heartbeat(self) -> bool:
44
- self._last_heartbeat = time.time()
45
-
46
- if self.should_stop():
47
- log.info('Stopping: %s', self)
48
- return False
49
-
50
- return True
51
-
52
- #
53
-
54
- def is_alive(self) -> bool:
55
- return (thr := self._thread) is not None and thr.is_alive()
56
-
57
- def start(self) -> None:
58
- thr = threading.Thread(target=self._run)
59
- self._thread = thr
60
- thr.start()
61
-
62
- @abc.abstractmethod
63
- def _run(self) -> None:
64
- raise NotImplementedError
65
-
66
- def stop(self) -> None:
67
- raise NotImplementedError