omlish 0.0.0.dev176__py3-none-any.whl → 0.0.0.dev178__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.dev176'
2
- __revision__ = '70fcf17d0007d56d6de7cd0e25042cd43dd0e28f'
1
+ __version__ = '0.0.0.dev178'
2
+ __revision__ = 'fd8b9c2c51797566d0d9a52a8b347895029c82f5'
3
3
 
4
4
 
5
5
  #
omlish/inject/scopes.py CHANGED
@@ -80,7 +80,7 @@ class ScopeSeededProvider(Provider):
80
80
  return self.key.ty
81
81
 
82
82
 
83
- def bind_scope_seed(ss: SeededScope, k: ta.Any) -> Element:
83
+ def bind_scope_seed(k: ta.Any, ss: SeededScope) -> Element:
84
84
  k = as_key(k)
85
85
  return Binding(k, ScopeSeededProvider(ss, k))
86
86
 
omlish/lite/inject.py CHANGED
@@ -1,6 +1,7 @@
1
1
  # ruff: noqa: UP006 UP007
2
2
  import abc
3
3
  import contextlib
4
+ import contextvars
4
5
  import dataclasses as dc
5
6
  import functools
6
7
  import inspect
@@ -70,6 +71,10 @@ class InjectorBinding:
70
71
  key: InjectorKey
71
72
  provider: InjectorProvider
72
73
 
74
+ def __post_init__(self) -> None:
75
+ check.isinstance(self.key, InjectorKey)
76
+ check.isinstance(self.provider, InjectorProvider)
77
+
73
78
 
74
79
  class InjectorBindings(abc.ABC):
75
80
  @abc.abstractmethod
@@ -333,6 +338,164 @@ def build_injector_provider_map(bs: InjectorBindings) -> ta.Mapping[InjectorKey,
333
338
  return pm
334
339
 
335
340
 
341
+ ###
342
+ # scopes
343
+
344
+
345
+ class InjectorScope(abc.ABC): # noqa
346
+ def __init__(
347
+ self,
348
+ *,
349
+ _i: Injector,
350
+ ) -> None:
351
+ check.not_in(abc.ABC, type(self).__bases__)
352
+
353
+ super().__init__()
354
+
355
+ self._i = _i
356
+
357
+ all_seeds: ta.Iterable[_InjectorScopeSeed] = self._i.provide(InjectorKey(_InjectorScopeSeed, array=True))
358
+ self._sks = {s.k for s in all_seeds if s.sc is type(self)}
359
+
360
+ #
361
+
362
+ @dc.dataclass(frozen=True)
363
+ class State:
364
+ seeds: ta.Dict[InjectorKey, ta.Any]
365
+ prvs: ta.Dict[InjectorKey, ta.Any] = dc.field(default_factory=dict)
366
+
367
+ def new_state(self, vs: ta.Mapping[InjectorKey, ta.Any]) -> State:
368
+ vs = dict(vs)
369
+ check.equal(set(vs.keys()), self._sks)
370
+ return InjectorScope.State(vs)
371
+
372
+ #
373
+
374
+ @abc.abstractmethod
375
+ def state(self) -> State:
376
+ raise NotImplementedError
377
+
378
+ @abc.abstractmethod
379
+ def enter(self, vs: ta.Mapping[InjectorKey, ta.Any]) -> ta.ContextManager[None]:
380
+ raise NotImplementedError
381
+
382
+
383
+ class ExclusiveInjectorScope(InjectorScope, abc.ABC):
384
+ _st: ta.Optional[InjectorScope.State] = None
385
+
386
+ def state(self) -> InjectorScope.State:
387
+ return check.not_none(self._st)
388
+
389
+ @contextlib.contextmanager
390
+ def enter(self, vs: ta.Mapping[InjectorKey, ta.Any]) -> ta.Iterator[None]:
391
+ check.none(self._st)
392
+ self._st = self.new_state(vs)
393
+ try:
394
+ yield
395
+ finally:
396
+ self._st = None
397
+
398
+
399
+ class ContextvarInjectorScope(InjectorScope, abc.ABC):
400
+ _cv: contextvars.ContextVar
401
+
402
+ def __init_subclass__(cls, **kwargs: ta.Any) -> None:
403
+ super().__init_subclass__(**kwargs)
404
+ check.not_in(abc.ABC, cls.__bases__)
405
+ check.state(not hasattr(cls, '_cv'))
406
+ cls._cv = contextvars.ContextVar(f'{cls.__name__}_cv')
407
+
408
+ def state(self) -> InjectorScope.State:
409
+ return self._cv.get()
410
+
411
+ @contextlib.contextmanager
412
+ def enter(self, vs: ta.Mapping[InjectorKey, ta.Any]) -> ta.Iterator[None]:
413
+ try:
414
+ self._cv.get()
415
+ except LookupError:
416
+ pass
417
+ else:
418
+ raise RuntimeError(f'Scope already entered: {self}')
419
+ st = self.new_state(vs)
420
+ tok = self._cv.set(st)
421
+ try:
422
+ yield
423
+ finally:
424
+ self._cv.reset(tok)
425
+
426
+
427
+ #
428
+
429
+
430
+ @dc.dataclass(frozen=True)
431
+ class ScopedInjectorProvider(InjectorProvider):
432
+ p: InjectorProvider
433
+ k: InjectorKey
434
+ sc: ta.Type[InjectorScope]
435
+
436
+ def __post_init__(self) -> None:
437
+ check.isinstance(self.p, InjectorProvider)
438
+ check.isinstance(self.k, InjectorKey)
439
+ check.issubclass(self.sc, InjectorScope)
440
+
441
+ def provider_fn(self) -> InjectorProviderFn:
442
+ def pfn(i: Injector) -> ta.Any:
443
+ st = i[self.sc].state()
444
+ try:
445
+ return st.prvs[self.k]
446
+ except KeyError:
447
+ pass
448
+ v = ufn(i)
449
+ st.prvs[self.k] = v
450
+ return v
451
+
452
+ ufn = self.p.provider_fn()
453
+ return pfn
454
+
455
+
456
+ @dc.dataclass(frozen=True)
457
+ class _ScopeSeedInjectorProvider(InjectorProvider):
458
+ k: InjectorKey
459
+ sc: ta.Type[InjectorScope]
460
+
461
+ def __post_init__(self) -> None:
462
+ check.isinstance(self.k, InjectorKey)
463
+ check.issubclass(self.sc, InjectorScope)
464
+
465
+ def provider_fn(self) -> InjectorProviderFn:
466
+ def pfn(i: Injector) -> ta.Any:
467
+ st = i[self.sc].state()
468
+ return st.seeds[self.k]
469
+ return pfn
470
+
471
+
472
+ def bind_injector_scope(sc: ta.Type[InjectorScope]) -> InjectorBindingOrBindings:
473
+ return as_injector_bindings(
474
+ InjectorBinder.bind(sc, singleton=True),
475
+ )
476
+
477
+
478
+ #
479
+
480
+
481
+ @dc.dataclass(frozen=True)
482
+ class _InjectorScopeSeed:
483
+ sc: ta.Type['InjectorScope']
484
+ k: InjectorKey
485
+
486
+ def __post_init__(self) -> None:
487
+ check.issubclass(self.sc, InjectorScope)
488
+ check.isinstance(self.k, InjectorKey)
489
+
490
+
491
+ def bind_injector_scope_seed(k: ta.Any, sc: ta.Type[InjectorScope]) -> InjectorBindingOrBindings:
492
+ kk = as_injector_key(k)
493
+ return as_injector_bindings(
494
+ InjectorBinding(kk, _ScopeSeedInjectorProvider(kk, sc)),
495
+ InjectorBinder.bind(_InjectorScopeSeed(sc, kk), array=True),
496
+ )
497
+
498
+
336
499
  ###
337
500
  # inspection
338
501
 
@@ -477,13 +640,21 @@ _INJECTOR_EAGER_ARRAY_KEY: InjectorKey[_InjectorEager] = InjectorKey(_InjectorEa
477
640
 
478
641
 
479
642
  class _Injector(Injector):
643
+ _DEFAULT_BINDINGS: ta.ClassVar[ta.List[InjectorBinding]] = []
644
+
480
645
  def __init__(self, bs: InjectorBindings, p: ta.Optional[Injector] = None) -> None:
481
646
  super().__init__()
482
647
 
483
648
  self._bs = check.isinstance(bs, InjectorBindings)
484
649
  self._p: ta.Optional[Injector] = check.isinstance(p, (Injector, type(None)))
485
650
 
486
- self._pfm = {k: v.provider_fn() for k, v in build_injector_provider_map(bs).items()}
651
+ self._pfm = {
652
+ k: v.provider_fn()
653
+ for k, v in build_injector_provider_map(as_injector_bindings(
654
+ *self._DEFAULT_BINDINGS,
655
+ bs,
656
+ )).items()
657
+ }
487
658
 
488
659
  if _INJECTOR_INJECTOR_KEY in self._pfm:
489
660
  raise DuplicateInjectorKeyError(_INJECTOR_INJECTOR_KEY)
@@ -650,6 +821,7 @@ class InjectorBinder:
650
821
  to_const: ta.Any = None,
651
822
  to_key: ta.Any = None,
652
823
 
824
+ in_: ta.Optional[ta.Type[InjectorScope]] = None,
653
825
  singleton: bool = False,
654
826
 
655
827
  eager: bool = False,
@@ -659,12 +831,12 @@ class InjectorBinder:
659
831
  if isinstance(obj, cls._BANNED_BIND_TYPES):
660
832
  raise TypeError(obj)
661
833
 
662
- ##
834
+ #
663
835
 
664
836
  if key is not None:
665
837
  key = as_injector_key(key)
666
838
 
667
- ##
839
+ #
668
840
 
669
841
  has_to = (
670
842
  to_fn is not None or
@@ -694,7 +866,7 @@ class InjectorBinder:
694
866
  key = InjectorKey(type(obj))
695
867
  del has_to
696
868
 
697
- ##
869
+ #
698
870
 
699
871
  if tag is not None:
700
872
  if key.tag is not None:
@@ -704,7 +876,7 @@ class InjectorBinder:
704
876
  if array is not None:
705
877
  key = dc.replace(key, array=array)
706
878
 
707
- ##
879
+ #
708
880
 
709
881
  providers: ta.List[InjectorProvider] = []
710
882
  if to_fn is not None:
@@ -719,23 +891,34 @@ class InjectorBinder:
719
891
  raise TypeError('Must specify provider')
720
892
  if len(providers) > 1:
721
893
  raise TypeError('May not specify multiple providers')
722
- provider, = providers
894
+ provider = check.single(providers)
723
895
 
724
- ##
896
+ #
725
897
 
898
+ pws: ta.List[ta.Any] = []
899
+ if in_ is not None:
900
+ check.issubclass(in_, InjectorScope)
901
+ check.not_in(abc.ABC, in_.__bases__)
902
+ pws.append(functools.partial(ScopedInjectorProvider, k=key, sc=in_))
726
903
  if singleton:
727
- provider = SingletonInjectorProvider(provider)
904
+ pws.append(SingletonInjectorProvider)
905
+ if len(pws) > 1:
906
+ raise TypeError('May not specify multiple provider wrappers')
907
+ elif pws:
908
+ provider = check.single(pws)(provider)
909
+
910
+ #
728
911
 
729
912
  binding = InjectorBinding(key, provider)
730
913
 
731
- ##
914
+ #
732
915
 
733
916
  extras: ta.List[InjectorBinding] = []
734
917
 
735
918
  if eager:
736
919
  extras.append(bind_injector_eager_key(key))
737
920
 
738
- ##
921
+ #
739
922
 
740
923
  if extras:
741
924
  return as_injector_bindings(binding, *extras)
@@ -808,7 +991,8 @@ def bind_injector_eager_key(key: ta.Any) -> InjectorBinding:
808
991
  return InjectorBinding(_INJECTOR_EAGER_ARRAY_KEY, ConstInjectorProvider(_InjectorEager(as_injector_key(key))))
809
992
 
810
993
 
811
- ##
994
+ ###
995
+ # api
812
996
 
813
997
 
814
998
  class InjectionApi:
@@ -831,6 +1015,14 @@ class InjectionApi:
831
1015
  def override(self, p: InjectorBindings, *args: InjectorBindingOrBindings) -> InjectorBindings:
832
1016
  return injector_override(p, *args)
833
1017
 
1018
+ # scopes
1019
+
1020
+ def bind_scope(self, sc: ta.Type[InjectorScope]) -> InjectorBindingOrBindings:
1021
+ return bind_injector_scope(sc)
1022
+
1023
+ def bind_scope_seed(self, k: ta.Any, sc: ta.Type[InjectorScope]) -> InjectorBindingOrBindings:
1024
+ return bind_injector_scope_seed(k, sc)
1025
+
834
1026
  # injector
835
1027
 
836
1028
  def create_injector(self, *args: InjectorBindingOrBindings, parent: ta.Optional[Injector] = None) -> Injector:
@@ -851,6 +1043,7 @@ class InjectionApi:
851
1043
  to_const: ta.Any = None,
852
1044
  to_key: ta.Any = None,
853
1045
 
1046
+ in_: ta.Optional[ta.Type[InjectorScope]] = None,
854
1047
  singleton: bool = False,
855
1048
 
856
1049
  eager: bool = False,
@@ -867,6 +1060,7 @@ class InjectionApi:
867
1060
  to_const=to_const,
868
1061
  to_key=to_key,
869
1062
 
1063
+ in_=in_,
870
1064
  singleton=singleton,
871
1065
 
872
1066
  eager=eager,
@@ -54,7 +54,7 @@ class Harness:
54
54
  *[
55
55
  inj.as_elements(
56
56
  inj.bind_scope(ss),
57
- inj.bind_scope_seed(ss, inj.Key(pytest.FixtureRequest, tag=pts)),
57
+ inj.bind_scope_seed(inj.Key(pytest.FixtureRequest, tag=pts), ss),
58
58
  )
59
59
  for pts, ss in _SCOPES_BY_PYTEST_SCOPE.items()
60
60
  ],
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: omlish
3
- Version: 0.0.0.dev176
3
+ Version: 0.0.0.dev178
4
4
  Summary: omlish
5
5
  Author: wrmsr
6
6
  License: BSD-3-Clause
@@ -1,5 +1,5 @@
1
1
  omlish/.manifests.json,sha256=lRkBDFxlAbf6lN5upo3WSf-owW8YG1T21dfpbQL-XHM,7598
2
- omlish/__about__.py,sha256=RBsHqgr7y9qfTJmoVH5GI5S3QcYfEugaqakxVkSTWjk,3409
2
+ omlish/__about__.py,sha256=d7Vqq3YraWwcnd0sR_ExiQePePaN2h_hwFfJ-RBD7IQ,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
@@ -294,7 +294,7 @@ omlish/inject/origins.py,sha256=OVQkiuRxx6ZtE8ZliufdndtFexcfpj-wZSDkUeGUCYM,534
294
294
  omlish/inject/overrides.py,sha256=hrm243slCw_DDRbn3dK5QK1jfHezVokG-WYO2JaQOV8,535
295
295
  omlish/inject/privates.py,sha256=hZOa_keY3KlXAzyiZ-sfN697UKXpkfXXNUIEmGT5TAA,641
296
296
  omlish/inject/providers.py,sha256=Z6UzNCwRhKHHR0L5CyBMo4F-1M_xElLkPA6EKQWcqlw,754
297
- omlish/inject/scopes.py,sha256=bxbpEPqRs9N61GDKD-4ZWXkB6xiLDrILLjcE2IvZEtM,1989
297
+ omlish/inject/scopes.py,sha256=iQ8gD5V2z6ahkJhfy7zycMnUky-scfT_M-GjLjKN6Uw,1989
298
298
  omlish/inject/types.py,sha256=11WVEPkZ-_8cv1BeTDRU-soIYxB_6x7dyWtsa2Iej9U,251
299
299
  omlish/inject/utils.py,sha256=_UOZqA8IcLWPqf4Mcg9iIusQ5yxP_6Txg6PWtUYl23o,408
300
300
  omlish/inject/impl/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -379,7 +379,7 @@ omlish/lite/cached.py,sha256=O7ozcoDNFm1Hg2wtpHEqYSp_i_nCLNOP6Ueq_Uk-7mU,1300
379
379
  omlish/lite/check.py,sha256=0PD-GKtaDqDX6jU5KbzbMvH-vl6jH82xgYfplmfTQkg,12941
380
380
  omlish/lite/contextmanagers.py,sha256=m9JO--p7L7mSl4cycXysH-1AO27weDKjP3DZG61cwwM,1683
381
381
  omlish/lite/dataclasses.py,sha256=M6UD4VwGo0Ky7RNzKWbO0IOy7iBZVCIbTiC6EYbFnX8,1035
382
- omlish/lite/inject.py,sha256=EEaioN9ESAveVCMe2s5osjwI97FPRUVoU8P95vGUiYo,23376
382
+ omlish/lite/inject.py,sha256=yolrXQPZ903FcZwbSrUXsXNCylZ31W1vesjLelRWGgo,28758
383
383
  omlish/lite/json.py,sha256=7-02Ny4fq-6YAu5ynvqoijhuYXWpLmfCI19GUeZnb1c,740
384
384
  omlish/lite/logs.py,sha256=CWFG0NKGhqNeEgryF5atN2gkPYbUdTINEw_s1phbINM,51
385
385
  omlish/lite/marshal.py,sha256=O_v_slgjAAiSGWComdRhqLXdumuIlBDc3SvoG_p00QM,15863
@@ -550,7 +550,7 @@ omlish/testing/pytest/helpers.py,sha256=TJpD60mBtLi9FtxX4TThfuXvg5FIRPSiZk1aeRwe
550
550
  omlish/testing/pytest/marks.py,sha256=4-3WgunN_fcmhkmQ6mtX_AFo5QRDwflNSH7NoyoXui4,432
551
551
  omlish/testing/pytest/skip.py,sha256=NxTkAQiS3HKZR3sfFdxOR2LCFwtCveY6Ap-qtexiZbw,839
552
552
  omlish/testing/pytest/inject/__init__.py,sha256=pdRKv1HcDmJ_yArKJbYITPXXZthRSGgBJWqITr0Er38,117
553
- omlish/testing/pytest/inject/harness.py,sha256=v4DaKJ0KL8oQjzIMK43Gh8GHP4hiI6-lY37O9lyOHRk,5724
553
+ omlish/testing/pytest/inject/harness.py,sha256=_Qf7lLcYc_dpauYOE68u_a65jPCFWmQUYv9m_OOdNqs,5724
554
554
  omlish/testing/pytest/plugins/__init__.py,sha256=ys1zXrYrNm7Uo6YOIVJ6Bd3dQo6kv387k7MbTYlqZSI,467
555
555
  omlish/testing/pytest/plugins/_registry.py,sha256=IK04KlBgiOJxKAyCCgjpX2R-9tE-btalYJkgjLc8Te8,77
556
556
  omlish/testing/pytest/plugins/asyncs.py,sha256=CG-cWWxCtxVIyKJKEjxfFV0MVwYBHPo1mb-umCGz9X8,5532
@@ -570,9 +570,9 @@ omlish/text/glyphsplit.py,sha256=Ug-dPRO7x-OrNNr8g1y6DotSZ2KH0S-VcOmUobwa4B0,329
570
570
  omlish/text/indent.py,sha256=6Jj6TFY9unaPa4xPzrnZemJ-fHsV53IamP93XGjSUHs,1274
571
571
  omlish/text/parts.py,sha256=7vPF1aTZdvLVYJ4EwBZVzRSy8XB3YqPd7JwEnNGGAOo,6495
572
572
  omlish/text/random.py,sha256=jNWpqiaKjKyTdMXC-pWAsSC10AAP-cmRRPVhm59ZWLk,194
573
- omlish-0.0.0.dev176.dist-info/LICENSE,sha256=B_hVtavaA8zCYDW99DYdcpDLKz1n3BBRjZrcbv8uG8c,1451
574
- omlish-0.0.0.dev176.dist-info/METADATA,sha256=5-3GbbCrNa96UjitAH5tN4L4YlNjWLwyCMzobgcWWRE,4264
575
- omlish-0.0.0.dev176.dist-info/WHEEL,sha256=PZUExdf71Ui_so67QXpySuHtCi3-J3wvF4ORK6k_S8U,91
576
- omlish-0.0.0.dev176.dist-info/entry_points.txt,sha256=Lt84WvRZJskWCAS7xnQGZIeVWksprtUHj0llrvVmod8,35
577
- omlish-0.0.0.dev176.dist-info/top_level.txt,sha256=pePsKdLu7DvtUiecdYXJ78iO80uDNmBlqe-8hOzOmfs,7
578
- omlish-0.0.0.dev176.dist-info/RECORD,,
573
+ omlish-0.0.0.dev178.dist-info/LICENSE,sha256=B_hVtavaA8zCYDW99DYdcpDLKz1n3BBRjZrcbv8uG8c,1451
574
+ omlish-0.0.0.dev178.dist-info/METADATA,sha256=Zltjh6LQhgQnlgh8RPDJgnQHdnTVpzHEP3rMc3OcjJU,4264
575
+ omlish-0.0.0.dev178.dist-info/WHEEL,sha256=PZUExdf71Ui_so67QXpySuHtCi3-J3wvF4ORK6k_S8U,91
576
+ omlish-0.0.0.dev178.dist-info/entry_points.txt,sha256=Lt84WvRZJskWCAS7xnQGZIeVWksprtUHj0llrvVmod8,35
577
+ omlish-0.0.0.dev178.dist-info/top_level.txt,sha256=pePsKdLu7DvtUiecdYXJ78iO80uDNmBlqe-8hOzOmfs,7
578
+ omlish-0.0.0.dev178.dist-info/RECORD,,