omlish 0.0.0.dev120__py3-none-any.whl → 0.0.0.dev121__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/__about__.py CHANGED
@@ -1,5 +1,5 @@
1
- __version__ = '0.0.0.dev120'
2
- __revision__ = 'aada6beb1d685cb095429e9c588d266b8a83aa23'
1
+ __version__ = '0.0.0.dev121'
2
+ __revision__ = '8d604587a14f8faa089e1a5d3ff68756b93b0c1f'
3
3
 
4
4
 
5
5
  #
omlish/inject/binder.py CHANGED
@@ -74,7 +74,7 @@ def bind_as_fn(cls: type[T]) -> type[T]:
74
74
  ##
75
75
 
76
76
 
77
- _BANNED_BIND_TYPES = (
77
+ _BANNED_BIND_TYPES: tuple[type, ...] = (
78
78
  Element,
79
79
  Provider,
80
80
  Elements,
@@ -7,7 +7,6 @@ TODO:
7
7
  """
8
8
  import dataclasses as dc
9
9
  import inspect
10
- import types
11
10
  import typing as ta
12
11
  import weakref
13
12
 
@@ -28,38 +27,38 @@ R = ta.TypeVar('R')
28
27
  ##
29
28
 
30
29
 
31
- _signature_cache: ta.MutableMapping[ta.Any, inspect.Signature] = weakref.WeakKeyDictionary()
30
+ _SIGNATURE_CACHE: ta.MutableMapping[ta.Any, inspect.Signature] = weakref.WeakKeyDictionary()
32
31
 
33
32
 
34
33
  def signature(obj: ta.Any) -> inspect.Signature:
35
34
  try:
36
- return _signature_cache[obj]
35
+ return _SIGNATURE_CACHE[obj]
37
36
  except TypeError:
38
37
  return inspect.signature(obj)
39
38
  except KeyError:
40
39
  pass
41
40
  sig = inspect.signature(obj)
42
- _signature_cache[obj] = sig
41
+ _SIGNATURE_CACHE[obj] = sig
43
42
  return sig
44
43
 
45
44
 
46
45
  ##
47
46
 
48
47
 
49
- _tags: ta.MutableMapping[ta.Any, dict[str, ta.Any]] = weakref.WeakKeyDictionary()
48
+ _TAGS: ta.MutableMapping[ta.Any, dict[str, ta.Any]] = weakref.WeakKeyDictionary()
50
49
 
51
50
 
52
51
  def tag(obj: T, **kwargs: ta.Any) -> T:
53
52
  for v in kwargs.values():
54
53
  if isinstance(v, Tag):
55
54
  raise TypeError(v)
56
- _tags.setdefault(obj, {}).update(**kwargs)
55
+ _TAGS.setdefault(obj, {}).update(**kwargs)
57
56
  return obj
58
57
 
59
58
 
60
59
  def tags(**kwargs: ta.Any) -> ta.Callable[[ta.Callable[P, R]], ta.Callable[P, R]]:
61
60
  def inner(obj):
62
- _tags[obj] = kwargs
61
+ _TAGS[obj] = kwargs
63
62
  return obj
64
63
  return inner
65
64
 
@@ -75,7 +74,7 @@ def build_kwargs_target(
75
74
  raw_optional: bool = False,
76
75
  ) -> KwargsTarget:
77
76
  sig = signature(obj)
78
- tags = _tags.get(obj)
77
+ tags = _TAGS.get(obj)
79
78
 
80
79
  seen: set[Key] = set(map(as_key, skip_kwargs)) if skip_kwargs is not None else set()
81
80
  kws: list[Kwarg] = []
@@ -92,10 +91,9 @@ def build_kwargs_target(
92
91
  if (
93
92
  not raw_optional and
94
93
  isinstance(rf := rfl.type_(ann), rfl.Union) and
95
- len(rf.args) == 2 # noqa
96
- and types.NoneType in rf.args
94
+ rf.is_optional
97
95
  ):
98
- [ann] = [a for a in rf.args if a is not types.NoneType]
96
+ ann = rf.without_none()
99
97
 
100
98
  rty = rfl.type_(ann)
101
99
 
omlish/lite/cached.py CHANGED
@@ -22,5 +22,5 @@ class _cached_nullary: # noqa
22
22
  return bound
23
23
 
24
24
 
25
- def cached_nullary(fn: ta.Callable[..., T]) -> ta.Callable[..., T]:
25
+ def cached_nullary(fn): # ta.Callable[..., T]) -> ta.Callable[..., T]:
26
26
  return _cached_nullary(fn)
@@ -25,8 +25,12 @@ class ExitStacked:
25
25
  def __exit__(self, exc_type, exc_val, exc_tb):
26
26
  if (es := self._exit_stack) is None:
27
27
  return None
28
+ self._exit_contexts()
28
29
  return es.__exit__(exc_type, exc_val, exc_tb)
29
30
 
31
+ def _exit_contexts(self) -> None:
32
+ pass
33
+
30
34
  def _enter_context(self, cm: ta.ContextManager[T]) -> T:
31
35
  es = check_not_none(self._exit_stack)
32
36
  return es.enter_context(cm)
omlish/lite/inject.py ADDED
@@ -0,0 +1,624 @@
1
+ # ruff: noqa: UP006 UP007
2
+ import abc
3
+ import dataclasses as dc
4
+ import functools
5
+ import inspect
6
+ import types
7
+ import typing as ta
8
+ import weakref
9
+
10
+ from .check import check_isinstance
11
+ from .check import check_not_isinstance
12
+ from .maybes import Maybe
13
+ from .reflect import get_optional_alias_arg
14
+ from .reflect import is_optional_alias
15
+
16
+
17
+ T = ta.TypeVar('T')
18
+
19
+ InjectorKeyCls = ta.Union[type, ta.NewType]
20
+
21
+ InjectorProviderFn = ta.Callable[['Injector'], ta.Any]
22
+ InjectorProviderFnMap = ta.Mapping['InjectorKey', 'InjectorProviderFn']
23
+
24
+ InjectorBindingOrBindings = ta.Union['InjectorBinding', 'InjectorBindings']
25
+
26
+
27
+ ###
28
+ # types
29
+
30
+
31
+ @dc.dataclass(frozen=True)
32
+ class InjectorKey:
33
+ cls: InjectorKeyCls
34
+ tag: ta.Any = None
35
+ array: bool = False
36
+
37
+
38
+ ##
39
+
40
+
41
+ class InjectorProvider(abc.ABC):
42
+ @abc.abstractmethod
43
+ def provider_fn(self) -> InjectorProviderFn:
44
+ raise NotImplementedError
45
+
46
+
47
+ ##
48
+
49
+
50
+ @dc.dataclass(frozen=True)
51
+ class InjectorBinding:
52
+ key: InjectorKey
53
+ provider: InjectorProvider
54
+
55
+
56
+ class InjectorBindings(abc.ABC):
57
+ @abc.abstractmethod
58
+ def bindings(self) -> ta.Iterator[InjectorBinding]:
59
+ raise NotImplementedError
60
+
61
+ ##
62
+
63
+
64
+ class Injector(abc.ABC):
65
+ @abc.abstractmethod
66
+ def try_provide(self, key: ta.Any) -> Maybe[ta.Any]:
67
+ raise NotImplementedError
68
+
69
+ @abc.abstractmethod
70
+ def provide(self, key: ta.Any) -> ta.Any:
71
+ raise NotImplementedError
72
+
73
+ @abc.abstractmethod
74
+ def provide_kwargs(self, obj: ta.Any) -> ta.Mapping[str, ta.Any]:
75
+ raise NotImplementedError
76
+
77
+ @abc.abstractmethod
78
+ def inject(self, obj: ta.Any) -> ta.Any:
79
+ raise NotImplementedError
80
+
81
+
82
+ ###
83
+ # exceptions
84
+
85
+
86
+ @dc.dataclass(frozen=True)
87
+ class InjectorKeyError(Exception):
88
+ key: InjectorKey
89
+
90
+ source: ta.Any = None
91
+ name: ta.Optional[str] = None
92
+
93
+
94
+ @dc.dataclass(frozen=True)
95
+ class UnboundInjectorKeyError(InjectorKeyError):
96
+ pass
97
+
98
+
99
+ @dc.dataclass(frozen=True)
100
+ class DuplicateInjectorKeyError(InjectorKeyError):
101
+ pass
102
+
103
+
104
+ ###
105
+ # keys
106
+
107
+
108
+ def as_injector_key(o: ta.Any) -> InjectorKey:
109
+ if o is inspect.Parameter.empty:
110
+ raise TypeError(o)
111
+ if isinstance(o, InjectorKey):
112
+ return o
113
+ if isinstance(o, (type, ta.NewType)):
114
+ return InjectorKey(o)
115
+ raise TypeError(o)
116
+
117
+
118
+ ###
119
+ # providers
120
+
121
+
122
+ @dc.dataclass(frozen=True)
123
+ class FnInjectorProvider(InjectorProvider):
124
+ fn: ta.Any
125
+
126
+ def __post_init__(self) -> None:
127
+ check_not_isinstance(self.fn, type)
128
+
129
+ def provider_fn(self) -> InjectorProviderFn:
130
+ def pfn(i: Injector) -> ta.Any:
131
+ return i.inject(self.fn)
132
+
133
+ return pfn
134
+
135
+
136
+ @dc.dataclass(frozen=True)
137
+ class CtorInjectorProvider(InjectorProvider):
138
+ cls: type
139
+
140
+ def __post_init__(self) -> None:
141
+ check_isinstance(self.cls, type)
142
+
143
+ def provider_fn(self) -> InjectorProviderFn:
144
+ def pfn(i: Injector) -> ta.Any:
145
+ return i.inject(self.cls)
146
+
147
+ return pfn
148
+
149
+
150
+ @dc.dataclass(frozen=True)
151
+ class ConstInjectorProvider(InjectorProvider):
152
+ v: ta.Any
153
+
154
+ def provider_fn(self) -> InjectorProviderFn:
155
+ return lambda _: self.v
156
+
157
+
158
+ @dc.dataclass(frozen=True)
159
+ class SingletonInjectorProvider(InjectorProvider):
160
+ p: InjectorProvider
161
+
162
+ def __post_init__(self) -> None:
163
+ check_isinstance(self.p, InjectorProvider)
164
+
165
+ def provider_fn(self) -> InjectorProviderFn:
166
+ v = not_set = object()
167
+
168
+ def pfn(i: Injector) -> ta.Any:
169
+ nonlocal v
170
+ if v is not_set:
171
+ v = ufn(i)
172
+ return v
173
+
174
+ ufn = self.p.provider_fn()
175
+ return pfn
176
+
177
+
178
+ @dc.dataclass(frozen=True)
179
+ class LinkInjectorProvider(InjectorProvider):
180
+ k: InjectorKey
181
+
182
+ def __post_init__(self) -> None:
183
+ check_isinstance(self.k, InjectorKey)
184
+
185
+ def provider_fn(self) -> InjectorProviderFn:
186
+ def pfn(i: Injector) -> ta.Any:
187
+ return i.provide(self.k)
188
+
189
+ return pfn
190
+
191
+
192
+ @dc.dataclass(frozen=True)
193
+ class ArrayInjectorProvider(InjectorProvider):
194
+ ps: ta.Sequence[InjectorProvider]
195
+
196
+ def provider_fn(self) -> InjectorProviderFn:
197
+ ps = [p.provider_fn() for p in self.ps]
198
+
199
+ def pfn(i: Injector) -> ta.Any:
200
+ rv = []
201
+ for ep in ps:
202
+ o = ep(i)
203
+ rv.append(o)
204
+ return rv
205
+
206
+ return pfn
207
+
208
+
209
+ ###
210
+ # bindings
211
+
212
+
213
+ @dc.dataclass(frozen=True)
214
+ class _InjectorBindings(InjectorBindings):
215
+ bs: ta.Optional[ta.Sequence[InjectorBinding]] = None
216
+ ps: ta.Optional[ta.Sequence[InjectorBindings]] = None
217
+
218
+ def bindings(self) -> ta.Iterator[InjectorBinding]:
219
+ if self.bs is not None:
220
+ yield from self.bs
221
+ if self.ps is not None:
222
+ for p in self.ps:
223
+ yield from p.bindings()
224
+
225
+
226
+ def as_injector_bindings(*args: InjectorBindingOrBindings) -> InjectorBindings:
227
+ bs: ta.List[InjectorBinding] = []
228
+ ps: ta.List[InjectorBindings] = []
229
+ for a in args:
230
+ if isinstance(a, InjectorBindings):
231
+ ps.append(a)
232
+ elif isinstance(a, InjectorBinding):
233
+ bs.append(a)
234
+ else:
235
+ raise TypeError(a)
236
+ return _InjectorBindings(
237
+ bs or None,
238
+ ps or None,
239
+ )
240
+
241
+
242
+ ##
243
+
244
+
245
+ @dc.dataclass(frozen=True)
246
+ class OverridesInjectorBindings(InjectorBindings):
247
+ p: InjectorBindings
248
+ m: ta.Mapping[InjectorKey, InjectorBinding]
249
+
250
+ def bindings(self) -> ta.Iterator[InjectorBinding]:
251
+ for b in self.p.bindings():
252
+ yield self.m.get(b.key, b)
253
+
254
+
255
+ def injector_override(p: InjectorBindings, *args: InjectorBindingOrBindings) -> InjectorBindings:
256
+ m: ta.Dict[InjectorKey, InjectorBinding] = {}
257
+ for b in as_injector_bindings(*args).bindings():
258
+ if b.key in m:
259
+ raise DuplicateInjectorKeyError(b.key)
260
+ m[b.key] = b
261
+ return OverridesInjectorBindings(p, m)
262
+
263
+
264
+ ##
265
+
266
+
267
+ def build_injector_provider_map(bs: InjectorBindings) -> ta.Mapping[InjectorKey, InjectorProvider]:
268
+ pm: ta.Dict[InjectorKey, InjectorProvider] = {}
269
+ am: ta.Dict[InjectorKey, ta.List[InjectorProvider]] = {}
270
+
271
+ for b in bs.bindings():
272
+ if b.key.array:
273
+ am.setdefault(b.key, []).append(b.provider)
274
+ else:
275
+ if b.key in pm:
276
+ raise KeyError(b.key)
277
+ pm[b.key] = b.provider
278
+
279
+ if am:
280
+ for k, aps in am.items():
281
+ pm[k] = ArrayInjectorProvider(aps)
282
+
283
+ return pm
284
+
285
+
286
+ ###
287
+ # inspection
288
+
289
+
290
+ _INJECTION_SIGNATURE_CACHE: ta.MutableMapping[ta.Any, inspect.Signature] = weakref.WeakKeyDictionary()
291
+
292
+
293
+ def _injection_signature(obj: ta.Any) -> inspect.Signature:
294
+ try:
295
+ return _INJECTION_SIGNATURE_CACHE[obj]
296
+ except TypeError:
297
+ return inspect.signature(obj)
298
+ except KeyError:
299
+ pass
300
+ sig = inspect.signature(obj)
301
+ _INJECTION_SIGNATURE_CACHE[obj] = sig
302
+ return sig
303
+
304
+
305
+ class InjectionKwarg(ta.NamedTuple):
306
+ name: str
307
+ key: InjectorKey
308
+ has_default: bool
309
+
310
+
311
+ class InjectionKwargsTarget(ta.NamedTuple):
312
+ obj: ta.Any
313
+ kwargs: ta.Sequence[InjectionKwarg]
314
+
315
+
316
+ def build_injection_kwargs_target(
317
+ obj: ta.Any,
318
+ *,
319
+ skip_args: int = 0,
320
+ skip_kwargs: ta.Optional[ta.Iterable[ta.Any]] = None,
321
+ raw_optional: bool = False,
322
+ ) -> InjectionKwargsTarget:
323
+ sig = _injection_signature(obj)
324
+
325
+ seen: ta.Set[InjectorKey] = set(map(as_injector_key, skip_kwargs)) if skip_kwargs is not None else set()
326
+ kws: ta.List[InjectionKwarg] = []
327
+ for p in list(sig.parameters.values())[skip_args:]:
328
+ if p.annotation is inspect.Signature.empty:
329
+ if p.default is not inspect.Parameter.empty:
330
+ raise KeyError(f'{obj}, {p.name}')
331
+ continue
332
+
333
+ if p.kind not in (inspect.Parameter.POSITIONAL_OR_KEYWORD, inspect.Parameter.KEYWORD_ONLY):
334
+ raise TypeError(sig)
335
+
336
+ ann = p.annotation
337
+ if (
338
+ not raw_optional and
339
+ is_optional_alias(ann)
340
+ ):
341
+ ann = get_optional_alias_arg(ann)
342
+
343
+ k = as_injector_key(ann)
344
+
345
+ if k in seen:
346
+ raise DuplicateInjectorKeyError(k)
347
+ seen.add(k)
348
+
349
+ kws.append(InjectionKwarg(
350
+ p.name,
351
+ k,
352
+ p.default is not inspect.Parameter.empty,
353
+ ))
354
+
355
+ return InjectionKwargsTarget(
356
+ obj,
357
+ kws,
358
+ )
359
+
360
+
361
+ ###
362
+ # binder
363
+
364
+
365
+ class InjectorBinder:
366
+ def __new__(cls, *args, **kwargs): # noqa
367
+ raise TypeError
368
+
369
+ _FN_TYPES: ta.Tuple[type, ...] = (
370
+ types.FunctionType,
371
+ types.MethodType,
372
+
373
+ classmethod,
374
+ staticmethod,
375
+
376
+ functools.partial,
377
+ functools.partialmethod,
378
+ )
379
+
380
+ @classmethod
381
+ def _is_fn(cls, obj: ta.Any) -> bool:
382
+ return isinstance(obj, cls._FN_TYPES)
383
+
384
+ @classmethod
385
+ def bind_as_fn(cls, icls: ta.Type[T]) -> ta.Type[T]:
386
+ check_isinstance(icls, type)
387
+ if icls not in cls._FN_TYPES:
388
+ cls._FN_TYPES = (*cls._FN_TYPES, icls)
389
+ return icls
390
+
391
+ _BANNED_BIND_TYPES: ta.Tuple[type, ...] = (
392
+ InjectorProvider,
393
+ )
394
+
395
+ @classmethod
396
+ def bind(
397
+ cls,
398
+ obj: ta.Any,
399
+ *,
400
+ key: ta.Any = None,
401
+ tag: ta.Any = None,
402
+ array: ta.Optional[bool] = None, # noqa
403
+
404
+ to_fn: ta.Any = None,
405
+ to_ctor: ta.Any = None,
406
+ to_const: ta.Any = None,
407
+ to_key: ta.Any = None,
408
+
409
+ singleton: bool = False,
410
+ ) -> InjectorBinding:
411
+ if obj is None or obj is inspect.Parameter.empty:
412
+ raise TypeError(obj)
413
+ if isinstance(obj, cls._BANNED_BIND_TYPES):
414
+ raise TypeError(obj)
415
+
416
+ ##
417
+
418
+ if key is not None:
419
+ key = as_injector_key(key)
420
+
421
+ ##
422
+
423
+ has_to = (
424
+ to_fn is not None or
425
+ to_ctor is not None or
426
+ to_const is not None or
427
+ to_key is not None
428
+ )
429
+ if isinstance(obj, InjectorKey):
430
+ if key is None:
431
+ key = obj
432
+ elif isinstance(obj, type):
433
+ if not has_to:
434
+ to_ctor = obj
435
+ if key is None:
436
+ key = InjectorKey(obj)
437
+ elif cls._is_fn(obj) and not has_to:
438
+ to_fn = obj
439
+ if key is None:
440
+ sig = _injection_signature(obj)
441
+ ty = check_isinstance(sig.return_annotation, type)
442
+ key = InjectorKey(ty)
443
+ else:
444
+ if to_const is not None:
445
+ raise TypeError('Cannot bind instance with to_const')
446
+ to_const = obj
447
+ if key is None:
448
+ key = InjectorKey(type(obj))
449
+ del has_to
450
+
451
+ ##
452
+
453
+ if tag is not None:
454
+ if key.tag is not None:
455
+ raise TypeError('Tag already set')
456
+ key = dc.replace(key, tag=tag)
457
+
458
+ if array is not None:
459
+ key = dc.replace(key, array=array)
460
+
461
+ ##
462
+
463
+ providers: ta.List[InjectorProvider] = []
464
+ if to_fn is not None:
465
+ providers.append(FnInjectorProvider(to_fn))
466
+ if to_ctor is not None:
467
+ providers.append(CtorInjectorProvider(to_ctor))
468
+ if to_const is not None:
469
+ providers.append(ConstInjectorProvider(to_const))
470
+ if to_key is not None:
471
+ providers.append(LinkInjectorProvider(as_injector_key(to_key)))
472
+ if not providers:
473
+ raise TypeError('Must specify provider')
474
+ if len(providers) > 1:
475
+ raise TypeError('May not specify multiple providers')
476
+ provider, = providers
477
+
478
+ ##
479
+
480
+ if singleton:
481
+ provider = SingletonInjectorProvider(provider)
482
+
483
+ ##
484
+
485
+ binding = InjectorBinding(key, provider)
486
+
487
+ ##
488
+
489
+ return binding
490
+
491
+
492
+ ###
493
+ # injector
494
+
495
+
496
+ _INJECTOR_INJECTOR_KEY = InjectorKey(Injector)
497
+
498
+
499
+ class _Injector(Injector):
500
+ def __init__(self, bs: InjectorBindings, p: ta.Optional[Injector] = None) -> None:
501
+ super().__init__()
502
+
503
+ self._bs = check_isinstance(bs, InjectorBindings)
504
+ self._p: ta.Optional[Injector] = check_isinstance(p, (Injector, type(None)))
505
+
506
+ self._pfm = {k: v.provider_fn() for k, v in build_injector_provider_map(bs).items()}
507
+
508
+ if _INJECTOR_INJECTOR_KEY in self._pfm:
509
+ raise DuplicateInjectorKeyError(_INJECTOR_INJECTOR_KEY)
510
+
511
+ def try_provide(self, key: ta.Any) -> Maybe[ta.Any]:
512
+ key = as_injector_key(key)
513
+
514
+ if key == _INJECTOR_INJECTOR_KEY:
515
+ return Maybe.just(self)
516
+
517
+ fn = self._pfm.get(key)
518
+ if fn is not None:
519
+ return Maybe.just(fn(self))
520
+
521
+ if self._p is not None:
522
+ pv = self._p.try_provide(key)
523
+ if pv is not None:
524
+ return Maybe.empty()
525
+
526
+ return Maybe.empty()
527
+
528
+ def provide(self, key: ta.Any) -> ta.Any:
529
+ v = self.try_provide(key)
530
+ if v.present:
531
+ return v.must()
532
+ raise UnboundInjectorKeyError(key)
533
+
534
+ def provide_kwargs(self, obj: ta.Any) -> ta.Mapping[str, ta.Any]:
535
+ kt = build_injection_kwargs_target(obj)
536
+ ret: ta.Dict[str, ta.Any] = {}
537
+ for kw in kt.kwargs:
538
+ if kw.has_default:
539
+ if not (mv := self.try_provide(kw.key)).present:
540
+ continue
541
+ v = mv.must()
542
+ else:
543
+ v = self.provide(kw.key)
544
+ ret[kw.name] = v
545
+ return ret
546
+
547
+ def inject(self, obj: ta.Any) -> ta.Any:
548
+ kws = self.provide_kwargs(obj)
549
+ return obj(**kws)
550
+
551
+
552
+ ###
553
+ # injection helpers
554
+
555
+
556
+ class Injection:
557
+ def __new__(cls, *args, **kwargs): # noqa
558
+ raise TypeError
559
+
560
+ # keys
561
+
562
+ @classmethod
563
+ def as_key(cls, o: ta.Any) -> InjectorKey:
564
+ return as_injector_key(o)
565
+
566
+ @classmethod
567
+ def array(cls, o: ta.Any) -> InjectorKey:
568
+ return dc.replace(as_injector_key(o), array=True)
569
+
570
+ @classmethod
571
+ def tag(cls, o: ta.Any, t: ta.Any) -> InjectorKey:
572
+ return dc.replace(as_injector_key(o), tag=t)
573
+
574
+ # bindings
575
+
576
+ @classmethod
577
+ def as_bindings(cls, *args: InjectorBindingOrBindings) -> InjectorBindings:
578
+ return as_injector_bindings(*args)
579
+
580
+ @classmethod
581
+ def override(cls, p: InjectorBindings, *args: InjectorBindingOrBindings) -> InjectorBindings:
582
+ return injector_override(p, *args)
583
+
584
+ # binder
585
+
586
+ @classmethod
587
+ def bind(
588
+ cls,
589
+ obj: ta.Any,
590
+ *,
591
+ key: ta.Any = None,
592
+ tag: ta.Any = None,
593
+ array: ta.Optional[bool] = None, # noqa
594
+
595
+ to_fn: ta.Any = None,
596
+ to_ctor: ta.Any = None,
597
+ to_const: ta.Any = None,
598
+ to_key: ta.Any = None,
599
+
600
+ singleton: bool = False,
601
+ ) -> InjectorBinding:
602
+ return InjectorBinder.bind(
603
+ obj,
604
+
605
+ key=key,
606
+ tag=tag,
607
+ array=array,
608
+
609
+ to_fn=to_fn,
610
+ to_ctor=to_ctor,
611
+ to_const=to_const,
612
+ to_key=to_key,
613
+
614
+ singleton=singleton,
615
+ )
616
+
617
+ # injector
618
+
619
+ @classmethod
620
+ def create_injector(cls, *args: InjectorBindingOrBindings, p: ta.Optional[Injector] = None) -> Injector:
621
+ return _Injector(as_injector_bindings(*args), p)
622
+
623
+
624
+ inj = Injection
omlish/lite/maybes.py ADDED
@@ -0,0 +1,45 @@
1
+ import abc
2
+ import typing as ta
3
+
4
+
5
+ T = ta.TypeVar('T')
6
+
7
+
8
+ class Maybe(ta.Generic[T]):
9
+ @property
10
+ @abc.abstractmethod
11
+ def present(self) -> bool:
12
+ raise NotImplementedError
13
+
14
+ @abc.abstractmethod
15
+ def must(self) -> T:
16
+ raise NotImplementedError
17
+
18
+ @classmethod
19
+ def just(cls, v: T) -> 'Maybe[T]':
20
+ return tuple.__new__(_Maybe, (v,)) # noqa
21
+
22
+ _empty: ta.ClassVar['Maybe']
23
+
24
+ @classmethod
25
+ def empty(cls) -> 'Maybe[T]':
26
+ return Maybe._empty
27
+
28
+
29
+ class _Maybe(Maybe[T], tuple):
30
+ __slots__ = ()
31
+
32
+ def __init_subclass__(cls, **kwargs):
33
+ raise TypeError
34
+
35
+ @property
36
+ def present(self) -> bool:
37
+ return bool(self)
38
+
39
+ def must(self) -> T:
40
+ if not self:
41
+ raise ValueError
42
+ return self[0]
43
+
44
+
45
+ Maybe._empty = tuple.__new__(_Maybe, ()) # noqa
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: omlish
3
- Version: 0.0.0.dev120
3
+ Version: 0.0.0.dev121
4
4
  Summary: omlish
5
5
  Author: wrmsr
6
6
  License: BSD-3-Clause
@@ -1,5 +1,5 @@
1
1
  omlish/.manifests.json,sha256=CxGnj-UiRPlZgmgWoovDWrOnqpSEmBy_kqA7cdfSA3w,1431
2
- omlish/__about__.py,sha256=JGKddDla0hzssCiRfhxitgG_mY-VKslBVWxhzDUU-Fg,3352
2
+ omlish/__about__.py,sha256=aTRzdXeouYEQLsJZyhmeZxxg3aaQgoLMDv_T3jZ_HZE,3352
3
3
  omlish/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
4
4
  omlish/argparse.py,sha256=cqKGAqcxuxv_s62z0gq29L9KAvg_3-_rFvXKjVpRJjo,8126
5
5
  omlish/c3.py,sha256=4vogWgwPb8TbNS2KkZxpoWbwjj7MuHG2lQG-hdtkvjI,8062
@@ -232,7 +232,7 @@ omlish/http/sessions.py,sha256=VZ_WS5uiQG5y7i3u8oKuQMqf8dPKUOjFm_qk_0OvI8c,4793
232
232
  omlish/http/sse.py,sha256=MDs9RvxQXoQliImcc6qK1ERajEYM7Q1l8xmr-9ceNBc,2315
233
233
  omlish/http/wsgi.py,sha256=czZsVUX-l2YTlMrUjKN49wRoP4rVpS0qpeBn4O5BoMY,948
234
234
  omlish/inject/__init__.py,sha256=JQ7x8l9MjU-kJ5ap7cPVq7SY7zbbCIrjyJAF0UeE5-s,1886
235
- omlish/inject/binder.py,sha256=H8AQ4ecmBOtDL8fMgrU1yUJl1gBADLNcdysRbvO8Wso,4167
235
+ omlish/inject/binder.py,sha256=DAbc8TZi5w8Mna0TUtq0mT4jeDVA7i7SlBtOFrh2swc,4185
236
236
  omlish/inject/bindings.py,sha256=pLXn2U3kvmAS-68IOG-tr77DbiI-wp9hGyy4lhG6_H8,525
237
237
  omlish/inject/eagers.py,sha256=5AkGYuwijG0ihsH9NSaZotggalJ5_xWXhHE9mkn6IBA,329
238
238
  omlish/inject/elements.py,sha256=BzTnkNS-3iAMI47LMC2543u6A8Tfk3aJXn3CO191ez4,1547
@@ -254,7 +254,7 @@ omlish/inject/impl/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuF
254
254
  omlish/inject/impl/bindings.py,sha256=8H586RCgmvwq53XBL9WMbb-1-Tdw_hh9zxIDCwcHA1c,414
255
255
  omlish/inject/impl/elements.py,sha256=bJBbHce_eZyIua2wbcejMwd9Uv-QeYcQ-c5N1qOXSmU,5950
256
256
  omlish/inject/impl/injector.py,sha256=WxIVOF6zvipb44302_j-xQZt8vnNCY0gdLX6UWzslXI,7550
257
- omlish/inject/impl/inspect.py,sha256=erLL1w6GzoPVGwORm56ra9vR-IH5BoMQrr7POwaFDjU,3050
257
+ omlish/inject/impl/inspect.py,sha256=qtHAOo7MvM7atWgxApYXZ0Dmw5zoP45ErPH3mnx9Kvk,2947
258
258
  omlish/inject/impl/multis.py,sha256=rRIWNCiTGaSWQUz_jxEy8LUmzdJDAlG94sLHYDS-ncg,2048
259
259
  omlish/inject/impl/origins.py,sha256=-cdcwz3BWb5LuC9Yn5ynYOwyPsKH06-kCc-3U0PxZ5w,1640
260
260
  omlish/inject/impl/privates.py,sha256=alpCYyk5VJ9lJknbRH2nLVNFYVvFhkj-VC1Vco3zCFQ,2613
@@ -298,15 +298,17 @@ omlish/lifecycles/manager.py,sha256=Au66KaO-fI-SEJALaPUJsCHYW2GE20xextk1wKn2BEU,
298
298
  omlish/lifecycles/states.py,sha256=zqMOU2ZU-MDNnWuwauM3_anIAiXM8LoBDElDEraptFg,1292
299
299
  omlish/lifecycles/transitions.py,sha256=qQtFby-h4VzbvgaUqT2NnbNumlcOx9FVVADP9t83xj4,1939
300
300
  omlish/lite/__init__.py,sha256=Y3l4WY4JRi2uLG6kgbGp93fuGfkxkKwZDvhsa0Rwgtk,15
301
- omlish/lite/cached.py,sha256=2Yuoi1edt2nnq3RxF3xDS40RZABmHrnommwv8iGEYQE,690
301
+ omlish/lite/cached.py,sha256=Fs-ljXVJmHBjAaHc-JuJXMEV4MNSX5c_KHZIM3AEaIw,694
302
302
  omlish/lite/check.py,sha256=ouJme9tkzWXKymm_xZDK4mngdYSkxDrok6CSegvf-1w,1015
303
- omlish/lite/contextmanagers.py,sha256=_n6a9xhn06BD8H6A_SDtcipMrSBpzBqcxI0Ob2juomM,1226
303
+ omlish/lite/contextmanagers.py,sha256=_jfNdpYvxkbKwyjQLbK-o69W89GoEuUfl_NrCosE9lU,1308
304
304
  omlish/lite/docker.py,sha256=3IVZZtIm7-UdB2SwArmN_MosTva1_KifyYp3YWjODbE,337
305
+ omlish/lite/inject.py,sha256=O3k8EYRKRKTkqoamM7awbcBvnYwy_fKIUPVf4FcObyg,15060
305
306
  omlish/lite/io.py,sha256=lcpI1cS_Kn90tvYMg8ZWkSlYloS4RFqXCk-rKyclhdg,3148
306
307
  omlish/lite/journald.py,sha256=3nfahFbTrdrfp9txhtto6JYAyrus2kcAFtviqdm3qAo,3949
307
308
  omlish/lite/json.py,sha256=7-02Ny4fq-6YAu5ynvqoijhuYXWpLmfCI19GUeZnb1c,740
308
309
  omlish/lite/logs.py,sha256=tw7mbQslkyo9LopfgQnj0tYiqJ9TDNiw7D07aF7Dm2g,6176
309
310
  omlish/lite/marshal.py,sha256=SyYMsJ-TaGO9FX7LykBB0WtqsqetX9eLBotPiz3M_xg,9478
311
+ omlish/lite/maybes.py,sha256=7OlHJ8Q2r4wQ-aRbZSlJY7x0e8gDvufFdlohGEIJ3P4,833
310
312
  omlish/lite/pidfile.py,sha256=PRSDOAXmNkNwxh-Vwif0Nrs8RAmWroiNhLKIbdjwzBc,1723
311
313
  omlish/lite/reflect.py,sha256=9QYJwdINraq1JNMEgvoqeSlVvRRgOXpxAkpgX8EgRXc,1307
312
314
  omlish/lite/runtime.py,sha256=VUhmNQvwf8QzkWSKj4Q0ReieJA_PzHaJNRBivfTseow,452
@@ -471,9 +473,9 @@ omlish/text/glyphsplit.py,sha256=Ug-dPRO7x-OrNNr8g1y6DotSZ2KH0S-VcOmUobwa4B0,329
471
473
  omlish/text/indent.py,sha256=6Jj6TFY9unaPa4xPzrnZemJ-fHsV53IamP93XGjSUHs,1274
472
474
  omlish/text/parts.py,sha256=7vPF1aTZdvLVYJ4EwBZVzRSy8XB3YqPd7JwEnNGGAOo,6495
473
475
  omlish/text/random.py,sha256=jNWpqiaKjKyTdMXC-pWAsSC10AAP-cmRRPVhm59ZWLk,194
474
- omlish-0.0.0.dev120.dist-info/LICENSE,sha256=B_hVtavaA8zCYDW99DYdcpDLKz1n3BBRjZrcbv8uG8c,1451
475
- omlish-0.0.0.dev120.dist-info/METADATA,sha256=9KUQ86p-Od1hMU_EibbqCHSW6pojpLGYyvZCxg935BE,4000
476
- omlish-0.0.0.dev120.dist-info/WHEEL,sha256=R06PA3UVYHThwHvxuRWMqaGcr-PuniXahwjmQRFMEkY,91
477
- omlish-0.0.0.dev120.dist-info/entry_points.txt,sha256=Lt84WvRZJskWCAS7xnQGZIeVWksprtUHj0llrvVmod8,35
478
- omlish-0.0.0.dev120.dist-info/top_level.txt,sha256=pePsKdLu7DvtUiecdYXJ78iO80uDNmBlqe-8hOzOmfs,7
479
- omlish-0.0.0.dev120.dist-info/RECORD,,
476
+ omlish-0.0.0.dev121.dist-info/LICENSE,sha256=B_hVtavaA8zCYDW99DYdcpDLKz1n3BBRjZrcbv8uG8c,1451
477
+ omlish-0.0.0.dev121.dist-info/METADATA,sha256=a5C2OhlCjHtn2VNKTUvzGAslZqvU4XAYaOvBfNd1884,4000
478
+ omlish-0.0.0.dev121.dist-info/WHEEL,sha256=R06PA3UVYHThwHvxuRWMqaGcr-PuniXahwjmQRFMEkY,91
479
+ omlish-0.0.0.dev121.dist-info/entry_points.txt,sha256=Lt84WvRZJskWCAS7xnQGZIeVWksprtUHj0llrvVmod8,35
480
+ omlish-0.0.0.dev121.dist-info/top_level.txt,sha256=pePsKdLu7DvtUiecdYXJ78iO80uDNmBlqe-8hOzOmfs,7
481
+ omlish-0.0.0.dev121.dist-info/RECORD,,