omlish 0.0.0.dev160__py3-none-any.whl → 0.0.0.dev162__py3-none-any.whl

Sign up to get free protection for your applications and to get access to all the features.
omlish/lite/inject.py CHANGED
@@ -610,7 +610,7 @@ class InjectorBinder:
610
610
  def __new__(cls, *args, **kwargs): # noqa
611
611
  raise TypeError
612
612
 
613
- _FN_TYPES: ta.Tuple[type, ...] = (
613
+ _FN_TYPES: ta.ClassVar[ta.Tuple[type, ...]] = (
614
614
  types.FunctionType,
615
615
  types.MethodType,
616
616
 
@@ -632,7 +632,7 @@ class InjectorBinder:
632
632
  cls._FN_TYPES = (*cls._FN_TYPES, icls)
633
633
  return icls
634
634
 
635
- _BANNED_BIND_TYPES: ta.Tuple[type, ...] = (
635
+ _BANNED_BIND_TYPES: ta.ClassVar[ta.Tuple[type, ...]] = (
636
636
  InjectorProvider,
637
637
  )
638
638
 
@@ -811,45 +811,35 @@ def bind_injector_eager_key(key: ta.Any) -> InjectorBinding:
811
811
  ##
812
812
 
813
813
 
814
- class Injection:
815
- def __new__(cls, *args, **kwargs): # noqa
816
- raise TypeError
817
-
814
+ class InjectionApi:
818
815
  # keys
819
816
 
820
- @classmethod
821
- def as_key(cls, o: ta.Any) -> InjectorKey:
817
+ def as_key(self, o: ta.Any) -> InjectorKey:
822
818
  return as_injector_key(o)
823
819
 
824
- @classmethod
825
- def array(cls, o: ta.Any) -> InjectorKey:
820
+ def array(self, o: ta.Any) -> InjectorKey:
826
821
  return dc.replace(as_injector_key(o), array=True)
827
822
 
828
- @classmethod
829
- def tag(cls, o: ta.Any, t: ta.Any) -> InjectorKey:
823
+ def tag(self, o: ta.Any, t: ta.Any) -> InjectorKey:
830
824
  return dc.replace(as_injector_key(o), tag=t)
831
825
 
832
826
  # bindings
833
827
 
834
- @classmethod
835
- def as_bindings(cls, *args: InjectorBindingOrBindings) -> InjectorBindings:
828
+ def as_bindings(self, *args: InjectorBindingOrBindings) -> InjectorBindings:
836
829
  return as_injector_bindings(*args)
837
830
 
838
- @classmethod
839
- def override(cls, p: InjectorBindings, *args: InjectorBindingOrBindings) -> InjectorBindings:
831
+ def override(self, p: InjectorBindings, *args: InjectorBindingOrBindings) -> InjectorBindings:
840
832
  return injector_override(p, *args)
841
833
 
842
834
  # injector
843
835
 
844
- @classmethod
845
- def create_injector(cls, *args: InjectorBindingOrBindings, parent: ta.Optional[Injector] = None) -> Injector:
836
+ def create_injector(self, *args: InjectorBindingOrBindings, parent: ta.Optional[Injector] = None) -> Injector:
846
837
  return _Injector(as_injector_bindings(*args), parent)
847
838
 
848
839
  # binder
849
840
 
850
- @classmethod
851
841
  def bind(
852
- cls,
842
+ self,
853
843
  obj: ta.Any,
854
844
  *,
855
845
  key: ta.Any = None,
@@ -884,32 +874,29 @@ class Injection:
884
874
 
885
875
  # helpers
886
876
 
887
- @classmethod
888
877
  def bind_factory(
889
- cls,
878
+ self,
890
879
  fn: ta.Callable[..., T],
891
880
  cls_: U,
892
881
  ann: ta.Any = None,
893
882
  ) -> InjectorBindingOrBindings:
894
- return cls.bind(make_injector_factory(fn, cls_, ann))
883
+ return self.bind(make_injector_factory(fn, cls_, ann))
895
884
 
896
- @classmethod
897
885
  def bind_array(
898
- cls,
886
+ self,
899
887
  obj: ta.Any = None,
900
888
  *,
901
889
  tag: ta.Any = None,
902
890
  ) -> InjectorBindingOrBindings:
903
891
  return bind_injector_array(obj, tag=tag)
904
892
 
905
- @classmethod
906
893
  def bind_array_type(
907
- cls,
894
+ self,
908
895
  ele: ta.Union[InjectorKey, InjectorKeyCls],
909
896
  cls_: U,
910
897
  ann: ta.Any = None,
911
898
  ) -> InjectorBindingOrBindings:
912
- return cls.bind(make_injector_array_type(ele, cls_, ann))
899
+ return self.bind(make_injector_array_type(ele, cls_, ann))
913
900
 
914
901
 
915
- inj = Injection
902
+ inj = InjectionApi()
omlish/os/atomics.py ADDED
@@ -0,0 +1,205 @@
1
+ # ruff: noqa: UP006 UP007
2
+ # @omlish-lite
3
+ import abc
4
+ import os
5
+ import shutil
6
+ import tempfile
7
+ import typing as ta
8
+
9
+ from omlish.lite.check import check
10
+ from omlish.lite.strings import attr_repr
11
+
12
+
13
+ AtomicPathSwapKind = ta.Literal['dir', 'file']
14
+ AtomicPathSwapState = ta.Literal['open', 'committed', 'aborted'] # ta.TypeAlias
15
+
16
+
17
+ ##
18
+
19
+
20
+ class AtomicPathSwap(abc.ABC):
21
+ def __init__(
22
+ self,
23
+ kind: AtomicPathSwapKind,
24
+ dst_path: str,
25
+ *,
26
+ auto_commit: bool = False,
27
+ ) -> None:
28
+ super().__init__()
29
+
30
+ self._kind = kind
31
+ self._dst_path = dst_path
32
+ self._auto_commit = auto_commit
33
+
34
+ self._state: AtomicPathSwapState = 'open'
35
+
36
+ def __repr__(self) -> str:
37
+ return attr_repr(self, 'kind', 'dst_path', 'tmp_path')
38
+
39
+ @property
40
+ def kind(self) -> AtomicPathSwapKind:
41
+ return self._kind
42
+
43
+ @property
44
+ def dst_path(self) -> str:
45
+ return self._dst_path
46
+
47
+ @property
48
+ @abc.abstractmethod
49
+ def tmp_path(self) -> str:
50
+ raise NotImplementedError
51
+
52
+ #
53
+
54
+ @property
55
+ def state(self) -> AtomicPathSwapState:
56
+ return self._state
57
+
58
+ def _check_state(self, *states: AtomicPathSwapState) -> None:
59
+ if self._state not in states:
60
+ raise RuntimeError(f'Atomic path swap not in correct state: {self._state}, {states}')
61
+
62
+ #
63
+
64
+ @abc.abstractmethod
65
+ def _commit(self) -> None:
66
+ raise NotImplementedError
67
+
68
+ def commit(self) -> None:
69
+ if self._state == 'committed':
70
+ return
71
+ self._check_state('open')
72
+ try:
73
+ self._commit()
74
+ except Exception: # noqa
75
+ self._abort()
76
+ raise
77
+ else:
78
+ self._state = 'committed'
79
+
80
+ #
81
+
82
+ @abc.abstractmethod
83
+ def _abort(self) -> None:
84
+ raise NotImplementedError
85
+
86
+ def abort(self) -> None:
87
+ if self._state == 'aborted':
88
+ return
89
+ self._abort()
90
+ self._state = 'aborted'
91
+
92
+ #
93
+
94
+ def __enter__(self) -> 'AtomicPathSwap':
95
+ return self
96
+
97
+ def __exit__(self, exc_type, exc_val, exc_tb):
98
+ if (
99
+ exc_type is None and
100
+ self._auto_commit and
101
+ self._state == 'open'
102
+ ):
103
+ self.commit()
104
+ else:
105
+ self.abort()
106
+
107
+
108
+ class AtomicPathSwapping(abc.ABC):
109
+ @abc.abstractmethod
110
+ def begin_atomic_path_swap(
111
+ self,
112
+ kind: AtomicPathSwapKind,
113
+ dst_path: str,
114
+ *,
115
+ name_hint: ta.Optional[str] = None,
116
+ make_dirs: bool = False,
117
+ **kwargs: ta.Any,
118
+ ) -> AtomicPathSwap:
119
+ raise NotImplementedError
120
+
121
+
122
+ ##
123
+
124
+
125
+ class OsRenameAtomicPathSwap(AtomicPathSwap):
126
+ def __init__(
127
+ self,
128
+ kind: AtomicPathSwapKind,
129
+ dst_path: str,
130
+ tmp_path: str,
131
+ **kwargs: ta.Any,
132
+ ) -> None:
133
+ if kind == 'dir':
134
+ check.state(os.path.isdir(tmp_path))
135
+ elif kind == 'file':
136
+ check.state(os.path.isfile(tmp_path))
137
+ else:
138
+ raise TypeError(kind)
139
+
140
+ super().__init__(
141
+ kind,
142
+ dst_path,
143
+ **kwargs,
144
+ )
145
+
146
+ self._tmp_path = tmp_path
147
+
148
+ @property
149
+ def tmp_path(self) -> str:
150
+ return self._tmp_path
151
+
152
+ def _commit(self) -> None:
153
+ os.rename(self._tmp_path, self._dst_path)
154
+
155
+ def _abort(self) -> None:
156
+ shutil.rmtree(self._tmp_path, ignore_errors=True)
157
+
158
+
159
+ class TempDirAtomicPathSwapping(AtomicPathSwapping):
160
+ def __init__(
161
+ self,
162
+ *,
163
+ temp_dir: ta.Optional[str] = None,
164
+ root_dir: ta.Optional[str] = None,
165
+ ) -> None:
166
+ super().__init__()
167
+
168
+ if root_dir is not None:
169
+ root_dir = os.path.abspath(root_dir)
170
+ self._root_dir = root_dir
171
+ self._temp_dir = temp_dir
172
+
173
+ def begin_atomic_path_swap(
174
+ self,
175
+ kind: AtomicPathSwapKind,
176
+ dst_path: str,
177
+ *,
178
+ name_hint: ta.Optional[str] = None,
179
+ make_dirs: bool = False,
180
+ **kwargs: ta.Any,
181
+ ) -> AtomicPathSwap:
182
+ dst_path = os.path.abspath(dst_path)
183
+ if self._root_dir is not None and not dst_path.startswith(check.non_empty_str(self._root_dir)):
184
+ raise RuntimeError(f'Atomic path swap dst must be in root dir: {dst_path}, {self._root_dir}')
185
+
186
+ dst_dir = os.path.dirname(dst_path)
187
+ if make_dirs:
188
+ os.makedirs(dst_dir, exist_ok=True)
189
+ if not os.path.isdir(dst_dir):
190
+ raise RuntimeError(f'Atomic path swap dst dir does not exist: {dst_dir}')
191
+
192
+ if kind == 'dir':
193
+ tmp_path = tempfile.mkdtemp(prefix=name_hint, dir=self._temp_dir)
194
+ elif kind == 'file':
195
+ fd, tmp_path = tempfile.mkstemp(prefix=name_hint, dir=self._temp_dir)
196
+ os.close(fd)
197
+ else:
198
+ raise TypeError(kind)
199
+
200
+ return OsRenameAtomicPathSwap(
201
+ kind,
202
+ dst_path,
203
+ tmp_path,
204
+ **kwargs,
205
+ )
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: omlish
3
- Version: 0.0.0.dev160
3
+ Version: 0.0.0.dev162
4
4
  Summary: omlish
5
5
  Author: wrmsr
6
6
  License: BSD-3-Clause
@@ -1,5 +1,5 @@
1
1
  omlish/.manifests.json,sha256=RX24SRc6DCEg77PUVnaXOKCWa5TF_c9RQJdGIf7gl9c,1135
2
- omlish/__about__.py,sha256=YIDkVlJyMJUrEtEf00ng1bGGafE3fLQ7pZzCbPUNOjE,3409
2
+ omlish/__about__.py,sha256=RGH11TXe-0tI-c-75Cmj5Em5DfZ8d1t-Xincc1SM3bE,3409
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
@@ -92,6 +92,15 @@ omlish/asyncs/asyncio/channels.py,sha256=ZbmsEmdK1fV96liHdcVpRqA2dAMkXJt4Q3rFAg3
92
92
  omlish/asyncs/asyncio/streams.py,sha256=Uc9PCWSfBqrK2kdVtfjjQU1eaYTWYmZm8QISDj2xiuw,1004
93
93
  omlish/asyncs/asyncio/subprocesses.py,sha256=XlIWwSxpVB7sMVc75-f7dI6r08JkdipNFRWXUKS8zAw,6960
94
94
  omlish/asyncs/asyncio/timeouts.py,sha256=Rj5OU9BIAPcVZZKp74z7SzUXF5xokh4dgsWkUqOy1aE,355
95
+ omlish/asyncs/bluelet/LICENSE,sha256=VHf3oPQihOHnWyIR8LcXX0dpONa1lgyJnjWC2qVuRR0,559
96
+ omlish/asyncs/bluelet/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
97
+ omlish/asyncs/bluelet/all.py,sha256=aUV6PwnR8DqnEBS9wsZuPW_UtP6G9M8_KY-mmxZeVG0,1516
98
+ omlish/asyncs/bluelet/api.py,sha256=yeM5SauiIbvKj26mDdxn5mOj8s5fFIw2xBSe-P6HVC0,904
99
+ omlish/asyncs/bluelet/core.py,sha256=NpGQhb865aeXPKV_67l-6FiuQzdDJJ85zjfvCR5R8Pc,5430
100
+ omlish/asyncs/bluelet/events.py,sha256=iXpRWmy64YcshT_nuyiJ39jbketZdtj8LrdlX3JmpoY,2440
101
+ omlish/asyncs/bluelet/files.py,sha256=pgcLV_3oGbpqQmOrii8SeizyYLp8XKofQJhqM82RlKw,2389
102
+ omlish/asyncs/bluelet/runner.py,sha256=cwgVjwRRXzqfOqRD0rkPHjt_Udyt9ZKpVs36uAJ_Q7c,15473
103
+ omlish/asyncs/bluelet/sockets.py,sha256=RrC2vU52dOEBYKzvoh1qA39uUE8p3uCB_oxnhaP1AeA,6752
95
104
  omlish/bootstrap/__init__.py,sha256=-Rtsg7uPQNhh1dIT9nqrz96XlqizwoLnWf-FwOEstJI,730
96
105
  omlish/bootstrap/__main__.py,sha256=4jCwsaogp0FrJjJZ85hzF4-WqluPeheHbfeoKynKvNs,194
97
106
  omlish/bootstrap/base.py,sha256=d8hqn4hp1XMMi5PgcJBQXPKmW47epu8CxBlqDZiRZb4,1073
@@ -339,7 +348,7 @@ omlish/lite/__init__.py,sha256=Y3l4WY4JRi2uLG6kgbGp93fuGfkxkKwZDvhsa0Rwgtk,15
339
348
  omlish/lite/cached.py,sha256=O7ozcoDNFm1Hg2wtpHEqYSp_i_nCLNOP6Ueq_Uk-7mU,1300
340
349
  omlish/lite/check.py,sha256=KvcO86LqWlh2j4ORaZXRR4FM8fFb7kUkNqq3lTs0Ta0,12821
341
350
  omlish/lite/contextmanagers.py,sha256=m9JO--p7L7mSl4cycXysH-1AO27weDKjP3DZG61cwwM,1683
342
- omlish/lite/inject.py,sha256=729Qi0TLbQgBtkvx97q1EUMe73VFYA1hu4woXkOTcwM,23572
351
+ omlish/lite/inject.py,sha256=EEaioN9ESAveVCMe2s5osjwI97FPRUVoU8P95vGUiYo,23376
343
352
  omlish/lite/json.py,sha256=7-02Ny4fq-6YAu5ynvqoijhuYXWpLmfCI19GUeZnb1c,740
344
353
  omlish/lite/logs.py,sha256=CWFG0NKGhqNeEgryF5atN2gkPYbUdTINEw_s1phbINM,51
345
354
  omlish/lite/marshal.py,sha256=ldoZs_yiQIUpOjBviV9f4mwm7hSZy0hRLXrvQA-6POU,14257
@@ -403,6 +412,7 @@ omlish/math/bits.py,sha256=yip1l8agOYzT7bFyMGc0RR3XlnGCfHMpjw_SECLLh1I,3477
403
412
  omlish/math/floats.py,sha256=UimhOT7KRl8LXTzOI5cQWoX_9h6WNWe_3vcOuO7-h_8,327
404
413
  omlish/math/stats.py,sha256=MegzKVsmv2kra4jDWLOUgV0X7Ee2Tbl5u6ql1v4-dEY,10053
405
414
  omlish/os/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
415
+ omlish/os/atomics.py,sha256=gArGtyH5l3UjLKwKIqFJ5XkRnNouXntPEtkRbI0ch_0,5071
406
416
  omlish/os/deathsig.py,sha256=hk9Yq2kyDdI-cI7OQH7mOfpRbOKzY_TfPKEqgrjVYbA,641
407
417
  omlish/os/files.py,sha256=1tNy1z5I_CgYKA5c6lOfsXc-hknP4tQDbSShdz8HArw,1308
408
418
  omlish/os/journald.py,sha256=2nI8Res1poXkbLc31--MPUlzYMESnCcPUkIxDOCjZW0,3903
@@ -525,9 +535,9 @@ omlish/text/glyphsplit.py,sha256=Ug-dPRO7x-OrNNr8g1y6DotSZ2KH0S-VcOmUobwa4B0,329
525
535
  omlish/text/indent.py,sha256=6Jj6TFY9unaPa4xPzrnZemJ-fHsV53IamP93XGjSUHs,1274
526
536
  omlish/text/parts.py,sha256=7vPF1aTZdvLVYJ4EwBZVzRSy8XB3YqPd7JwEnNGGAOo,6495
527
537
  omlish/text/random.py,sha256=jNWpqiaKjKyTdMXC-pWAsSC10AAP-cmRRPVhm59ZWLk,194
528
- omlish-0.0.0.dev160.dist-info/LICENSE,sha256=B_hVtavaA8zCYDW99DYdcpDLKz1n3BBRjZrcbv8uG8c,1451
529
- omlish-0.0.0.dev160.dist-info/METADATA,sha256=CZH9L3qhAM-tYsJfEokB-s2fTLnRy-QMkOSnErJHumc,4264
530
- omlish-0.0.0.dev160.dist-info/WHEEL,sha256=PZUExdf71Ui_so67QXpySuHtCi3-J3wvF4ORK6k_S8U,91
531
- omlish-0.0.0.dev160.dist-info/entry_points.txt,sha256=Lt84WvRZJskWCAS7xnQGZIeVWksprtUHj0llrvVmod8,35
532
- omlish-0.0.0.dev160.dist-info/top_level.txt,sha256=pePsKdLu7DvtUiecdYXJ78iO80uDNmBlqe-8hOzOmfs,7
533
- omlish-0.0.0.dev160.dist-info/RECORD,,
538
+ omlish-0.0.0.dev162.dist-info/LICENSE,sha256=B_hVtavaA8zCYDW99DYdcpDLKz1n3BBRjZrcbv8uG8c,1451
539
+ omlish-0.0.0.dev162.dist-info/METADATA,sha256=thW6t3ofpA5bodN1Uc7hDhNjXOwZd68S1SqJZuv4hIw,4264
540
+ omlish-0.0.0.dev162.dist-info/WHEEL,sha256=PZUExdf71Ui_so67QXpySuHtCi3-J3wvF4ORK6k_S8U,91
541
+ omlish-0.0.0.dev162.dist-info/entry_points.txt,sha256=Lt84WvRZJskWCAS7xnQGZIeVWksprtUHj0llrvVmod8,35
542
+ omlish-0.0.0.dev162.dist-info/top_level.txt,sha256=pePsKdLu7DvtUiecdYXJ78iO80uDNmBlqe-8hOzOmfs,7
543
+ omlish-0.0.0.dev162.dist-info/RECORD,,