omlish 0.0.0.dev120__py3-none-any.whl → 0.0.0.dev121__py3-none-any.whl

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