ominfra 0.0.0.dev242__py3-none-any.whl → 0.0.0.dev243__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.
@@ -3025,20 +3025,64 @@ class IncrementalWriteBuffer:
3025
3025
 
3026
3026
 
3027
3027
  class ExitStacked:
3028
+ def __init_subclass__(cls, **kwargs: ta.Any) -> None:
3029
+ super().__init_subclass__(**kwargs)
3030
+
3031
+ for a in ('__enter__', '__exit__'):
3032
+ for b in cls.__bases__:
3033
+ if b is ExitStacked:
3034
+ continue
3035
+ try:
3036
+ fn = getattr(b, a)
3037
+ except AttributeError:
3038
+ pass
3039
+ else:
3040
+ if fn is not getattr(ExitStacked, a):
3041
+ raise TypeError(f'ExitStacked subclass {cls} must not not override {a} via {b}')
3042
+
3028
3043
  _exit_stack: ta.Optional[contextlib.ExitStack] = None
3029
3044
 
3045
+ @contextlib.contextmanager
3046
+ def _exit_stacked_init_wrapper(self) -> ta.Iterator[None]:
3047
+ """
3048
+ Overridable wrapper around __enter__ which deliberately does not have access to an _exit_stack yet. Intended for
3049
+ things like wrapping __enter__ in a lock.
3050
+ """
3051
+
3052
+ yield
3053
+
3054
+ @ta.final
3030
3055
  def __enter__(self: ExitStackedT) -> ExitStackedT:
3031
- check.state(self._exit_stack is None)
3032
- es = self._exit_stack = contextlib.ExitStack()
3033
- es.__enter__()
3034
- return self
3056
+ """
3057
+ Final because any contexts entered during this init must be exited if any exception is thrown, and user
3058
+ overriding would likely interfere with that. Override `_enter_contexts` for such init.
3059
+ """
3060
+
3061
+ with self._exit_stacked_init_wrapper():
3062
+ check.state(self._exit_stack is None)
3063
+ es = self._exit_stack = contextlib.ExitStack()
3064
+ es.__enter__()
3065
+ try:
3066
+ self._enter_contexts()
3067
+ except Exception: # noqa
3068
+ es.__exit__(*sys.exc_info())
3069
+ raise
3070
+ return self
3035
3071
 
3072
+ @ta.final
3036
3073
  def __exit__(self, exc_type, exc_val, exc_tb):
3037
3074
  if (es := self._exit_stack) is None:
3038
3075
  return None
3039
- self._exit_contexts()
3076
+ try:
3077
+ self._exit_contexts()
3078
+ except Exception: # noqa
3079
+ es.__exit__(*sys.exc_info())
3080
+ raise
3040
3081
  return es.__exit__(exc_type, exc_val, exc_tb)
3041
3082
 
3083
+ def _enter_contexts(self) -> None:
3084
+ pass
3085
+
3042
3086
  def _exit_contexts(self) -> None:
3043
3087
  pass
3044
3088
 
@@ -3048,20 +3092,54 @@ class ExitStacked:
3048
3092
 
3049
3093
 
3050
3094
  class AsyncExitStacked:
3095
+ def __init_subclass__(cls, **kwargs: ta.Any) -> None:
3096
+ super().__init_subclass__(**kwargs)
3097
+
3098
+ for a in ('__aenter__', '__aexit__'):
3099
+ for b in cls.__bases__:
3100
+ if b is AsyncExitStacked:
3101
+ continue
3102
+ try:
3103
+ fn = getattr(b, a)
3104
+ except AttributeError:
3105
+ pass
3106
+ else:
3107
+ if fn is not getattr(AsyncExitStacked, a):
3108
+ raise TypeError(f'AsyncExitStacked subclass {cls} must not not override {a} via {b}')
3109
+
3051
3110
  _exit_stack: ta.Optional[contextlib.AsyncExitStack] = None
3052
3111
 
3112
+ @contextlib.asynccontextmanager
3113
+ async def _async_exit_stacked_init_wrapper(self) -> ta.AsyncGenerator[None, None]:
3114
+ yield
3115
+
3116
+ @ta.final
3053
3117
  async def __aenter__(self: AsyncExitStackedT) -> AsyncExitStackedT:
3054
- check.state(self._exit_stack is None)
3055
- es = self._exit_stack = contextlib.AsyncExitStack()
3056
- await es.__aenter__()
3057
- return self
3118
+ async with self._async_exit_stacked_init_wrapper():
3119
+ check.state(self._exit_stack is None)
3120
+ es = self._exit_stack = contextlib.AsyncExitStack()
3121
+ await es.__aenter__()
3122
+ try:
3123
+ await self._async_enter_contexts()
3124
+ except Exception: # noqa
3125
+ await es.__aexit__(*sys.exc_info())
3126
+ raise
3127
+ return self
3058
3128
 
3129
+ @ta.final
3059
3130
  async def __aexit__(self, exc_type, exc_val, exc_tb):
3060
3131
  if (es := self._exit_stack) is None:
3061
3132
  return None
3062
- await self._async_exit_contexts()
3133
+ try:
3134
+ await self._async_exit_contexts()
3135
+ except Exception: # noqa
3136
+ await es.__aexit__(*sys.exc_info())
3137
+ raise
3063
3138
  return await es.__aexit__(exc_type, exc_val, exc_tb)
3064
3139
 
3140
+ async def _async_enter_contexts(self) -> None:
3141
+ pass
3142
+
3065
3143
  async def _async_exit_contexts(self) -> None:
3066
3144
  pass
3067
3145
 
@@ -4078,9 +4156,10 @@ class ThreadWorker(ExitStacked, abc.ABC):
4078
4156
 
4079
4157
  #
4080
4158
 
4081
- def __enter__(self: ThreadWorkerT) -> ThreadWorkerT:
4159
+ @contextlib.contextmanager
4160
+ def _exit_stacked_init_wrapper(self) -> ta.Iterator[None]:
4082
4161
  with self._lock:
4083
- return super().__enter__() # noqa
4162
+ yield
4084
4163
 
4085
4164
  #
4086
4165
 
ominfra/scripts/manage.py CHANGED
@@ -5511,20 +5511,64 @@ DEFAULT_CONFIG_RENDERER = SwitchedConfigRenderer(DEFAULT_CONFIG_RENDERERS)
5511
5511
 
5512
5512
 
5513
5513
  class ExitStacked:
5514
+ def __init_subclass__(cls, **kwargs: ta.Any) -> None:
5515
+ super().__init_subclass__(**kwargs)
5516
+
5517
+ for a in ('__enter__', '__exit__'):
5518
+ for b in cls.__bases__:
5519
+ if b is ExitStacked:
5520
+ continue
5521
+ try:
5522
+ fn = getattr(b, a)
5523
+ except AttributeError:
5524
+ pass
5525
+ else:
5526
+ if fn is not getattr(ExitStacked, a):
5527
+ raise TypeError(f'ExitStacked subclass {cls} must not not override {a} via {b}')
5528
+
5514
5529
  _exit_stack: ta.Optional[contextlib.ExitStack] = None
5515
5530
 
5531
+ @contextlib.contextmanager
5532
+ def _exit_stacked_init_wrapper(self) -> ta.Iterator[None]:
5533
+ """
5534
+ Overridable wrapper around __enter__ which deliberately does not have access to an _exit_stack yet. Intended for
5535
+ things like wrapping __enter__ in a lock.
5536
+ """
5537
+
5538
+ yield
5539
+
5540
+ @ta.final
5516
5541
  def __enter__(self: ExitStackedT) -> ExitStackedT:
5517
- check.state(self._exit_stack is None)
5518
- es = self._exit_stack = contextlib.ExitStack()
5519
- es.__enter__()
5520
- return self
5542
+ """
5543
+ Final because any contexts entered during this init must be exited if any exception is thrown, and user
5544
+ overriding would likely interfere with that. Override `_enter_contexts` for such init.
5545
+ """
5521
5546
 
5547
+ with self._exit_stacked_init_wrapper():
5548
+ check.state(self._exit_stack is None)
5549
+ es = self._exit_stack = contextlib.ExitStack()
5550
+ es.__enter__()
5551
+ try:
5552
+ self._enter_contexts()
5553
+ except Exception: # noqa
5554
+ es.__exit__(*sys.exc_info())
5555
+ raise
5556
+ return self
5557
+
5558
+ @ta.final
5522
5559
  def __exit__(self, exc_type, exc_val, exc_tb):
5523
5560
  if (es := self._exit_stack) is None:
5524
5561
  return None
5525
- self._exit_contexts()
5562
+ try:
5563
+ self._exit_contexts()
5564
+ except Exception: # noqa
5565
+ es.__exit__(*sys.exc_info())
5566
+ raise
5526
5567
  return es.__exit__(exc_type, exc_val, exc_tb)
5527
5568
 
5569
+ def _enter_contexts(self) -> None:
5570
+ pass
5571
+
5528
5572
  def _exit_contexts(self) -> None:
5529
5573
  pass
5530
5574
 
@@ -5534,20 +5578,54 @@ class ExitStacked:
5534
5578
 
5535
5579
 
5536
5580
  class AsyncExitStacked:
5581
+ def __init_subclass__(cls, **kwargs: ta.Any) -> None:
5582
+ super().__init_subclass__(**kwargs)
5583
+
5584
+ for a in ('__aenter__', '__aexit__'):
5585
+ for b in cls.__bases__:
5586
+ if b is AsyncExitStacked:
5587
+ continue
5588
+ try:
5589
+ fn = getattr(b, a)
5590
+ except AttributeError:
5591
+ pass
5592
+ else:
5593
+ if fn is not getattr(AsyncExitStacked, a):
5594
+ raise TypeError(f'AsyncExitStacked subclass {cls} must not not override {a} via {b}')
5595
+
5537
5596
  _exit_stack: ta.Optional[contextlib.AsyncExitStack] = None
5538
5597
 
5598
+ @contextlib.asynccontextmanager
5599
+ async def _async_exit_stacked_init_wrapper(self) -> ta.AsyncGenerator[None, None]:
5600
+ yield
5601
+
5602
+ @ta.final
5539
5603
  async def __aenter__(self: AsyncExitStackedT) -> AsyncExitStackedT:
5540
- check.state(self._exit_stack is None)
5541
- es = self._exit_stack = contextlib.AsyncExitStack()
5542
- await es.__aenter__()
5543
- return self
5604
+ async with self._async_exit_stacked_init_wrapper():
5605
+ check.state(self._exit_stack is None)
5606
+ es = self._exit_stack = contextlib.AsyncExitStack()
5607
+ await es.__aenter__()
5608
+ try:
5609
+ await self._async_enter_contexts()
5610
+ except Exception: # noqa
5611
+ await es.__aexit__(*sys.exc_info())
5612
+ raise
5613
+ return self
5544
5614
 
5615
+ @ta.final
5545
5616
  async def __aexit__(self, exc_type, exc_val, exc_tb):
5546
5617
  if (es := self._exit_stack) is None:
5547
5618
  return None
5548
- await self._async_exit_contexts()
5619
+ try:
5620
+ await self._async_exit_contexts()
5621
+ except Exception: # noqa
5622
+ await es.__aexit__(*sys.exc_info())
5623
+ raise
5549
5624
  return await es.__aexit__(exc_type, exc_val, exc_tb)
5550
5625
 
5626
+ async def _async_enter_contexts(self) -> None:
5627
+ pass
5628
+
5551
5629
  async def _async_exit_contexts(self) -> None:
5552
5630
  pass
5553
5631
 
ominfra/threadworkers.py CHANGED
@@ -10,6 +10,7 @@ TODO:
10
10
  - shared stop_event?
11
11
  """
12
12
  import abc
13
+ import contextlib
13
14
  import dataclasses as dc
14
15
  import threading
15
16
  import time
@@ -48,9 +49,10 @@ class ThreadWorker(ExitStacked, abc.ABC):
48
49
 
49
50
  #
50
51
 
51
- def __enter__(self: ThreadWorkerT) -> ThreadWorkerT:
52
+ @contextlib.contextmanager
53
+ def _exit_stacked_init_wrapper(self) -> ta.Iterator[None]:
52
54
  with self._lock:
53
- return super().__enter__() # noqa
55
+ yield
54
56
 
55
57
  #
56
58
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: ominfra
3
- Version: 0.0.0.dev242
3
+ Version: 0.0.0.dev243
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.dev242
16
- Requires-Dist: omlish==0.0.0.dev242
15
+ Requires-Dist: omdev==0.0.0.dev243
16
+ Requires-Dist: omlish==0.0.0.dev243
17
17
  Provides-Extra: all
18
18
  Requires-Dist: paramiko~=3.5; extra == "all"
19
19
  Requires-Dist: asyncssh~=2.18; extra == "all"
@@ -3,7 +3,7 @@ ominfra/__about__.py,sha256=6i1AoruFYQCd-PyhhbDQDWY2d1tiQu9nkwWr-fXAqfY,705
3
3
  ominfra/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
4
4
  ominfra/pyremote.py,sha256=qjY3Bj0GhuYznQ3bhqXmU9zyIVZkhD9Wv76QUG34ZME,15198
5
5
  ominfra/systemd.py,sha256=d61NVrJoItzSaqhMDgKGrQjbrxEVAujUMDsj8WggXos,1022
6
- ominfra/threadworkers.py,sha256=oX4ubZn7h932saXpRIJu2MNhBExgGGMuGhdXarZxLJw,4948
6
+ ominfra/threadworkers.py,sha256=ADWHAdvjzPxm94yJcEt5_nM7q7aahdQUNP_EGQeOWAw,4974
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
@@ -112,8 +112,8 @@ ominfra/manage/targets/connection.py,sha256=rVI1YJxFClcF-sdttqWyIz9_XjPI01GUdwxY
112
112
  ominfra/manage/targets/inject.py,sha256=P4597xWM-V3I_gCt2O71OLhYQkkXtuJvkYRsIbhhMcE,1561
113
113
  ominfra/manage/targets/targets.py,sha256=7GP6UAZyJFEhpkJN6UQdpr_WN3p7C76v-s445y-WB6U,1885
114
114
  ominfra/scripts/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
115
- ominfra/scripts/journald2aws.py,sha256=Hub3-WL7ZzxU9-UaIB78eT9grq4XldTyd3EZmcY2ulI,164834
116
- ominfra/scripts/manage.py,sha256=REje_aRYaa1k0gXdLtZfZo6TU2K7hsR0LoCIq0BEZVI,374968
115
+ ominfra/scripts/journald2aws.py,sha256=i1WBsTvVx8XetO_Kl0hGPHGDAFhPybyMBm60bvg_Hi8,167534
116
+ ominfra/scripts/manage.py,sha256=KMYlDMLDa9W4dQXwRpc0q0dHnNGT8Q0Vk2Gh7hq1DZo,377660
117
117
  ominfra/scripts/supervisor.py,sha256=XQGNjIHFvyy7S5uv7bQ4hOne4hqWqny1SLcNlr1vX7w,296335
118
118
  ominfra/supervisor/LICENSE.txt,sha256=ZrHY15PVR98y26Yg6iQfa-SXnUaYTDhrUsPVcEO5OKM,1874
119
119
  ominfra/supervisor/__init__.py,sha256=Y3l4WY4JRi2uLG6kgbGp93fuGfkxkKwZDvhsa0Rwgtk,15
@@ -156,9 +156,9 @@ ominfra/tailscale/api.py,sha256=C5-t_b6jZXUWcy5k8bXm7CFnk73pSdrlMOgGDeGVrpw,1370
156
156
  ominfra/tailscale/cli.py,sha256=3FnJbgpLw6gInTfhERd1mDy9ijjMUGxkdYVo43Tnxx4,3555
157
157
  ominfra/tools/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
158
158
  ominfra/tools/listresources.py,sha256=auGP1LlbBJSFKUWNvQo_UzA8IsBNZBTMwEkFFRJ4FX4,6185
159
- ominfra-0.0.0.dev242.dist-info/LICENSE,sha256=B_hVtavaA8zCYDW99DYdcpDLKz1n3BBRjZrcbv8uG8c,1451
160
- ominfra-0.0.0.dev242.dist-info/METADATA,sha256=LH7niglYt6yUAtKp3nsbr4-xGYyk-3JFjgMm9BNZvl4,731
161
- ominfra-0.0.0.dev242.dist-info/WHEEL,sha256=jB7zZ3N9hIM9adW7qlTAyycLYW9npaWKLRzaoVcLKcM,91
162
- ominfra-0.0.0.dev242.dist-info/entry_points.txt,sha256=kgecQ2MgGrM9qK744BoKS3tMesaC3yjLnl9pa5CRczg,37
163
- ominfra-0.0.0.dev242.dist-info/top_level.txt,sha256=E-b2OHkk_AOBLXHYZQ2EOFKl-_6uOGd8EjeG-Zy6h_w,8
164
- ominfra-0.0.0.dev242.dist-info/RECORD,,
159
+ ominfra-0.0.0.dev243.dist-info/LICENSE,sha256=B_hVtavaA8zCYDW99DYdcpDLKz1n3BBRjZrcbv8uG8c,1451
160
+ ominfra-0.0.0.dev243.dist-info/METADATA,sha256=Yb-OAI8b5WxeyUO6lZcUcECtTvFcxhEAYCS6EiW0rdU,731
161
+ ominfra-0.0.0.dev243.dist-info/WHEEL,sha256=jB7zZ3N9hIM9adW7qlTAyycLYW9npaWKLRzaoVcLKcM,91
162
+ ominfra-0.0.0.dev243.dist-info/entry_points.txt,sha256=kgecQ2MgGrM9qK744BoKS3tMesaC3yjLnl9pa5CRczg,37
163
+ ominfra-0.0.0.dev243.dist-info/top_level.txt,sha256=E-b2OHkk_AOBLXHYZQ2EOFKl-_6uOGd8EjeG-Zy6h_w,8
164
+ ominfra-0.0.0.dev243.dist-info/RECORD,,