omlish 0.0.0.dev420__py3-none-any.whl → 0.0.0.dev421__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.
Files changed (93) hide show
  1. omlish/__about__.py +2 -2
  2. omlish/asyncs/bluelet/core.py +2 -2
  3. omlish/asyncs/bluelet/events.py +4 -3
  4. omlish/asyncs/bluelet/files.py +2 -2
  5. omlish/asyncs/bluelet/sockets.py +2 -2
  6. omlish/collections/frozen.py +1 -1
  7. omlish/collections/kv/base.py +1 -1
  8. omlish/collections/multimaps.py +1 -1
  9. omlish/collections/persistent/treapmap.py +2 -1
  10. omlish/concurrent/threadlets.py +2 -2
  11. omlish/configs/formats.py +5 -4
  12. omlish/configs/processing/flattening.py +2 -1
  13. omlish/configs/processing/rewriting.py +2 -2
  14. omlish/configs/shadow.py +3 -2
  15. omlish/daemons/spawning.py +2 -2
  16. omlish/daemons/targets.py +1 -1
  17. omlish/daemons/waiting.py +2 -1
  18. omlish/dataclasses/impl/generation/base.py +3 -2
  19. omlish/dataclasses/impl/generation/compilation.py +2 -1
  20. omlish/dataclasses/impl/generation/ops.py +2 -2
  21. omlish/dataclasses/impl/generation/processor.py +1 -1
  22. omlish/dataclasses/metaclass/meta.py +1 -0
  23. omlish/dataclasses/tools/static.py +5 -1
  24. omlish/formats/dotenv.py +3 -1
  25. omlish/formats/logfmt.py +111 -0
  26. omlish/formats/yaml.py +1 -1
  27. omlish/funcs/builders.py +2 -1
  28. omlish/funcs/match.py +1 -1
  29. omlish/funcs/pairs.py +41 -23
  30. omlish/funcs/pipes.py +3 -1
  31. omlish/http/asgi.py +2 -1
  32. omlish/http/coro/client/io.py +3 -2
  33. omlish/http/coro/server/server.py +2 -2
  34. omlish/http/handlers.py +2 -1
  35. omlish/http/jwt.py +1 -1
  36. omlish/http/parsing.py +2 -2
  37. omlish/io/compress/base.py +3 -2
  38. omlish/io/coro/readers.py +4 -3
  39. omlish/io/fdio/handlers.py +3 -2
  40. omlish/io/fdio/pollers.py +3 -1
  41. omlish/lang/__init__.py +20 -8
  42. omlish/lang/attrs.py +3 -1
  43. omlish/lang/casing.py +3 -1
  44. omlish/lang/classes/abstract.py +35 -96
  45. omlish/lang/classes/virtual.py +2 -2
  46. omlish/lang/contextmanagers.py +6 -4
  47. omlish/lang/generators.py +2 -2
  48. omlish/lang/iterables.py +6 -6
  49. omlish/lang/objects.py +0 -58
  50. omlish/lang/outcomes.py +3 -1
  51. omlish/lang/typing.py +5 -24
  52. omlish/lite/abstract.py +119 -0
  53. omlish/lite/contextmanagers.py +4 -1
  54. omlish/lite/inject.py +9 -9
  55. omlish/lite/marshal.py +230 -114
  56. omlish/lite/maybes.py +3 -1
  57. omlish/lite/maysync.py +4 -2
  58. omlish/lite/objects.py +81 -0
  59. omlish/lite/reflect.py +0 -15
  60. omlish/lite/timeouts.py +3 -1
  61. omlish/lite/wrappers.py +23 -0
  62. omlish/logs/abc.py +21 -5
  63. omlish/logs/all.py +14 -3
  64. omlish/logs/configs.py +13 -10
  65. omlish/logs/levels.py +4 -0
  66. omlish/logs/protocol.py +77 -39
  67. omlish/marshal/__init__.py +1 -0
  68. omlish/os/atomics.py +3 -2
  69. omlish/os/deathpacts/base.py +4 -2
  70. omlish/os/forkhooks.py +2 -2
  71. omlish/os/pidfiles/pinning.py +2 -1
  72. omlish/reflect/types.py +4 -3
  73. omlish/secrets/crypto.py +1 -1
  74. omlish/sockets/bind.py +2 -1
  75. omlish/sockets/handlers.py +3 -2
  76. omlish/sockets/server/handlers.py +2 -1
  77. omlish/sockets/server/server.py +4 -3
  78. omlish/sql/api/base.py +2 -2
  79. omlish/subprocesses/asyncs.py +2 -1
  80. omlish/subprocesses/base.py +2 -2
  81. omlish/subprocesses/maysync.py +1 -2
  82. omlish/subprocesses/run.py +2 -1
  83. omlish/subprocesses/sync.py +2 -1
  84. omlish/term/coloring.py +3 -1
  85. omlish/testing/pytest/plugins/asyncs/backends/base.py +3 -1
  86. omlish/testing/unittest/loading.py +2 -2
  87. omlish/text/asdl.py +4 -3
  88. {omlish-0.0.0.dev420.dist-info → omlish-0.0.0.dev421.dist-info}/METADATA +1 -1
  89. {omlish-0.0.0.dev420.dist-info → omlish-0.0.0.dev421.dist-info}/RECORD +93 -88
  90. {omlish-0.0.0.dev420.dist-info → omlish-0.0.0.dev421.dist-info}/WHEEL +0 -0
  91. {omlish-0.0.0.dev420.dist-info → omlish-0.0.0.dev421.dist-info}/entry_points.txt +0 -0
  92. {omlish-0.0.0.dev420.dist-info → omlish-0.0.0.dev421.dist-info}/licenses/LICENSE +0 -0
  93. {omlish-0.0.0.dev420.dist-info → omlish-0.0.0.dev421.dist-info}/top_level.txt +0 -0
@@ -13,6 +13,8 @@ import time
13
13
  import types
14
14
  import typing as ta
15
15
 
16
+ from ..lite.abstract import Abstract
17
+
16
18
 
17
19
  T = ta.TypeVar('T')
18
20
  K = ta.TypeVar('K')
@@ -22,7 +24,7 @@ V = ta.TypeVar('V')
22
24
  ##
23
25
 
24
26
 
25
- class ContextManaged(abc.ABC): # noqa
27
+ class ContextManaged(Abstract):
26
28
  def __enter__(self):
27
29
  return None
28
30
 
@@ -56,7 +58,7 @@ NOP_CONTEXT_MANAGER = ValueContextManager(None)
56
58
  #
57
59
 
58
60
 
59
- class AsyncContextManaged(abc.ABC): # noqa
61
+ class AsyncContextManaged(Abstract):
60
62
  async def __aenter__(self):
61
63
  return None
62
64
 
@@ -90,7 +92,7 @@ NOP_ASYNC_CONTEXT_MANAGER = ValueAsyncContextManager(None)
90
92
  ##
91
93
 
92
94
 
93
- class ContextManager(abc.ABC, ta.Generic[T]):
95
+ class ContextManager(Abstract, ta.Generic[T]):
94
96
  def __init_subclass__(cls, **kwargs: ta.Any) -> None:
95
97
  super().__init_subclass__(**kwargs)
96
98
 
@@ -122,7 +124,7 @@ class ContextManager(abc.ABC, ta.Generic[T]):
122
124
  #
123
125
 
124
126
 
125
- class AsyncContextManager(abc.ABC, ta.Generic[T]):
127
+ class AsyncContextManager(Abstract, ta.Generic[T]):
126
128
  def __init_subclass__(cls, **kwargs: ta.Any) -> None:
127
129
  super().__init_subclass__(**kwargs)
128
130
 
omlish/lang/generators.py CHANGED
@@ -2,8 +2,8 @@ import abc
2
2
  import functools
3
3
  import typing as ta
4
4
 
5
+ from ..lite.abstract import Abstract
5
6
  from ..lite.maybes import Maybe
6
- from .classes.restrict import Abstract
7
7
 
8
8
 
9
9
  T = ta.TypeVar('T')
@@ -45,7 +45,7 @@ class GeneratorLike(ta.Protocol[O_co, I_contra, R_co]):
45
45
  ...
46
46
 
47
47
 
48
- class GeneratorLike_(abc.ABC, ta.Generic[O, I, R]): # noqa
48
+ class GeneratorLike_(Abstract, ta.Generic[O, I, R]): # noqa
49
49
  @abc.abstractmethod
50
50
  def send(self, i: I) -> O: # Raises[StopIteration[R]]
51
51
  raise NotImplementedError
omlish/lang/iterables.py CHANGED
@@ -1,4 +1,4 @@
1
- import dataclasses as dc
1
+ import collections
2
2
  import functools
3
3
  import itertools
4
4
  import typing as ta
@@ -22,9 +22,8 @@ def take(n: int, it: ta.Iterable[T]) -> list[T]:
22
22
  return list(itertools.islice(it, n))
23
23
 
24
24
 
25
- def exhaust(it: ta.Iterable[ta.Any]) -> None:
26
- for _ in it:
27
- pass
25
+ def consume(it: ta.Iterable[ta.Any]) -> None:
26
+ collections.deque(it, maxlen=0)
28
27
 
29
28
 
30
29
  def peek(vs: ta.Iterable[T]) -> tuple[T, ta.Iterator[T]]:
@@ -87,9 +86,10 @@ def readiter(f, sz):
87
86
  ##
88
87
 
89
88
 
90
- @dc.dataclass(frozen=True)
89
+ @ta.final
91
90
  class IterGen(ta.Generic[T]):
92
- fn: ta.Callable[[], ta.Iterable[T]]
91
+ def __init__(self, fn: ta.Callable[[], ta.Iterable[T]]) -> None:
92
+ self.fn = fn
93
93
 
94
94
  def __iter__(self):
95
95
  return iter(self.fn())
omlish/lang/objects.py CHANGED
@@ -101,64 +101,6 @@ def deep_subclasses(
101
101
  todo.extend(reversed(cur.__subclasses__()))
102
102
 
103
103
 
104
- def mro_owner_dict(
105
- instance_cls: type,
106
- owner_cls: type | None = None,
107
- *,
108
- bottom_up_key_order: bool = False,
109
- sort_keys: bool = False,
110
- ) -> ta.Mapping[str, tuple[type, ta.Any]]:
111
- if owner_cls is None:
112
- owner_cls = instance_cls
113
-
114
- mro = instance_cls.__mro__[-2::-1]
115
- try:
116
- pos = mro.index(owner_cls)
117
- except ValueError:
118
- raise TypeError(f'Owner class {owner_cls} not in mro of instance class {instance_cls}') from None
119
-
120
- dct: dict[str, tuple[type, ta.Any]] = {}
121
- if not bottom_up_key_order:
122
- for cur_cls in mro[:pos + 1][::-1]:
123
- for k, v in cur_cls.__dict__.items():
124
- if k not in dct:
125
- dct[k] = (cur_cls, v)
126
-
127
- else:
128
- for cur_cls in mro[:pos + 1]:
129
- dct.update({k: (cur_cls, v) for k, v in cur_cls.__dict__.items()})
130
-
131
- if sort_keys:
132
- dct = dict(sorted(dct.items(), key=lambda t: t[0]))
133
-
134
- return dct
135
-
136
-
137
- def mro_dict(
138
- instance_cls: type,
139
- owner_cls: type | None = None,
140
- *,
141
- bottom_up_key_order: bool = False,
142
- sort_keys: bool = False,
143
- ) -> ta.Mapping[str, ta.Any]:
144
- return {
145
- k: v
146
- for k, (o, v) in mro_owner_dict(
147
- instance_cls,
148
- owner_cls,
149
- bottom_up_key_order=bottom_up_key_order,
150
- sort_keys=sort_keys,
151
- ).items()
152
- }
153
-
154
-
155
- def dir_dict(o: ta.Any) -> dict[str, ta.Any]:
156
- return {
157
- a: getattr(o, a)
158
- for a in dir(o)
159
- }
160
-
161
-
162
104
  ##
163
105
 
164
106
 
omlish/lang/outcomes.py CHANGED
@@ -22,6 +22,8 @@ import abc
22
22
  import dataclasses as dc
23
23
  import typing as ta
24
24
 
25
+ from ..lite.abstract import Abstract
26
+
25
27
 
26
28
  T = ta.TypeVar('T')
27
29
  ValueT_co = ta.TypeVar('ValueT_co', covariant=True)
@@ -131,7 +133,7 @@ async def acapture(
131
133
 
132
134
 
133
135
  @dc.dataclass(repr=False, init=False, slots=True, frozen=True, order=True)
134
- class Outcome(abc.ABC, ta.Generic[ValueT_co]):
136
+ class Outcome(Abstract, ta.Generic[ValueT_co]):
135
137
  """
136
138
  An abstract class representing the result of a Python computation.
137
139
 
omlish/lang/typing.py CHANGED
@@ -5,10 +5,11 @@ TODO:
5
5
  - probably need to gen types per inst
6
6
  - typed_factory
7
7
  """
8
- import functools
9
8
  import inspect
10
9
  import typing as ta
11
10
 
11
+ from ..lite.wrappers import update_wrapper_no_annotations
12
+
12
13
 
13
14
  Ty = ta.TypeVar('Ty', bound=type)
14
15
 
@@ -59,26 +60,6 @@ def protocol_check(proto: type) -> ta.Callable[[Ty], Ty]:
59
60
  ##
60
61
 
61
62
 
62
- _ANN_ATTRS: frozenset[str] = frozenset([
63
- '__annotations__',
64
-
65
- '__annotate__',
66
- '__annotate_func__',
67
-
68
- '__annotations_cache__',
69
- ])
70
-
71
- _UPDATE_WRAPPER_ASSIGNED_NO_ANNS = list(frozenset(functools.WRAPPER_ASSIGNMENTS) - _ANN_ATTRS)
72
-
73
-
74
- def _update_wrapper_no_anns(wrapper, wrapped):
75
- functools.update_wrapper(wrapper, wrapped, assigned=_UPDATE_WRAPPER_ASSIGNED_NO_ANNS)
76
- return wrapper
77
-
78
-
79
- ##
80
-
81
-
82
63
  _MISSING = object()
83
64
 
84
65
 
@@ -113,7 +94,7 @@ def typed_lambda(ret=_MISSING, **kw): # noqa
113
94
  src = f'{"".join(proto)} {"".join(call)}'
114
95
  exec(src, ns)
115
96
 
116
- lam = _update_wrapper_no_anns(ns['__lam'], fn)
97
+ lam = update_wrapper_no_annotations(ns['__lam'], fn)
117
98
  lam.__signature__ = inspect.signature(lam, follow_wrapped=False)
118
99
  return lam
119
100
 
@@ -131,7 +112,7 @@ def typed_partial(obj, **kw): # noqa
131
112
 
132
113
  sig = inspect.signature(obj)
133
114
 
134
- inner = _update_wrapper_no_anns(lambda **lkw: obj(**lkw, **kw), obj)
115
+ inner = update_wrapper_no_annotations(lambda **lkw: obj(**lkw, **kw), obj)
135
116
 
136
117
  ret = (
137
118
  obj if isinstance(obj, type) else
@@ -149,7 +130,7 @@ def typed_partial(obj, **kw): # noqa
149
130
  },
150
131
  )(inner)
151
132
 
152
- return _update_wrapper_no_anns(lam, obj)
133
+ return update_wrapper_no_annotations(lam, obj)
153
134
 
154
135
 
155
136
  ##
@@ -0,0 +1,119 @@
1
+ # ruff: noqa: UP006
2
+ import abc
3
+ import typing as ta
4
+
5
+
6
+ ##
7
+
8
+
9
+ _ABSTRACT_METHODS_ATTR = '__abstractmethods__'
10
+ _IS_ABSTRACT_METHOD_ATTR = '__isabstractmethod__'
11
+
12
+
13
+ def is_abstract_method(obj: ta.Any) -> bool:
14
+ return bool(getattr(obj, _IS_ABSTRACT_METHOD_ATTR, False))
15
+
16
+
17
+ def update_abstracts(cls, *, force=False):
18
+ if not force and not hasattr(cls, _ABSTRACT_METHODS_ATTR):
19
+ # Per stdlib: We check for __abstractmethods__ here because cls might by a C implementation or a python
20
+ # implementation (especially during testing), and we want to handle both cases.
21
+ return cls
22
+
23
+ abstracts: ta.Set[str] = set()
24
+
25
+ for scls in cls.__bases__:
26
+ for name in getattr(scls, _ABSTRACT_METHODS_ATTR, ()):
27
+ value = getattr(cls, name, None)
28
+ if getattr(value, _IS_ABSTRACT_METHOD_ATTR, False):
29
+ abstracts.add(name)
30
+
31
+ for name, value in cls.__dict__.items():
32
+ if getattr(value, _IS_ABSTRACT_METHOD_ATTR, False):
33
+ abstracts.add(name)
34
+
35
+ setattr(cls, _ABSTRACT_METHODS_ATTR, frozenset(abstracts))
36
+ return cls
37
+
38
+
39
+ #
40
+
41
+
42
+ class AbstractTypeError(TypeError):
43
+ pass
44
+
45
+
46
+ _FORCE_ABSTRACT_ATTR = '__forceabstract__'
47
+
48
+
49
+ class Abstract:
50
+ """
51
+ Different from, but interoperable with, abc.ABC / abc.ABCMeta:
52
+
53
+ - This raises AbstractTypeError during class creation, not instance instantiation - unless Abstract or abc.ABC are
54
+ explicitly present in the class's direct bases.
55
+ - This will forbid instantiation of classes with Abstract in their direct bases even if there are no
56
+ abstractmethods left on the class.
57
+ - This is a mixin, not a metaclass.
58
+ - As it is not an ABCMeta, this does not support virtual base classes. As a result, operations like `isinstance`
59
+ and `issubclass` are ~7x faster.
60
+ - It additionally enforces a base class order of (Abstract, abc.ABC) to preemptively prevent common mro conflicts.
61
+
62
+ If not mixed-in with an ABCMeta, it will update __abstractmethods__ itself.
63
+ """
64
+
65
+ __slots__ = ()
66
+
67
+ __abstractmethods__: ta.ClassVar[ta.FrozenSet[str]] = frozenset()
68
+
69
+ #
70
+
71
+ def __forceabstract__(self):
72
+ raise TypeError
73
+
74
+ # This is done manually, rather than through @abc.abstractmethod, to mask it from static analysis.
75
+ setattr(__forceabstract__, _IS_ABSTRACT_METHOD_ATTR, True)
76
+
77
+ #
78
+
79
+ def __init_subclass__(cls, **kwargs: ta.Any) -> None:
80
+ setattr(
81
+ cls,
82
+ _FORCE_ABSTRACT_ATTR,
83
+ getattr(Abstract, _FORCE_ABSTRACT_ATTR) if Abstract in cls.__bases__ else False,
84
+ )
85
+
86
+ super().__init_subclass__(**kwargs)
87
+
88
+ if not (Abstract in cls.__bases__ or abc.ABC in cls.__bases__):
89
+ ams = {a: cls for a, o in cls.__dict__.items() if is_abstract_method(o)}
90
+
91
+ seen = set(cls.__dict__)
92
+ for b in cls.__bases__:
93
+ ams.update({a: b for a in set(getattr(b, _ABSTRACT_METHODS_ATTR, [])) - seen}) # noqa
94
+ seen.update(dir(b))
95
+
96
+ if ams:
97
+ raise AbstractTypeError(
98
+ f'Cannot subclass abstract class {cls.__name__} with abstract methods: ' +
99
+ ', '.join(sorted([
100
+ '.'.join([
101
+ *([m] if (m := getattr(c, '__module__')) else []),
102
+ getattr(c, '__qualname__', getattr(c, '__name__')),
103
+ a,
104
+ ])
105
+ for a, c in ams.items()
106
+ ])),
107
+ )
108
+
109
+ xbi = (Abstract, abc.ABC) # , ta.Generic ?
110
+ bis = [(cls.__bases__.index(b), b) for b in xbi if b in cls.__bases__]
111
+ if bis != sorted(bis):
112
+ raise TypeError(
113
+ f'Abstract subclass {cls.__name__} must have proper base class order of '
114
+ f'({", ".join(getattr(b, "__name__") for b in xbi)}), got: '
115
+ f'({", ".join(getattr(b, "__name__") for _, b in sorted(bis))})',
116
+ )
117
+
118
+ if not isinstance(cls, abc.ABCMeta):
119
+ update_abstracts(cls, force=True)
@@ -190,7 +190,7 @@ def attr_setting(obj, attr, val, *, default=None): # noqa
190
190
  ##
191
191
 
192
192
 
193
- class aclosing(contextlib.AbstractAsyncContextManager): # noqa
193
+ class AsyncClosingManager(contextlib.AbstractAsyncContextManager):
194
194
  def __init__(self, thing):
195
195
  self.thing = thing
196
196
 
@@ -199,3 +199,6 @@ class aclosing(contextlib.AbstractAsyncContextManager): # noqa
199
199
 
200
200
  async def __aexit__(self, *exc_info):
201
201
  await self.thing.aclose()
202
+
203
+
204
+ aclosing = AsyncClosingManager
omlish/lite/inject.py CHANGED
@@ -9,6 +9,7 @@ import types
9
9
  import typing as ta
10
10
  import weakref
11
11
 
12
+ from .abstract import Abstract
12
13
  from .check import check
13
14
  from .maybes import Maybe
14
15
  from .reflect import get_optional_alias_arg
@@ -58,7 +59,7 @@ def check_valid_injector_key_cls(cls: T) -> T:
58
59
  ##
59
60
 
60
61
 
61
- class InjectorProvider(abc.ABC):
62
+ class InjectorProvider(Abstract):
62
63
  @abc.abstractmethod
63
64
  def provider_fn(self) -> InjectorProviderFn:
64
65
  raise NotImplementedError
@@ -77,7 +78,7 @@ class InjectorBinding:
77
78
  check.isinstance(self.provider, InjectorProvider)
78
79
 
79
80
 
80
- class InjectorBindings(abc.ABC):
81
+ class InjectorBindings(Abstract):
81
82
  @abc.abstractmethod
82
83
  def bindings(self) -> ta.Iterator[InjectorBinding]:
83
84
  raise NotImplementedError
@@ -85,7 +86,7 @@ class InjectorBindings(abc.ABC):
85
86
  ##
86
87
 
87
88
 
88
- class Injector(abc.ABC):
89
+ class Injector(Abstract):
89
90
  @abc.abstractmethod
90
91
  def try_provide(self, key: ta.Any) -> Maybe[ta.Any]:
91
92
  raise NotImplementedError
@@ -344,14 +345,12 @@ def injector_override(p: InjectorBindings, *args: InjectorBindingOrBindings) ->
344
345
  # scopes
345
346
 
346
347
 
347
- class InjectorScope(abc.ABC): # noqa
348
+ class InjectorScope(Abstract):
348
349
  def __init__(
349
350
  self,
350
351
  *,
351
352
  _i: Injector,
352
353
  ) -> None:
353
- check.not_in(abc.ABC, type(self).__bases__)
354
-
355
354
  super().__init__()
356
355
 
357
356
  self._i = _i
@@ -382,7 +381,7 @@ class InjectorScope(abc.ABC): # noqa
382
381
  raise NotImplementedError
383
382
 
384
383
 
385
- class ExclusiveInjectorScope(InjectorScope, abc.ABC):
384
+ class ExclusiveInjectorScope(InjectorScope, Abstract):
386
385
  _st: ta.Optional[InjectorScope.State] = None
387
386
 
388
387
  def state(self) -> InjectorScope.State:
@@ -398,12 +397,13 @@ class ExclusiveInjectorScope(InjectorScope, abc.ABC):
398
397
  self._st = None
399
398
 
400
399
 
401
- class ContextvarInjectorScope(InjectorScope, abc.ABC):
400
+ class ContextvarInjectorScope(InjectorScope, Abstract):
402
401
  _cv: contextvars.ContextVar
403
402
 
404
403
  def __init_subclass__(cls, **kwargs: ta.Any) -> None:
405
404
  super().__init_subclass__(**kwargs)
406
405
 
406
+ check.not_in(Abstract, cls.__bases__)
407
407
  check.not_in(abc.ABC, cls.__bases__)
408
408
  check.state(not hasattr(cls, '_cv'))
409
409
  cls._cv = contextvars.ContextVar(f'{cls.__name__}_cv')
@@ -909,7 +909,7 @@ class InjectorBinder:
909
909
  pws: ta.List[ta.Any] = []
910
910
  if in_ is not None:
911
911
  check.issubclass(in_, InjectorScope)
912
- check.not_in(abc.ABC, in_.__bases__)
912
+ check.not_in(Abstract, in_.__bases__)
913
913
  pws.append(functools.partial(ScopedInjectorProvider, k=key, sc=in_))
914
914
  if singleton:
915
915
  pws.append(SingletonInjectorProvider)