omlish 0.0.0.dev149__py3-none-any.whl → 0.0.0.dev151__py3-none-any.whl

Sign up to get free protection for your applications and to get access to all the features.
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: