python-injection 0.12.2__tar.gz → 0.13.0__tar.gz

Sign up to get free protection for your applications and to get access to all the features.
Files changed (30) hide show
  1. python_injection-0.13.0/.gitignore +322 -0
  2. {python_injection-0.12.2 → python_injection-0.13.0}/PKG-INFO +14 -15
  3. {python_injection-0.12.2 → python_injection-0.13.0}/README.md +3 -2
  4. {python_injection-0.12.2 → python_injection-0.13.0}/injection/__init__.py +5 -0
  5. {python_injection-0.12.2 → python_injection-0.13.0}/injection/__init__.pyi +59 -48
  6. {python_injection-0.12.2 → python_injection-0.13.0}/injection/_core/common/asynchronous.py +15 -7
  7. {python_injection-0.12.2 → python_injection-0.13.0}/injection/_core/common/event.py +1 -1
  8. python_injection-0.13.0/injection/_core/common/key.py +5 -0
  9. {python_injection-0.12.2 → python_injection-0.13.0}/injection/_core/common/type.py +33 -2
  10. python_injection-0.13.0/injection/_core/injectables.py +179 -0
  11. {python_injection-0.12.2 → python_injection-0.13.0}/injection/_core/module.py +111 -48
  12. python_injection-0.13.0/injection/_core/scope.py +215 -0
  13. {python_injection-0.12.2 → python_injection-0.13.0}/injection/exceptions.py +12 -0
  14. python_injection-0.13.0/injection/integrations/fastapi.py +38 -0
  15. {python_injection-0.12.2 → python_injection-0.13.0}/injection/testing/__init__.py +2 -0
  16. python_injection-0.13.0/injection/testing/__init__.pyi +17 -0
  17. {python_injection-0.12.2 → python_injection-0.13.0}/injection/utils.py +7 -2
  18. {python_injection-0.12.2 → python_injection-0.13.0}/pyproject.toml +39 -32
  19. python_injection-0.12.2/injection/_core/common/threading.py +0 -11
  20. python_injection-0.12.2/injection/_core/injectables.py +0 -98
  21. python_injection-0.12.2/injection/integrations/fastapi.py +0 -63
  22. python_injection-0.12.2/injection/testing/__init__.pyi +0 -14
  23. {python_injection-0.12.2 → python_injection-0.13.0}/injection/_core/__init__.py +0 -0
  24. {python_injection-0.12.2 → python_injection-0.13.0}/injection/_core/common/__init__.py +0 -0
  25. {python_injection-0.12.2 → python_injection-0.13.0}/injection/_core/common/invertible.py +0 -0
  26. {python_injection-0.12.2 → python_injection-0.13.0}/injection/_core/common/lazy.py +0 -0
  27. {python_injection-0.12.2 → python_injection-0.13.0}/injection/_core/descriptors.py +0 -0
  28. {python_injection-0.12.2 → python_injection-0.13.0}/injection/_core/hook.py +0 -0
  29. {python_injection-0.12.2 → python_injection-0.13.0}/injection/integrations/__init__.py +0 -0
  30. {python_injection-0.12.2 → python_injection-0.13.0}/injection/py.typed +0 -0
@@ -0,0 +1,322 @@
1
+ # Created by https://www.toptal.com/developers/gitignore/api/python,pycharm,dotenv,macos
2
+ # Edit at https://www.toptal.com/developers/gitignore?templates=python,pycharm,dotenv,macos
3
+
4
+ ### dotenv ###
5
+ .env
6
+
7
+ ### macOS ###
8
+ # General
9
+ .DS_Store
10
+ .AppleDouble
11
+ .LSOverride
12
+
13
+ # Icon must end with two \r
14
+ Icon
15
+
16
+
17
+ # Thumbnails
18
+ ._*
19
+
20
+ # Files that might appear in the root of a volume
21
+ .DocumentRevisions-V100
22
+ .fseventsd
23
+ .Spotlight-V100
24
+ .TemporaryItems
25
+ .Trashes
26
+ .VolumeIcon.icns
27
+ .com.apple.timemachine.donotpresent
28
+
29
+ # Directories potentially created on remote AFP share
30
+ .AppleDB
31
+ .AppleDesktop
32
+ Network Trash Folder
33
+ Temporary Items
34
+ .apdisk
35
+
36
+ ### macOS Patch ###
37
+ # iCloud generated files
38
+ *.icloud
39
+
40
+ ### PyCharm ###
41
+ # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider
42
+ # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
43
+
44
+ # User-specific stuff
45
+ .idea/**/workspace.xml
46
+ .idea/**/tasks.xml
47
+ .idea/**/usage.statistics.xml
48
+ .idea/**/dictionaries
49
+ .idea/**/shelf
50
+
51
+ # AWS User-specific
52
+ .idea/**/aws.xml
53
+
54
+ # Generated files
55
+ .idea/**/contentModel.xml
56
+
57
+ # Sensitive or high-churn files
58
+ .idea/**/dataSources/
59
+ .idea/**/dataSources.ids
60
+ .idea/**/dataSources.local.xml
61
+ .idea/**/sqlDataSources.xml
62
+ .idea/**/dynamic.xml
63
+ .idea/**/uiDesigner.xml
64
+ .idea/**/dbnavigator.xml
65
+
66
+ # Gradle
67
+ .idea/**/gradle.xml
68
+ .idea/**/libraries
69
+
70
+ # Gradle and Maven with auto-import
71
+ # When using Gradle or Maven with auto-import, you should exclude module files,
72
+ # since they will be recreated, and may cause churn. Uncomment if using
73
+ # auto-import.
74
+ # .idea/artifacts
75
+ # .idea/compiler.xml
76
+ # .idea/jarRepositories.xml
77
+ # .idea/modules.xml
78
+ # .idea/*.iml
79
+ # .idea/modules
80
+ # *.iml
81
+ # *.ipr
82
+
83
+ # CMake
84
+ cmake-build-*/
85
+
86
+ # Mongo Explorer plugin
87
+ .idea/**/mongoSettings.xml
88
+
89
+ # File-based project format
90
+ *.iws
91
+
92
+ # IntelliJ
93
+ out/
94
+
95
+ # mpeltonen/sbt-idea plugin
96
+ .idea_modules/
97
+
98
+ # JIRA plugin
99
+ atlassian-ide-plugin.xml
100
+
101
+ # Cursive Clojure plugin
102
+ .idea/replstate.xml
103
+
104
+ # SonarLint plugin
105
+ .idea/sonarlint/
106
+
107
+ # Crashlytics plugin (for Android Studio and IntelliJ)
108
+ com_crashlytics_export_strings.xml
109
+ crashlytics.properties
110
+ crashlytics-build.properties
111
+ fabric.properties
112
+
113
+ # Editor-based Rest Client
114
+ .idea/httpRequests
115
+
116
+ # Android studio 3.1+ serialized cache file
117
+ .idea/caches/build_file_checksums.ser
118
+
119
+ ### PyCharm Patch ###
120
+ # Comment Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-215987721
121
+
122
+ # *.iml
123
+ # modules.xml
124
+ # .idea/misc.xml
125
+ # *.ipr
126
+
127
+ # Sonarlint plugin
128
+ # https://plugins.jetbrains.com/plugin/7973-sonarlint
129
+ .idea/**/sonarlint/
130
+
131
+ # SonarQube Plugin
132
+ # https://plugins.jetbrains.com/plugin/7238-sonarqube-community-plugin
133
+ .idea/**/sonarIssues.xml
134
+
135
+ # Markdown Navigator plugin
136
+ # https://plugins.jetbrains.com/plugin/7896-markdown-navigator-enhanced
137
+ .idea/**/markdown-navigator.xml
138
+ .idea/**/markdown-navigator-enh.xml
139
+ .idea/**/markdown-navigator/
140
+
141
+ # Cache file creation bug
142
+ # See https://youtrack.jetbrains.com/issue/JBR-2257
143
+ .idea/$CACHE_FILE$
144
+
145
+ # CodeStream plugin
146
+ # https://plugins.jetbrains.com/plugin/12206-codestream
147
+ .idea/codestream.xml
148
+
149
+ # Azure Toolkit for IntelliJ plugin
150
+ # https://plugins.jetbrains.com/plugin/8053-azure-toolkit-for-intellij
151
+ .idea/**/azureSettings.xml
152
+
153
+ ### Python ###
154
+ # Byte-compiled / optimized / DLL files
155
+ __pycache__/
156
+ *.py[cod]
157
+ *$py.class
158
+
159
+ # C extensions
160
+ *.so
161
+
162
+ # Distribution / packaging
163
+ .Python
164
+ build/
165
+ develop-eggs/
166
+ dist/
167
+ downloads/
168
+ eggs/
169
+ .eggs/
170
+ lib/
171
+ lib64/
172
+ parts/
173
+ sdist/
174
+ var/
175
+ wheels/
176
+ share/python-wheels/
177
+ *.egg-info/
178
+ .installed.cfg
179
+ *.egg
180
+ MANIFEST
181
+
182
+ # PyInstaller
183
+ # Usually these files are written by a python script from a template
184
+ # before PyInstaller builds the exe, so as to inject date/other infos into it.
185
+ *.manifest
186
+ *.spec
187
+
188
+ # Installer logs
189
+ pip-log.txt
190
+ pip-delete-this-directory.txt
191
+
192
+ # Unit test / coverage reports
193
+ htmlcov/
194
+ .tox/
195
+ .nox/
196
+ .coverage
197
+ .coverage.*
198
+ .cache
199
+ nosetests.xml
200
+ coverage.xml
201
+ *.cover
202
+ *.py,cover
203
+ .hypothesis/
204
+ .pytest_cache/
205
+ cover/
206
+
207
+ # Translations
208
+ *.mo
209
+ *.pot
210
+
211
+ # Django stuff:
212
+ *.log
213
+ local_settings.py
214
+ db.sqlite3
215
+ db.sqlite3-journal
216
+
217
+ # Flask stuff:
218
+ instance/
219
+ .webassets-cache
220
+
221
+ # Scrapy stuff:
222
+ .scrapy
223
+
224
+ # Sphinx documentation
225
+ docs/_build/
226
+
227
+ # PyBuilder
228
+ .pybuilder/
229
+ target/
230
+
231
+ # Jupyter Notebook
232
+ .ipynb_checkpoints
233
+
234
+ # IPython
235
+ profile_default/
236
+ ipython_config.py
237
+
238
+ # pyenv
239
+ # For a library or package, you might want to ignore these files since the code is
240
+ # intended to run in multiple environments; otherwise, check them in:
241
+ # .python-version
242
+
243
+ # pipenv
244
+ # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
245
+ # However, in case of collaboration, if having platform-specific dependencies or dependencies
246
+ # having no cross-platform support, pipenv may install dependencies that don't work, or not
247
+ # install all needed dependencies.
248
+ #Pipfile.lock
249
+
250
+ # poetry
251
+ # Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
252
+ # This is especially recommended for binary packages to ensure reproducibility, and is more
253
+ # commonly ignored for libraries.
254
+ # https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
255
+ #poetry.lock
256
+
257
+ # pdm
258
+ # Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
259
+ #pdm.lock
260
+ # pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
261
+ # in version control.
262
+ # https://pdm.fming.dev/#use-with-ide
263
+ .pdm.toml
264
+
265
+ # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
266
+ __pypackages__/
267
+
268
+ # Celery stuff
269
+ celerybeat-schedule
270
+ celerybeat.pid
271
+
272
+ # SageMath parsed files
273
+ *.sage.py
274
+
275
+ # Environments
276
+ .venv
277
+ env/
278
+ venv/
279
+ ENV/
280
+ env.bak/
281
+ venv.bak/
282
+
283
+ # Spyder project settings
284
+ .spyderproject
285
+ .spyproject
286
+
287
+ # Rope project settings
288
+ .ropeproject
289
+
290
+ # mkdocs documentation
291
+ /site
292
+
293
+ # mypy
294
+ .mypy_cache/
295
+ .dmypy.json
296
+ dmypy.json
297
+
298
+ # Pyre type checker
299
+ .pyre/
300
+
301
+ # pytype static type analyzer
302
+ .pytype/
303
+
304
+ # Cython debug symbols
305
+ cython_debug/
306
+
307
+ # PyCharm
308
+ # JetBrains specific template is maintained in a separate JetBrains.gitignore that can
309
+ # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
310
+ # and can be added to the global gitignore or merged into this file. For a more nuclear
311
+ # option (not recommended) you can uncomment the following to ignore the entire idea folder.
312
+ #.idea/
313
+
314
+ # End of https://www.toptal.com/developers/gitignore/api/python,pycharm,dotenv,macos
315
+
316
+ # Pyenv
317
+ .python-version
318
+
319
+ # Code editors
320
+ .fleet/
321
+ .idea/
322
+ .vscode/
@@ -1,31 +1,30 @@
1
- Metadata-Version: 2.3
1
+ Metadata-Version: 2.4
2
2
  Name: python-injection
3
- Version: 0.12.2
3
+ Version: 0.13.0
4
4
  Summary: Fast and easy dependency injection framework.
5
- Home-page: https://github.com/100nm/python-injection
5
+ Project-URL: Repository, https://github.com/100nm/python-injection
6
+ Author: remimd
6
7
  License: MIT
7
8
  Keywords: dependencies,dependency,inject,injection
8
- Author: remimd
9
- Requires-Python: >=3.12, <4
10
9
  Classifier: Development Status :: 4 - Beta
10
+ Classifier: Intended Audience :: Developers
11
+ Classifier: Natural Language :: English
12
+ Classifier: Operating System :: OS Independent
13
+ Classifier: Programming Language :: Python
14
+ Classifier: Programming Language :: Python :: 3
15
+ Classifier: Programming Language :: Python :: 3 :: Only
11
16
  Classifier: Topic :: Software Development :: Libraries
12
17
  Classifier: Topic :: Software Development :: Libraries :: Application Frameworks
13
18
  Classifier: Topic :: Software Development :: Libraries :: Python Modules
14
19
  Classifier: Topic :: Software Development :: Testing
15
- Classifier: Programming Language :: Python
16
- Classifier: Programming Language :: Python :: 3
17
- Classifier: Programming Language :: Python :: 3 :: Only
18
- Classifier: Operating System :: OS Independent
19
- Classifier: Intended Audience :: Developers
20
- Classifier: Natural Language :: English
21
20
  Classifier: Typing :: Typed
22
- Project-URL: Repository, https://github.com/100nm/python-injection
21
+ Requires-Python: <4,>=3.12
23
22
  Description-Content-Type: text/markdown
24
23
 
25
24
  # python-injection
26
25
 
27
26
  [![CI](https://github.com/100nm/python-injection/actions/workflows/ci.yml/badge.svg)](https://github.com/100nm/python-injection)
28
- [![PyPI](https://img.shields.io/pypi/v/python-injection.svg?color=blue)](https://pypi.org/project/python-injection/)
27
+ [![PyPI](https://img.shields.io/pypi/v/python-injection.svg?color=blue)](https://pypi.org/project/python-injection)
29
28
  [![Ruff](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json)](https://github.com/astral-sh/ruff)
30
29
 
31
30
  Fast and easy dependency injection framework.
@@ -80,9 +79,9 @@ if __name__ == "__main__":
80
79
  ## Resources
81
80
 
82
81
  * [**Basic usage**](https://github.com/100nm/python-injection/tree/prod/documentation/basic-usage.md)
82
+ * [**Scoped dependencies**](https://github.com/100nm/python-injection/tree/prod/documentation/scoped-dependencies.md)
83
83
  * [**Testing**](https://github.com/100nm/python-injection/tree/prod/documentation/testing.md)
84
84
  * [**Advanced usage**](https://github.com/100nm/python-injection/tree/prod/documentation/advanced-usage.md)
85
85
  * [**Utils**](https://github.com/100nm/python-injection/tree/prod/documentation/utils.md)
86
86
  * [**Integrations**](https://github.com/100nm/python-injection/tree/prod/documentation/integrations.md)
87
- * [**Concrete example**](https://github.com/100nm/python-injection/tree/prod/documentation/example)
88
-
87
+ * [**Concrete example**](https://github.com/100nm/python-injection-example)
@@ -1,7 +1,7 @@
1
1
  # python-injection
2
2
 
3
3
  [![CI](https://github.com/100nm/python-injection/actions/workflows/ci.yml/badge.svg)](https://github.com/100nm/python-injection)
4
- [![PyPI](https://img.shields.io/pypi/v/python-injection.svg?color=blue)](https://pypi.org/project/python-injection/)
4
+ [![PyPI](https://img.shields.io/pypi/v/python-injection.svg?color=blue)](https://pypi.org/project/python-injection)
5
5
  [![Ruff](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json)](https://github.com/astral-sh/ruff)
6
6
 
7
7
  Fast and easy dependency injection framework.
@@ -56,8 +56,9 @@ if __name__ == "__main__":
56
56
  ## Resources
57
57
 
58
58
  * [**Basic usage**](https://github.com/100nm/python-injection/tree/prod/documentation/basic-usage.md)
59
+ * [**Scoped dependencies**](https://github.com/100nm/python-injection/tree/prod/documentation/scoped-dependencies.md)
59
60
  * [**Testing**](https://github.com/100nm/python-injection/tree/prod/documentation/testing.md)
60
61
  * [**Advanced usage**](https://github.com/100nm/python-injection/tree/prod/documentation/advanced-usage.md)
61
62
  * [**Utils**](https://github.com/100nm/python-injection/tree/prod/documentation/utils.md)
62
63
  * [**Integrations**](https://github.com/100nm/python-injection/tree/prod/documentation/integrations.md)
63
- * [**Concrete example**](https://github.com/100nm/python-injection/tree/prod/documentation/example)
64
+ * [**Concrete example**](https://github.com/100nm/python-injection-example)
@@ -1,6 +1,7 @@
1
1
  from ._core.descriptors import LazyInstance
2
2
  from ._core.injectables import Injectable
3
3
  from ._core.module import Mode, Module, Priority, mod
4
+ from ._core.scope import adefine_scope, define_scope
4
5
 
5
6
  __all__ = (
6
7
  "Injectable",
@@ -8,16 +9,19 @@ __all__ = (
8
9
  "Mode",
9
10
  "Module",
10
11
  "Priority",
12
+ "adefine_scope",
11
13
  "afind_instance",
12
14
  "aget_instance",
13
15
  "aget_lazy_instance",
14
16
  "constant",
17
+ "define_scope",
15
18
  "find_instance",
16
19
  "get_instance",
17
20
  "get_lazy_instance",
18
21
  "inject",
19
22
  "injectable",
20
23
  "mod",
24
+ "scoped",
21
25
  "set_constant",
22
26
  "should_be_injectable",
23
27
  "singleton",
@@ -32,6 +36,7 @@ get_instance = mod().get_instance
32
36
  get_lazy_instance = mod().get_lazy_instance
33
37
  inject = mod().inject
34
38
  injectable = mod().injectable
39
+ scoped = mod().scoped
35
40
  set_constant = mod().set_constant
36
41
  should_be_injectable = mod().should_be_injectable
37
42
  singleton = mod().singleton
@@ -1,17 +1,9 @@
1
1
  from abc import abstractmethod
2
- from collections.abc import Awaitable, Callable
3
- from contextlib import ContextDecorator
2
+ from collections.abc import AsyncIterator, Awaitable, Callable, Iterator
3
+ from contextlib import asynccontextmanager, contextmanager
4
4
  from enum import Enum
5
5
  from logging import Logger
6
- from typing import (
7
- Any,
8
- ContextManager,
9
- Protocol,
10
- Self,
11
- final,
12
- overload,
13
- runtime_checkable,
14
- )
6
+ from typing import Any, Final, Protocol, Self, final, overload, runtime_checkable
15
7
 
16
8
  from ._core.common.invertible import Invertible as _Invertible
17
9
  from ._core.common.type import InputType as _InputType
@@ -19,27 +11,47 @@ from ._core.common.type import TypeInfo as _TypeInfo
19
11
  from ._core.module import InjectableFactory as _InjectableFactory
20
12
  from ._core.module import ModeStr, PriorityStr
21
13
 
22
- _: Module = ...
14
+ __MODULE: Final[Module] = ...
23
15
 
24
- afind_instance = _.afind_instance
25
- aget_instance = _.aget_instance
26
- aget_lazy_instance = _.aget_lazy_instance
27
- constant = _.constant
28
- find_instance = _.find_instance
29
- get_instance = _.get_instance
30
- get_lazy_instance = _.get_lazy_instance
31
- inject = _.inject
32
- injectable = _.injectable
33
- set_constant = _.set_constant
34
- should_be_injectable = _.should_be_injectable
35
- singleton = _.singleton
36
-
37
- del _
16
+ afind_instance = __MODULE.afind_instance
17
+ aget_instance = __MODULE.aget_instance
18
+ aget_lazy_instance = __MODULE.aget_lazy_instance
19
+ constant = __MODULE.constant
20
+ find_instance = __MODULE.find_instance
21
+ get_instance = __MODULE.get_instance
22
+ get_lazy_instance = __MODULE.get_lazy_instance
23
+ inject = __MODULE.inject
24
+ injectable = __MODULE.injectable
25
+ scoped = __MODULE.scoped
26
+ set_constant = __MODULE.set_constant
27
+ should_be_injectable = __MODULE.should_be_injectable
28
+ singleton = __MODULE.singleton
38
29
 
30
+ @asynccontextmanager
31
+ def adefine_scope(name: str, *, shared: bool = ...) -> AsyncIterator[None]: ...
32
+ @contextmanager
33
+ def define_scope(name: str, *, shared: bool = ...) -> Iterator[None]: ...
39
34
  def mod(name: str = ..., /) -> Module:
40
35
  """
41
36
  Short syntax for `Module.from_name`.
42
37
  """
38
+ @runtime_checkable
39
+ class Injectable[T](Protocol):
40
+ @property
41
+ def is_locked(self) -> bool: ...
42
+ def unlock(self) -> None: ...
43
+ @abstractmethod
44
+ async def aget_instance(self) -> T: ...
45
+ @abstractmethod
46
+ def get_instance(self) -> T: ...
47
+
48
+ class LazyInstance[T]:
49
+ def __init__(self, cls: _InputType[T], module: Module = ...) -> None: ...
50
+ @overload
51
+ def __get__(self, instance: object, owner: type | None = ...) -> T: ...
52
+ @overload
53
+ def __get__(self, instance: None = ..., owner: type | None = ...) -> Self: ...
54
+
43
55
  @final
44
56
  class Module:
45
57
  """
@@ -94,6 +106,21 @@ class Module:
94
106
  always be the same.
95
107
  """
96
108
 
109
+ def scoped[**P, T](
110
+ self,
111
+ scope_name: str,
112
+ /,
113
+ *,
114
+ inject: bool = ...,
115
+ on: _TypeInfo[T] = (),
116
+ mode: Mode | ModeStr = ...,
117
+ ) -> Any:
118
+ """
119
+ Decorator applicable to a class or function or generator function. It is used
120
+ to indicate how the scoped instance will be constructed. At injection time, the
121
+ injected instance is retrieved from the scope.
122
+ """
123
+
97
124
  def should_be_injectable[T](self, wrapped: type[T] = ..., /) -> Any:
98
125
  """
99
126
  Decorator applicable to a class. It is used to specify whether an injectable
@@ -233,12 +260,13 @@ class Module:
233
260
  Function to remove a module in use.
234
261
  """
235
262
 
263
+ @contextmanager
236
264
  def use_temporarily(
237
265
  self,
238
266
  module: Module,
239
267
  *,
240
268
  priority: Priority | PriorityStr = ...,
241
- ) -> ContextManager[None] | ContextDecorator:
269
+ ) -> Iterator[None]:
242
270
  """
243
271
  Context manager or decorator for temporary use of a module.
244
272
  """
@@ -275,30 +303,13 @@ class Module:
275
303
  Class method for getting the default module.
276
304
  """
277
305
 
278
- @final
279
- class Priority(Enum):
280
- LOW = ...
281
- HIGH = ...
282
-
283
- @runtime_checkable
284
- class Injectable[T](Protocol):
285
- @property
286
- def is_locked(self) -> bool: ...
287
- def unlock(self) -> None: ...
288
- @abstractmethod
289
- async def aget_instance(self) -> T: ...
290
- @abstractmethod
291
- def get_instance(self) -> T: ...
292
-
293
306
  @final
294
307
  class Mode(Enum):
295
308
  FALLBACK = ...
296
309
  NORMAL = ...
297
310
  OVERRIDE = ...
298
311
 
299
- class LazyInstance[T]:
300
- def __init__(self, cls: _InputType[T], module: Module = ...) -> None: ...
301
- @overload
302
- def __get__(self, instance: object, owner: type | None = ...) -> T: ...
303
- @overload
304
- def __get__(self, instance: None = ..., owner: type | None = ...) -> Self: ...
312
+ @final
313
+ class Priority(Enum):
314
+ LOW = ...
315
+ HIGH = ...
@@ -1,7 +1,17 @@
1
+ import asyncio
1
2
  from abc import abstractmethod
2
- from collections.abc import Awaitable, Callable, Generator
3
+ from collections.abc import Awaitable, Callable, Coroutine, Generator
3
4
  from dataclasses import dataclass
4
- from typing import Any, NoReturn, Protocol, runtime_checkable
5
+ from typing import Any, Protocol, runtime_checkable
6
+
7
+
8
+ def run_sync[T](coroutine: Coroutine[Any, Any, T]) -> T:
9
+ loop = asyncio.get_event_loop()
10
+
11
+ try:
12
+ return loop.run_until_complete(coroutine)
13
+ finally:
14
+ coroutine.close()
5
15
 
6
16
 
7
17
  @dataclass(repr=False, eq=False, frozen=True, slots=True)
@@ -27,15 +37,13 @@ class Caller[**P, T](Protocol):
27
37
 
28
38
  @dataclass(repr=False, eq=False, frozen=True, slots=True)
29
39
  class AsyncCaller[**P, T](Caller[P, T]):
30
- callable: Callable[P, Awaitable[T]]
40
+ callable: Callable[P, Coroutine[Any, Any, T]]
31
41
 
32
42
  async def acall(self, /, *args: P.args, **kwargs: P.kwargs) -> T:
33
43
  return await self.callable(*args, **kwargs)
34
44
 
35
- def call(self, /, *args: P.args, **kwargs: P.kwargs) -> NoReturn:
36
- raise RuntimeError(
37
- "Synchronous call isn't supported for an asynchronous Callable."
38
- )
45
+ def call(self, /, *args: P.args, **kwargs: P.kwargs) -> T:
46
+ return run_sync(self.callable(*args, **kwargs))
39
47
 
40
48
 
41
49
  @dataclass(repr=False, eq=False, frozen=True, slots=True)
@@ -25,7 +25,7 @@ class EventChannel:
25
25
  @contextmanager
26
26
  def dispatch(self, event: Event) -> Iterator[None]:
27
27
  with ExitStack() as stack:
28
- for listener in tuple(self.__listeners):
28
+ for listener in self.__listeners:
29
29
  context_manager = listener.on_event(event)
30
30
 
31
31
  if context_manager is None:
@@ -0,0 +1,5 @@
1
+ from uuid import uuid4
2
+
3
+
4
+ def new_short_key() -> str:
5
+ return uuid4().hex[:7]
@@ -1,4 +1,12 @@
1
- from collections.abc import Callable, Iterable, Iterator
1
+ from collections.abc import (
2
+ AsyncGenerator,
3
+ AsyncIterable,
4
+ AsyncIterator,
5
+ Callable,
6
+ Generator,
7
+ Iterable,
8
+ Iterator,
9
+ )
2
10
  from inspect import isfunction
3
11
  from types import GenericAlias, UnionType
4
12
  from typing import (
@@ -23,7 +31,7 @@ def get_return_types(*args: TypeInfo[Any]) -> Iterator[InputType[Any]]:
23
31
  ):
24
32
  inner_args = arg
25
33
 
26
- elif isfunction(arg) and (return_type := get_type_hints(arg).get("return")):
34
+ elif isfunction(arg) and (return_type := get_return_hint(arg)):
27
35
  inner_args = (return_type,)
28
36
 
29
37
  else:
@@ -33,6 +41,29 @@ def get_return_types(*args: TypeInfo[Any]) -> Iterator[InputType[Any]]:
33
41
  yield from get_return_types(*inner_args)
34
42
 
35
43
 
44
+ def get_return_hint[T](function: Callable[..., T]) -> InputType[T] | None:
45
+ return get_type_hints(function).get("return")
46
+
47
+
48
+ def get_yield_hint[T](
49
+ function: Callable[..., Iterator[T]] | Callable[..., AsyncIterator[T]],
50
+ ) -> InputType[T] | None:
51
+ return_type = get_return_hint(function)
52
+
53
+ if get_origin(return_type) not in {
54
+ AsyncGenerator,
55
+ AsyncIterable,
56
+ AsyncIterator,
57
+ Generator,
58
+ Iterable,
59
+ Iterator,
60
+ }:
61
+ return None
62
+
63
+ args = get_args(return_type)
64
+ return next(iter(args), None)
65
+
66
+
36
67
  def standardize_types(
37
68
  *types: InputType[Any],
38
69
  with_origin: bool = False,