omlish 0.0.0.dev149__py3-none-any.whl → 0.0.0.dev151__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.
omlish/lite/check.py CHANGED
@@ -1,104 +1,462 @@
1
1
  # ruff: noqa: UP006 UP007
2
+ """
3
+ TODO:
4
+ - def maybe(v: lang.Maybe[T])
5
+ - patch / override lite.check ?
6
+ - checker interface?
7
+ """
8
+ import collections
9
+ import threading
2
10
  import typing as ta
3
11
 
4
12
 
5
13
  T = ta.TypeVar('T')
6
14
  SizedT = ta.TypeVar('SizedT', bound=ta.Sized)
7
15
 
16
+ CheckMessage = ta.Union[str, ta.Callable[..., ta.Optional[str]], None] # ta.TypeAlias
17
+ CheckLateConfigureFn = ta.Callable[['Checks'], None] # ta.TypeAlias
18
+ CheckOnRaiseFn = ta.Callable[[Exception], None] # ta.TypeAlias
19
+ CheckExceptionFactory = ta.Callable[..., Exception] # ta.TypeAlias
20
+ CheckArgsRenderer = ta.Callable[..., ta.Optional[str]] # ta.TypeAlias
8
21
 
9
- def check_isinstance(v: ta.Any, spec: ta.Union[ta.Type[T], tuple]) -> T:
10
- if not isinstance(v, spec):
11
- raise TypeError(v)
12
- return v
13
22
 
23
+ ##
14
24
 
15
- def check_not_isinstance(v: T, spec: ta.Union[type, tuple]) -> T:
16
- if isinstance(v, spec):
17
- raise TypeError(v)
18
- return v
19
25
 
26
+ class Checks:
27
+ def __init__(self) -> None:
28
+ super().__init__()
20
29
 
21
- def check_none(v: T) -> None:
22
- if v is not None:
23
- raise ValueError(v)
30
+ self._config_lock = threading.RLock()
31
+ self._on_raise_fns: ta.Sequence[CheckOnRaiseFn] = []
32
+ self._exception_factory: CheckExceptionFactory = Checks.default_exception_factory
33
+ self._args_renderer: ta.Optional[CheckArgsRenderer] = None
34
+ self._late_configure_fns: ta.Sequence[CheckLateConfigureFn] = []
24
35
 
36
+ @staticmethod
37
+ def default_exception_factory(exc_cls: ta.Type[Exception], *args, **kwargs) -> Exception:
38
+ return exc_cls(*args, **kwargs) # noqa
25
39
 
26
- def check_not_none(v: ta.Optional[T]) -> T:
27
- if v is None:
28
- raise ValueError
29
- return v
40
+ #
30
41
 
42
+ def register_on_raise(self, fn: CheckOnRaiseFn) -> None:
43
+ with self._config_lock:
44
+ self._on_raise_fns = [*self._on_raise_fns, fn]
31
45
 
32
- def check_not(v: ta.Any) -> None:
33
- if v:
34
- raise ValueError(v)
35
- return v
46
+ def unregister_on_raise(self, fn: CheckOnRaiseFn) -> None:
47
+ with self._config_lock:
48
+ self._on_raise_fns = [e for e in self._on_raise_fns if e != fn]
36
49
 
50
+ #
37
51
 
38
- def check_non_empty_str(v: ta.Optional[str]) -> str:
39
- if not v:
40
- raise ValueError
41
- return v
52
+ def set_exception_factory(self, factory: CheckExceptionFactory) -> None:
53
+ self._exception_factory = factory
42
54
 
55
+ def set_args_renderer(self, renderer: ta.Optional[CheckArgsRenderer]) -> None:
56
+ self._args_renderer = renderer
43
57
 
44
- def check_arg(v: bool, msg: str = 'Illegal argument') -> None:
45
- if not v:
46
- raise ValueError(msg)
58
+ #
59
+
60
+ def register_late_configure(self, fn: CheckLateConfigureFn) -> None:
61
+ with self._config_lock:
62
+ self._late_configure_fns = [*self._late_configure_fns, fn]
47
63
 
64
+ def _late_configure(self) -> None:
65
+ if not self._late_configure_fns:
66
+ return
48
67
 
49
- def check_state(v: bool, msg: str = 'Illegal state') -> None:
50
- if not v:
51
- raise ValueError(msg)
68
+ with self._config_lock:
69
+ if not (lc := self._late_configure_fns):
70
+ return
52
71
 
72
+ for fn in lc:
73
+ fn(self)
74
+
75
+ self._late_configure_fns = []
76
+
77
+ #
78
+
79
+ class _ArgsKwargs:
80
+ def __init__(self, *args, **kwargs):
81
+ self.args = args
82
+ self.kwargs = kwargs
83
+
84
+ def _raise(
85
+ self,
86
+ exception_type: ta.Type[Exception],
87
+ default_message: str,
88
+ message: CheckMessage,
89
+ ak: _ArgsKwargs = _ArgsKwargs(),
90
+ *,
91
+ render_fmt: ta.Optional[str] = None,
92
+ ) -> ta.NoReturn:
93
+ exc_args = ()
94
+ if callable(message):
95
+ message = ta.cast(ta.Callable, message)(*ak.args, **ak.kwargs)
96
+ if isinstance(message, tuple):
97
+ message, *exc_args = message # type: ignore
98
+
99
+ if message is None:
100
+ message = default_message
53
101
 
54
- def check_equal(l: T, r: T) -> T:
55
- if l != r:
56
- raise ValueError(l, r)
57
- return l
58
-
59
-
60
- def check_not_equal(l: T, r: T) -> T:
61
- if l == r:
62
- raise ValueError(l, r)
63
- return l
64
-
65
-
66
- def check_is(l: T, r: T) -> T:
67
- if l is not r:
68
- raise ValueError(l, r)
69
- return l
70
-
71
-
72
- def check_is_not(l: T, r: ta.Any) -> T:
73
- if l is r:
74
- raise ValueError(l, r)
75
- return l
76
-
77
-
78
- def check_in(v: T, c: ta.Container[T]) -> T:
79
- if v not in c:
80
- raise ValueError(v, c)
81
- return v
82
-
83
-
84
- def check_not_in(v: T, c: ta.Container[T]) -> T:
85
- if v in c:
86
- raise ValueError(v, c)
87
- return v
88
-
89
-
90
- def check_single(vs: ta.Iterable[T]) -> T:
91
- [v] = vs
92
- return v
93
-
94
-
95
- def check_empty(v: SizedT) -> SizedT:
96
- if len(v):
97
- raise ValueError(v)
98
- return v
99
-
100
-
101
- def check_not_empty(v: SizedT) -> SizedT:
102
- if not len(v):
103
- raise ValueError(v)
104
- return v
102
+ self._late_configure()
103
+
104
+ if render_fmt is not None and (af := self._args_renderer) is not None:
105
+ rendered_args = af(render_fmt, *ak.args)
106
+ if rendered_args is not None:
107
+ message = f'{message} : {rendered_args}'
108
+
109
+ exc = self._exception_factory(
110
+ exception_type,
111
+ message,
112
+ *exc_args,
113
+ *ak.args,
114
+ **ak.kwargs,
115
+ )
116
+
117
+ for fn in self._on_raise_fns:
118
+ fn(exc)
119
+
120
+ raise exc
121
+
122
+ #
123
+
124
+ def _unpack_isinstance_spec(self, spec: ta.Any) -> tuple:
125
+ if isinstance(spec, type):
126
+ return (spec,)
127
+ if not isinstance(spec, tuple):
128
+ spec = (spec,)
129
+ if None in spec:
130
+ spec = tuple(filter(None, spec)) + (None.__class__,) # noqa
131
+ if ta.Any in spec:
132
+ spec = (object,)
133
+ return spec
134
+
135
+ def isinstance(self, v: ta.Any, spec: ta.Union[ta.Type[T], tuple], msg: CheckMessage = None) -> T: # noqa
136
+ if not isinstance(v, self._unpack_isinstance_spec(spec)):
137
+ self._raise(
138
+ TypeError,
139
+ 'Must be instance',
140
+ msg,
141
+ Checks._ArgsKwargs(v, spec),
142
+ render_fmt='not isinstance(%s, %s)',
143
+ )
144
+
145
+ return v
146
+
147
+ def of_isinstance(self, spec: ta.Union[ta.Type[T], tuple], msg: CheckMessage = None) -> ta.Callable[[ta.Any], T]:
148
+ def inner(v):
149
+ return self.isinstance(v, self._unpack_isinstance_spec(spec), msg)
150
+
151
+ return inner
152
+
153
+ def cast(self, v: ta.Any, cls: ta.Type[T], msg: CheckMessage = None) -> T: # noqa
154
+ if not isinstance(v, cls):
155
+ self._raise(
156
+ TypeError,
157
+ 'Must be instance',
158
+ msg,
159
+ Checks._ArgsKwargs(v, cls),
160
+ )
161
+
162
+ return v
163
+
164
+ def of_cast(self, cls: ta.Type[T], msg: CheckMessage = None) -> ta.Callable[[T], T]:
165
+ def inner(v):
166
+ return self.cast(v, cls, msg)
167
+
168
+ return inner
169
+
170
+ def not_isinstance(self, v: T, spec: ta.Any, msg: CheckMessage = None) -> T: # noqa
171
+ if isinstance(v, self._unpack_isinstance_spec(spec)):
172
+ self._raise(
173
+ TypeError,
174
+ 'Must not be instance',
175
+ msg,
176
+ Checks._ArgsKwargs(v, spec),
177
+ render_fmt='isinstance(%s, %s)',
178
+ )
179
+
180
+ return v
181
+
182
+ def of_not_isinstance(self, spec: ta.Any, msg: CheckMessage = None) -> ta.Callable[[T], T]:
183
+ def inner(v):
184
+ return self.not_isinstance(v, self._unpack_isinstance_spec(spec), msg)
185
+
186
+ return inner
187
+
188
+ ##
189
+
190
+ def issubclass(self, v: ta.Type[T], spec: ta.Any, msg: CheckMessage = None) -> ta.Type[T]: # noqa
191
+ if not issubclass(v, spec):
192
+ self._raise(
193
+ TypeError,
194
+ 'Must be subclass',
195
+ msg,
196
+ Checks._ArgsKwargs(v, spec),
197
+ render_fmt='not issubclass(%s, %s)',
198
+ )
199
+
200
+ return v
201
+
202
+ def not_issubclass(self, v: ta.Type[T], spec: ta.Any, msg: CheckMessage = None) -> ta.Type[T]: # noqa
203
+ if issubclass(v, spec):
204
+ self._raise(
205
+ TypeError,
206
+ 'Must not be subclass',
207
+ msg,
208
+ Checks._ArgsKwargs(v, spec),
209
+ render_fmt='issubclass(%s, %s)',
210
+ )
211
+
212
+ return v
213
+
214
+ #
215
+
216
+ def in_(self, v: T, c: ta.Container[T], msg: CheckMessage = None) -> T:
217
+ if v not in c:
218
+ self._raise(
219
+ ValueError,
220
+ 'Must be in',
221
+ msg,
222
+ Checks._ArgsKwargs(v, c),
223
+ render_fmt='%s not in %s',
224
+ )
225
+
226
+ return v
227
+
228
+ def not_in(self, v: T, c: ta.Container[T], msg: CheckMessage = None) -> T:
229
+ if v in c:
230
+ self._raise(
231
+ ValueError,
232
+ 'Must not be in',
233
+ msg,
234
+ Checks._ArgsKwargs(v, c),
235
+ render_fmt='%s in %s',
236
+ )
237
+
238
+ return v
239
+
240
+ def empty(self, v: SizedT, msg: CheckMessage = None) -> SizedT:
241
+ if len(v) != 0:
242
+ self._raise(
243
+ ValueError,
244
+ 'Must be empty',
245
+ msg,
246
+ Checks._ArgsKwargs(v),
247
+ render_fmt='%s',
248
+ )
249
+
250
+ return v
251
+
252
+ def iterempty(self, v: ta.Iterable[T], msg: CheckMessage = None) -> ta.Iterable[T]:
253
+ it = iter(v)
254
+ try:
255
+ next(it)
256
+ except StopIteration:
257
+ pass
258
+ else:
259
+ self._raise(
260
+ ValueError,
261
+ 'Must be empty',
262
+ msg,
263
+ Checks._ArgsKwargs(v),
264
+ render_fmt='%s',
265
+ )
266
+
267
+ return v
268
+
269
+ def not_empty(self, v: SizedT, msg: CheckMessage = None) -> SizedT:
270
+ if len(v) == 0:
271
+ self._raise(
272
+ ValueError,
273
+ 'Must not be empty',
274
+ msg,
275
+ Checks._ArgsKwargs(v),
276
+ render_fmt='%s',
277
+ )
278
+
279
+ return v
280
+
281
+ def unique(self, it: ta.Iterable[T], msg: CheckMessage = None) -> ta.Iterable[T]:
282
+ dupes = [e for e, c in collections.Counter(it).items() if c > 1]
283
+ if dupes:
284
+ self._raise(
285
+ ValueError,
286
+ 'Must be unique',
287
+ msg,
288
+ Checks._ArgsKwargs(it, dupes),
289
+ )
290
+
291
+ return it
292
+
293
+ def single(self, obj: ta.Iterable[T], message: CheckMessage = None) -> T:
294
+ try:
295
+ [value] = obj
296
+ except ValueError:
297
+ self._raise(
298
+ ValueError,
299
+ 'Must be single',
300
+ message,
301
+ Checks._ArgsKwargs(obj),
302
+ render_fmt='%s',
303
+ )
304
+
305
+ return value
306
+
307
+ def opt_single(self, obj: ta.Iterable[T], message: CheckMessage = None) -> ta.Optional[T]:
308
+ it = iter(obj)
309
+ try:
310
+ value = next(it)
311
+ except StopIteration:
312
+ return None
313
+
314
+ try:
315
+ next(it)
316
+ except StopIteration:
317
+ return value # noqa
318
+
319
+ self._raise(
320
+ ValueError,
321
+ 'Must be empty or single',
322
+ message,
323
+ Checks._ArgsKwargs(obj),
324
+ render_fmt='%s',
325
+ )
326
+
327
+ raise RuntimeError # noqa
328
+
329
+ #
330
+
331
+ def none(self, v: ta.Any, msg: CheckMessage = None) -> None:
332
+ if v is not None:
333
+ self._raise(
334
+ ValueError,
335
+ 'Must be None',
336
+ msg,
337
+ Checks._ArgsKwargs(v),
338
+ render_fmt='%s',
339
+ )
340
+
341
+ def not_none(self, v: ta.Optional[T], msg: CheckMessage = None) -> T:
342
+ if v is None:
343
+ self._raise(
344
+ ValueError,
345
+ 'Must not be None',
346
+ msg,
347
+ Checks._ArgsKwargs(v),
348
+ render_fmt='%s',
349
+ )
350
+
351
+ return v
352
+
353
+ #
354
+
355
+ def equal(self, v: T, o: ta.Any, msg: CheckMessage = None) -> T:
356
+ if o != v:
357
+ self._raise(
358
+ ValueError,
359
+ 'Must be equal',
360
+ msg,
361
+ Checks._ArgsKwargs(v, o),
362
+ render_fmt='%s != %s',
363
+ )
364
+
365
+ return v
366
+
367
+ def is_(self, v: T, o: ta.Any, msg: CheckMessage = None) -> T:
368
+ if o is not v:
369
+ self._raise(
370
+ ValueError,
371
+ 'Must be the same',
372
+ msg,
373
+ Checks._ArgsKwargs(v, o),
374
+ render_fmt='%s is not %s',
375
+ )
376
+
377
+ return v
378
+
379
+ def is_not(self, v: T, o: ta.Any, msg: CheckMessage = None) -> T:
380
+ if o is v:
381
+ self._raise(
382
+ ValueError,
383
+ 'Must not be the same',
384
+ msg,
385
+ Checks._ArgsKwargs(v, o),
386
+ render_fmt='%s is %s',
387
+ )
388
+
389
+ return v
390
+
391
+ def callable(self, v: T, msg: CheckMessage = None) -> T: # noqa
392
+ if not callable(v):
393
+ self._raise(
394
+ TypeError,
395
+ 'Must be callable',
396
+ msg,
397
+ Checks._ArgsKwargs(v),
398
+ render_fmt='%s',
399
+ )
400
+
401
+ return v # type: ignore
402
+
403
+ def non_empty_str(self, v: ta.Optional[str], msg: CheckMessage = None) -> str:
404
+ if not isinstance(v, str) or not v:
405
+ self._raise(
406
+ ValueError,
407
+ 'Must be non-empty str',
408
+ msg,
409
+ Checks._ArgsKwargs(v),
410
+ render_fmt='%s',
411
+ )
412
+
413
+ return v
414
+
415
+ def replacing(self, expected: ta.Any, old: ta.Any, new: T, msg: CheckMessage = None) -> T:
416
+ if old != expected:
417
+ self._raise(
418
+ ValueError,
419
+ 'Must be replacing',
420
+ msg,
421
+ Checks._ArgsKwargs(expected, old, new),
422
+ render_fmt='%s -> %s -> %s',
423
+ )
424
+
425
+ return new
426
+
427
+ def replacing_none(self, old: ta.Any, new: T, msg: CheckMessage = None) -> T:
428
+ if old is not None:
429
+ self._raise(
430
+ ValueError,
431
+ 'Must be replacing None',
432
+ msg,
433
+ Checks._ArgsKwargs(old, new),
434
+ render_fmt='%s -> %s',
435
+ )
436
+
437
+ return new
438
+
439
+ #
440
+
441
+ def arg(self, v: bool, msg: CheckMessage = None) -> None:
442
+ if not v:
443
+ self._raise(
444
+ RuntimeError,
445
+ 'Argument condition not met',
446
+ msg,
447
+ Checks._ArgsKwargs(v),
448
+ render_fmt='%s',
449
+ )
450
+
451
+ def state(self, v: bool, msg: CheckMessage = None) -> None:
452
+ if not v:
453
+ self._raise(
454
+ RuntimeError,
455
+ 'State condition not met',
456
+ msg,
457
+ Checks._ArgsKwargs(v),
458
+ render_fmt='%s',
459
+ )
460
+
461
+
462
+ check = Checks()
@@ -2,8 +2,7 @@
2
2
  import contextlib
3
3
  import typing as ta
4
4
 
5
- from .check import check_not_none
6
- from .check import check_state
5
+ from .check import check
7
6
 
8
7
 
9
8
  T = ta.TypeVar('T')
@@ -17,7 +16,7 @@ class ExitStacked:
17
16
  _exit_stack: ta.Optional[contextlib.ExitStack] = None
18
17
 
19
18
  def __enter__(self: ExitStackedT) -> ExitStackedT:
20
- check_state(self._exit_stack is None)
19
+ check.state(self._exit_stack is None)
21
20
  es = self._exit_stack = contextlib.ExitStack()
22
21
  es.__enter__()
23
22
  return self
@@ -32,7 +31,7 @@ class ExitStacked:
32
31
  pass
33
32
 
34
33
  def _enter_context(self, cm: ta.ContextManager[T]) -> T:
35
- es = check_not_none(self._exit_stack)
34
+ es = check.not_none(self._exit_stack)
36
35
  return es.enter_context(cm)
37
36
 
38
37
 
omlish/lite/inject.py CHANGED
@@ -8,11 +8,7 @@ import types
8
8
  import typing as ta
9
9
  import weakref
10
10
 
11
- from .check import check_in
12
- from .check import check_isinstance
13
- from .check import check_not_in
14
- from .check import check_not_isinstance
15
- from .check import check_not_none
11
+ from .check import check
16
12
  from .maybes import Maybe
17
13
  from .reflect import get_optional_alias_arg
18
14
  from .reflect import is_new_type
@@ -170,7 +166,7 @@ class FnInjectorProvider(InjectorProvider):
170
166
  fn: ta.Any
171
167
 
172
168
  def __post_init__(self) -> None:
173
- check_not_isinstance(self.fn, type)
169
+ check.not_isinstance(self.fn, type)
174
170
 
175
171
  def provider_fn(self) -> InjectorProviderFn:
176
172
  def pfn(i: Injector) -> ta.Any:
@@ -184,7 +180,7 @@ class CtorInjectorProvider(InjectorProvider):
184
180
  cls_: type
185
181
 
186
182
  def __post_init__(self) -> None:
187
- check_isinstance(self.cls_, type)
183
+ check.isinstance(self.cls_, type)
188
184
 
189
185
  def provider_fn(self) -> InjectorProviderFn:
190
186
  def pfn(i: Injector) -> ta.Any:
@@ -206,7 +202,7 @@ class SingletonInjectorProvider(InjectorProvider):
206
202
  p: InjectorProvider
207
203
 
208
204
  def __post_init__(self) -> None:
209
- check_isinstance(self.p, InjectorProvider)
205
+ check.isinstance(self.p, InjectorProvider)
210
206
 
211
207
  def provider_fn(self) -> InjectorProviderFn:
212
208
  v = not_set = object()
@@ -226,7 +222,7 @@ class LinkInjectorProvider(InjectorProvider):
226
222
  k: InjectorKey
227
223
 
228
224
  def __post_init__(self) -> None:
229
- check_isinstance(self.k, InjectorKey)
225
+ check.isinstance(self.k, InjectorKey)
230
226
 
231
227
  def provider_fn(self) -> InjectorProviderFn:
232
228
  def pfn(i: Injector) -> ta.Any:
@@ -423,7 +419,7 @@ def build_injection_kwargs_target(
423
419
 
424
420
  skip_names: ta.Set[str] = set()
425
421
  if skip_kwargs is not None:
426
- skip_names.update(check_not_isinstance(skip_kwargs, str))
422
+ skip_names.update(check.not_isinstance(skip_kwargs, str))
427
423
 
428
424
  seen: ta.Set[InjectorKey] = set()
429
425
  kws: ta.List[InjectionKwarg] = []
@@ -484,8 +480,8 @@ class _Injector(Injector):
484
480
  def __init__(self, bs: InjectorBindings, p: ta.Optional[Injector] = None) -> None:
485
481
  super().__init__()
486
482
 
487
- self._bs = check_isinstance(bs, InjectorBindings)
488
- self._p: ta.Optional[Injector] = check_isinstance(p, (Injector, type(None)))
483
+ self._bs = check.isinstance(bs, InjectorBindings)
484
+ self._p: ta.Optional[Injector] = check.isinstance(p, (Injector, type(None)))
489
485
 
490
486
  self._pfm = {k: v.provider_fn() for k, v in build_injector_provider_map(bs).items()}
491
487
 
@@ -516,8 +512,8 @@ class _Injector(Injector):
516
512
  return Maybe.empty()
517
513
 
518
514
  def handle_provision(self, key: InjectorKey, mv: Maybe) -> Maybe:
519
- check_in(key, self._seen_keys)
520
- check_not_in(key, self._provisions)
515
+ check.in_(key, self._seen_keys)
516
+ check.not_in(key, self._provisions)
521
517
  self._provisions[key] = mv
522
518
  return mv
523
519
 
@@ -631,7 +627,7 @@ class InjectorBinder:
631
627
 
632
628
  @classmethod
633
629
  def bind_as_fn(cls, icls: ta.Type[T]) -> ta.Type[T]:
634
- check_isinstance(icls, type)
630
+ check.isinstance(icls, type)
635
631
  if icls not in cls._FN_TYPES:
636
632
  cls._FN_TYPES = (*cls._FN_TYPES, icls)
637
633
  return icls
@@ -688,7 +684,7 @@ class InjectorBinder:
688
684
  to_fn = obj
689
685
  if key is None:
690
686
  insp = _injection_inspect(obj)
691
- key_cls: ta.Any = check_valid_injector_key_cls(check_not_none(insp.type_hints.get('return')))
687
+ key_cls: ta.Any = check_valid_injector_key_cls(check.not_none(insp.type_hints.get('return')))
692
688
  key = InjectorKey(key_cls)
693
689
  else:
694
690
  if to_const is not None: