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

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 (30) hide show
  1. python_injection-0.13.0/.gitignore +322 -0
  2. {python_injection-0.12.3 → python_injection-0.13.0}/PKG-INFO +14 -15
  3. {python_injection-0.12.3 → python_injection-0.13.0}/README.md +3 -2
  4. {python_injection-0.12.3 → python_injection-0.13.0}/injection/__init__.py +5 -0
  5. {python_injection-0.12.3 → python_injection-0.13.0}/injection/__init__.pyi +59 -48
  6. {python_injection-0.12.3 → python_injection-0.13.0}/injection/_core/common/asynchronous.py +12 -11
  7. {python_injection-0.12.3 → 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.3 → 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.3 → python_injection-0.13.0}/injection/_core/module.py +109 -46
  12. python_injection-0.13.0/injection/_core/scope.py +215 -0
  13. {python_injection-0.12.3 → 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.3 → 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.3 → python_injection-0.13.0}/injection/utils.py +7 -2
  18. {python_injection-0.12.3 → python_injection-0.13.0}/pyproject.toml +39 -32
  19. python_injection-0.12.3/injection/_core/common/threading.py +0 -11
  20. python_injection-0.12.3/injection/_core/injectables.py +0 -98
  21. python_injection-0.12.3/injection/integrations/fastapi.py +0 -63
  22. python_injection-0.12.3/injection/testing/__init__.pyi +0 -14
  23. {python_injection-0.12.3 → python_injection-0.13.0}/injection/_core/__init__.py +0 -0
  24. {python_injection-0.12.3 → python_injection-0.13.0}/injection/_core/common/__init__.py +0 -0
  25. {python_injection-0.12.3 → python_injection-0.13.0}/injection/_core/common/invertible.py +0 -0
  26. {python_injection-0.12.3 → python_injection-0.13.0}/injection/_core/common/lazy.py +0 -0
  27. {python_injection-0.12.3 → python_injection-0.13.0}/injection/_core/descriptors.py +0 -0
  28. {python_injection-0.12.3 → python_injection-0.13.0}/injection/_core/hook.py +0 -0
  29. {python_injection-0.12.3 → python_injection-0.13.0}/injection/integrations/__init__.py +0 -0
  30. {python_injection-0.12.3 → 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.3
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,10 +1,19 @@
1
1
  import asyncio
2
2
  from abc import abstractmethod
3
- from collections.abc import Awaitable, Callable, Generator
3
+ from collections.abc import Awaitable, Callable, Coroutine, Generator
4
4
  from dataclasses import dataclass
5
5
  from typing import Any, Protocol, runtime_checkable
6
6
 
7
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()
15
+
16
+
8
17
  @dataclass(repr=False, eq=False, frozen=True, slots=True)
9
18
  class SimpleAwaitable[T](Awaitable[T]):
10
19
  callable: Callable[..., Awaitable[T]]
@@ -28,21 +37,13 @@ class Caller[**P, T](Protocol):
28
37
 
29
38
  @dataclass(repr=False, eq=False, frozen=True, slots=True)
30
39
  class AsyncCaller[**P, T](Caller[P, T]):
31
- callable: Callable[P, Awaitable[T]]
40
+ callable: Callable[P, Coroutine[Any, Any, T]]
32
41
 
33
42
  async def acall(self, /, *args: P.args, **kwargs: P.kwargs) -> T:
34
43
  return await self.callable(*args, **kwargs)
35
44
 
36
45
  def call(self, /, *args: P.args, **kwargs: P.kwargs) -> T:
37
- loop = asyncio.get_event_loop()
38
-
39
- if loop.is_running():
40
- raise RuntimeError(
41
- "Can't call an asynchronous function in a synchronous context."
42
- )
43
-
44
- coroutine = self.callable(*args, **kwargs)
45
- return loop.run_until_complete(coroutine)
46
+ return run_sync(self.callable(*args, **kwargs))
46
47
 
47
48
 
48
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,