ashka-lifecycle 0.1.0b3__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.
@@ -0,0 +1,58 @@
1
+ Metadata-Version: 2.3
2
+ Name: ashka-lifecycle
3
+ Version: 0.1.0b3
4
+ Summary: dishka lifecycle support
5
+ Author: handsome-Druid
6
+ Author-email: handsome-Druid <102826168@qq.com>
7
+ Requires-Dist: dishka>=1.9.0,<=1.10.1
8
+ Requires-Python: >=3.10, <3.15
9
+ Description-Content-Type: text/markdown
10
+
11
+ # ashka
12
+
13
+ [![CI](https://github.com/handsome-Druid/ashka/actions/workflows/ci.yml/badge.svg)](https://github.com/handsome-Druid/ashka/actions/workflows/ci.yml)
14
+ [![Publish](https://github.com/handsome-Druid/ashka/actions/workflows/publish.yml/badge.svg)](https://github.com/handsome-Druid/ashka/actions/workflows/publish.yml)
15
+
16
+ `ashka` extends `reagento/dishka` with async/sync application lifecycle support.
17
+
18
+ The `a` prefix lets `ashka` sort before `dishka`, helping ensure the correct
19
+ import order. The name also means async application lifecycle support, which can
20
+ speed up startup compared with serially initializing components in a lifespan
21
+ function.
22
+
23
+ ## Documentation
24
+
25
+ - [English](https://github.com/handsome-Druid/ashka/blob/master/docs/en/README.md)
26
+ - [中文](https://github.com/handsome-Druid/ashka/blob/master/docs/zh/README.md)
27
+
28
+ ## Current Features
29
+
30
+ - Pass `AshkaScope.BOOTSTRAP` to the `scope` parameter of `ashka.provide`, for example
31
+ `@ashka.provide(scope=ashka.AshkaScope.BOOTSTRAP)`, to register a dependency in the APP scope
32
+ and create it when the container is initialized. Both synchronous and
33
+ asynchronous containers can be initialized explicitly with `container.init()`
34
+ or automatically by entering the container context manager.
35
+ - Each ashka integration provides a consistent `get_container` function. It
36
+ retrieves the container passed to `setup_dishka` from the integrated
37
+ framework application, router, broker, context, or other supported object.
38
+ - The central `ashka.integrations.setup_dishka` and
39
+ `ashka.integrations.get_container` functions automatically dispatch to the
40
+ corresponding integration based on the type of the object passed to them.
41
+ - Integrations are available for Aiogram, Aiohttp, ARQ, Celery, Click, FastAPI,
42
+ Flask, Litestar, Sanic, Starlette, Taskiq, and Telebot. FastStream is
43
+ available as a manual opt-in integration.
44
+
45
+ ## Patched Implementations
46
+
47
+ - `dishka.**.make_container` -> `ashka.**.make_container`
48
+ - `dishka.**.make_async_container` -> `ashka.**.make_async_container`
49
+ - `dishka.integrations.*.setup_dishka` -> `ashka.integrations.*.setup_dishka`
50
+
51
+ These monkey patches keep the API paths consistent with upstream dishka and
52
+ reduce the cognitive load during migration. Import `ashka` before importing
53
+ these dishka APIs so existing projects can keep their current imports without
54
+ immediate code changes. This is a migration aid rather than a stable usage
55
+ pattern; migrate imports from dishka to ashka incrementally when convenient
56
+ instead of relying on the monkey patches indefinitely.
57
+
58
+ See [FastStream support](https://github.com/handsome-Druid/ashka/blob/master/docs/en/support/faststream.md) for its support status and manual opt-in.
@@ -0,0 +1,48 @@
1
+ # ashka
2
+
3
+ [![CI](https://github.com/handsome-Druid/ashka/actions/workflows/ci.yml/badge.svg)](https://github.com/handsome-Druid/ashka/actions/workflows/ci.yml)
4
+ [![Publish](https://github.com/handsome-Druid/ashka/actions/workflows/publish.yml/badge.svg)](https://github.com/handsome-Druid/ashka/actions/workflows/publish.yml)
5
+
6
+ `ashka` extends `reagento/dishka` with async/sync application lifecycle support.
7
+
8
+ The `a` prefix lets `ashka` sort before `dishka`, helping ensure the correct
9
+ import order. The name also means async application lifecycle support, which can
10
+ speed up startup compared with serially initializing components in a lifespan
11
+ function.
12
+
13
+ ## Documentation
14
+
15
+ - [English](https://github.com/handsome-Druid/ashka/blob/master/docs/en/README.md)
16
+ - [中文](https://github.com/handsome-Druid/ashka/blob/master/docs/zh/README.md)
17
+
18
+ ## Current Features
19
+
20
+ - Pass `AshkaScope.BOOTSTRAP` to the `scope` parameter of `ashka.provide`, for example
21
+ `@ashka.provide(scope=ashka.AshkaScope.BOOTSTRAP)`, to register a dependency in the APP scope
22
+ and create it when the container is initialized. Both synchronous and
23
+ asynchronous containers can be initialized explicitly with `container.init()`
24
+ or automatically by entering the container context manager.
25
+ - Each ashka integration provides a consistent `get_container` function. It
26
+ retrieves the container passed to `setup_dishka` from the integrated
27
+ framework application, router, broker, context, or other supported object.
28
+ - The central `ashka.integrations.setup_dishka` and
29
+ `ashka.integrations.get_container` functions automatically dispatch to the
30
+ corresponding integration based on the type of the object passed to them.
31
+ - Integrations are available for Aiogram, Aiohttp, ARQ, Celery, Click, FastAPI,
32
+ Flask, Litestar, Sanic, Starlette, Taskiq, and Telebot. FastStream is
33
+ available as a manual opt-in integration.
34
+
35
+ ## Patched Implementations
36
+
37
+ - `dishka.**.make_container` -> `ashka.**.make_container`
38
+ - `dishka.**.make_async_container` -> `ashka.**.make_async_container`
39
+ - `dishka.integrations.*.setup_dishka` -> `ashka.integrations.*.setup_dishka`
40
+
41
+ These monkey patches keep the API paths consistent with upstream dishka and
42
+ reduce the cognitive load during migration. Import `ashka` before importing
43
+ these dishka APIs so existing projects can keep their current imports without
44
+ immediate code changes. This is a migration aid rather than a stable usage
45
+ pattern; migrate imports from dishka to ashka incrementally when convenient
46
+ instead of relying on the monkey patches indefinitely.
47
+
48
+ See [FastStream support](https://github.com/handsome-Druid/ashka/blob/master/docs/en/support/faststream.md) for its support status and manual opt-in.
@@ -0,0 +1,47 @@
1
+ [project]
2
+ name = "ashka-lifecycle"
3
+ version = "0.1.0b3"
4
+ description = "dishka lifecycle support"
5
+ readme = "README.md"
6
+ requires-python = ">=3.10,<3.15"
7
+ dependencies = ["dishka>=1.9.0,<=1.10.1"]
8
+
9
+ [[project.authors]]
10
+ name = "handsome-Druid"
11
+ email = "102826168@qq.com"
12
+
13
+ [build-system]
14
+ requires = ["uv_build>=0.12.5,<0.13.0"]
15
+ build-backend = "uv_build"
16
+
17
+ [dependency-groups]
18
+ dev = [
19
+ "ashka>=0.8.0",
20
+ "fastapi>=0.141.1",
21
+ "httpx2>=2.12.0",
22
+ "pyright>=1.1.411",
23
+ "pytest>=9.1.1",
24
+ "pytest-asyncio>=1.4.0",
25
+ "pytest-cov>=7.1.0",
26
+ "pytest-xdist>=3.8.0",
27
+ "ruff>=0.16.4",
28
+ ]
29
+
30
+ [[tool.uv.index]]
31
+ name = "testpypi"
32
+ url = "https://test.pypi.org/simple/"
33
+ publish-url = "https://test.pypi.org/legacy/"
34
+ explicit = true
35
+
36
+ [tool.coverage.run]
37
+ branch = true
38
+ patch = ["subprocess"]
39
+
40
+ [tool.coverage.report]
41
+ fail_under = 100
42
+
43
+ [tool.pytest.ini_options]
44
+ addopts = "-n auto"
45
+
46
+ [tool.ruff]
47
+ exclude = ["typings"]
@@ -0,0 +1,48 @@
1
+ [project]
2
+ name = "ashka-lifecycle"
3
+ version = "0.1.0b3"
4
+ description = "dishka lifecycle support"
5
+ readme = "README.md"
6
+ authors = [
7
+ { name = "handsome-Druid", email = "102826168@qq.com" }
8
+ ]
9
+ requires-python = ">=3.10,<3.15"
10
+ dependencies = [
11
+ "dishka>=1.9.0,<=1.10.1",
12
+ ]
13
+
14
+ [build-system]
15
+ requires = ["uv_build>=0.12.5,<0.13.0"]
16
+ build-backend = "uv_build"
17
+
18
+ [dependency-groups]
19
+ dev = [
20
+ "ashka>=0.8.0",
21
+ "fastapi>=0.141.1",
22
+ "httpx2>=2.12.0",
23
+ "pyright>=1.1.411",
24
+ "pytest>=9.1.1",
25
+ "pytest-asyncio>=1.4.0",
26
+ "pytest-cov>=7.1.0",
27
+ "pytest-xdist>=3.8.0",
28
+ "ruff>=0.16.4",
29
+ ]
30
+
31
+ [[tool.uv.index]]
32
+ name = "testpypi"
33
+ url = "https://test.pypi.org/simple/"
34
+ publish-url = "https://test.pypi.org/legacy/"
35
+ explicit = true
36
+
37
+ [tool.coverage.run]
38
+ branch = true
39
+ patch = ["subprocess"]
40
+
41
+ [tool.coverage.report]
42
+ fail_under = 100
43
+
44
+ [tool.pytest.ini_options]
45
+ addopts = "-n auto"
46
+
47
+ [tool.ruff]
48
+ exclude = ["typings"]
@@ -0,0 +1,11 @@
1
+ __all__ = [
2
+ "AshkaScope",
3
+ "make_async_container",
4
+ "make_container",
5
+ "provide",
6
+ ]
7
+
8
+ from .async_container import make_async_container
9
+ from .container import make_container
10
+ from .entities.scope import AshkaScope
11
+ from .provider import provide # pyright: ignore[reportUnknownVariableType]
@@ -0,0 +1,10 @@
1
+ """
2
+ This type stub file was generated by pyright.
3
+ """
4
+
5
+ from .async_container import make_async_container
6
+ from .container import make_container
7
+ from .entities.scope import AshkaScope
8
+ from .provider import provide
9
+
10
+ __all__ = ["AshkaScope", "make_async_container", "make_container", "provide"]
@@ -0,0 +1,62 @@
1
+ from asyncio import gather
2
+ from typing import Any, cast
3
+
4
+ import dishka
5
+ from dishka import AsyncContainer
6
+ from dishka.provider import BaseProvider
7
+
8
+ from ashka_lifecycle.entities.bootstrap import (
9
+ bootstrap_keys_by_container,
10
+ bootstrap_sources, # pyright: ignore[reportUnknownVariableType]
11
+ )
12
+
13
+ __all__ = ["make_async_container"]
14
+
15
+ _aenter = AsyncContainer.__aenter__
16
+
17
+
18
+ async def __aenter__(self: AsyncContainer):
19
+ aenter = await _aenter(self)
20
+
21
+ if self in bootstrap_keys_by_container:
22
+ await gather(
23
+ *(
24
+ self.get(key.type_hint, key.component)
25
+ for key in bootstrap_keys_by_container[self]
26
+ )
27
+ )
28
+
29
+ return aenter
30
+
31
+
32
+ class AsyncContainerType(AsyncContainer):
33
+ __slots__ = ()
34
+
35
+ __aenter__ = __aenter__
36
+
37
+ async def init(self) -> None:
38
+ await self.__aenter__()
39
+
40
+
41
+ AsyncContainer.__aenter__ = __aenter__
42
+ AsyncContainer.init = AsyncContainerType.init # pyright: ignore[reportAttributeAccessIssue]
43
+
44
+ _make_async_container = dishka.make_async_container
45
+
46
+
47
+ def make_async_container(*providers: BaseProvider, **kwargs: Any) -> AsyncContainerType:
48
+ bootstrap_keys_by_container[
49
+ async_container := _make_async_container(*providers, **kwargs)
50
+ ] = [
51
+ factory.provides.with_component(provider.component)
52
+ for provider in providers
53
+ for factory in provider.factories
54
+ if getattr(factory.source, "__func__", factory.source) in bootstrap_sources
55
+ ]
56
+
57
+ return cast(AsyncContainerType, async_container)
58
+
59
+
60
+ dishka.make_async_container = dishka.async_container.make_async_container = (
61
+ make_async_container
62
+ )
@@ -0,0 +1,25 @@
1
+ """
2
+ This type stub file was generated by pyright.
3
+ """
4
+
5
+ from typing import Any
6
+ from dishka import AsyncContainer
7
+ from dishka.provider import BaseProvider
8
+
9
+ __all__ = ["make_async_container"]
10
+ _aenter = ...
11
+ async def __aenter__(self: AsyncContainer): # -> AsyncContainer:
12
+ ...
13
+
14
+ class AsyncContainerType(AsyncContainer):
15
+ __slots__ = ...
16
+ __aenter__ = ...
17
+ async def init(self) -> None:
18
+ ...
19
+
20
+
21
+
22
+ _make_async_container = ...
23
+ def make_async_container(*providers: BaseProvider, **kwargs: Any) -> AsyncContainerType:
24
+ ...
25
+
@@ -0,0 +1,53 @@
1
+ from typing import Any, cast
2
+
3
+ import dishka
4
+ from dishka import Container
5
+ from dishka.provider import BaseProvider
6
+
7
+ from ashka_lifecycle.entities.bootstrap import (
8
+ bootstrap_keys_by_container,
9
+ bootstrap_sources, # pyright: ignore[reportUnknownVariableType]
10
+ )
11
+
12
+ __all__ = ["make_container"]
13
+
14
+ _enter = Container.__enter__
15
+
16
+
17
+ def __enter__(self: Container):
18
+ enter = _enter(self)
19
+
20
+ if self in bootstrap_keys_by_container:
21
+ for key in bootstrap_keys_by_container[self]:
22
+ self.get(key.type_hint, key.component)
23
+
24
+ return enter
25
+
26
+
27
+ class ContainerType(Container):
28
+ __slots__ = ()
29
+
30
+ __enter__ = __enter__
31
+
32
+ def init(self) -> None:
33
+ self.__enter__()
34
+
35
+
36
+ Container.__enter__ = __enter__
37
+ Container.init = ContainerType.init # pyright: ignore[reportAttributeAccessIssue]
38
+
39
+ _make_container = dishka.make_container
40
+
41
+
42
+ def make_container(*providers: BaseProvider, **kwargs: Any) -> ContainerType:
43
+ bootstrap_keys_by_container[container := _make_container(*providers, **kwargs)] = [
44
+ factory.provides.with_component(provider.component)
45
+ for provider in providers
46
+ for factory in provider.factories
47
+ if getattr(factory.source, "__func__", factory.source) in bootstrap_sources
48
+ ]
49
+
50
+ return cast(ContainerType, container)
51
+
52
+
53
+ dishka.make_container = dishka.container.make_container = make_container
@@ -0,0 +1,25 @@
1
+ """
2
+ This type stub file was generated by pyright.
3
+ """
4
+
5
+ from typing import Any
6
+ from dishka import Container
7
+ from dishka.provider import BaseProvider
8
+
9
+ __all__ = ["make_container"]
10
+ _enter = ...
11
+ def __enter__(self: Container): # -> Container:
12
+ ...
13
+
14
+ class ContainerType(Container):
15
+ __slots__ = ...
16
+ __enter__ = ...
17
+ def init(self) -> None:
18
+ ...
19
+
20
+
21
+
22
+ _make_container = ...
23
+ def make_container(*providers: BaseProvider, **kwargs: Any) -> ContainerType:
24
+ ...
25
+
@@ -0,0 +1,23 @@
1
+ from weakref import WeakKeyDictionary, WeakSet
2
+
3
+ from dishka import AsyncContainer, Container, DependencyKey
4
+ from dishka.provider.make_factory import ProvideSource
5
+
6
+ bootstrap_sources: WeakSet[ProvideSource] = WeakSet() # pyright: ignore[reportUnknownVariableType]
7
+
8
+ if all(
9
+ hasattr(Container_, "__weakref__") for Container_ in (Container, AsyncContainer)
10
+ ):
11
+ bootstrap_keys_by_container: ( # pragma: nocover
12
+ WeakKeyDictionary[
13
+ Container | AsyncContainer,
14
+ list[DependencyKey],
15
+ ]
16
+ | dict[
17
+ Container | AsyncContainer,
18
+ list[DependencyKey],
19
+ ]
20
+ ) = WeakKeyDictionary()
21
+
22
+ else:
23
+ bootstrap_keys_by_container = {} # pragma: nocover
@@ -0,0 +1,13 @@
1
+ """
2
+ This type stub file was generated by pyright.
3
+ """
4
+
5
+ from weakref import WeakKeyDictionary, WeakSet
6
+ from dishka import AsyncContainer, Container, DependencyKey
7
+ from dishka.provider.make_factory import ProvideSource
8
+
9
+ bootstrap_sources: WeakSet[ProvideSource] = ...
10
+ if all(hasattr(Container_, "__weakref__") for Container_ in (Container, AsyncContainer)):
11
+ bootstrap_keys_by_container: (WeakKeyDictionary[Container | AsyncContainer, list[DependencyKey],] | dict[Container | AsyncContainer, list[DependencyKey],]) = ...
12
+ else:
13
+ bootstrap_keys_by_container = ...
@@ -0,0 +1,7 @@
1
+ from enum import Enum
2
+
3
+ __all__ = ["AshkaScope"]
4
+
5
+
6
+ class AshkaScope(Enum):
7
+ BOOTSTRAP = "BOOTSTRAP"
@@ -0,0 +1,11 @@
1
+ """
2
+ This type stub file was generated by pyright.
3
+ """
4
+
5
+ from enum import Enum
6
+
7
+ __all__ = ["AshkaScope"]
8
+ class AshkaScope(Enum):
9
+ BOOTSTRAP = ...
10
+
11
+
@@ -0,0 +1,3 @@
1
+ from .make_factory import provide # pyright: ignore[reportUnknownVariableType]
2
+
3
+ __all__ = ["provide"]
@@ -0,0 +1,7 @@
1
+ """
2
+ This type stub file was generated by pyright.
3
+ """
4
+
5
+ from .make_factory import provide
6
+
7
+ __all__ = ["provide"]
@@ -0,0 +1,54 @@
1
+ from collections.abc import Callable
2
+ from typing import Any, overload
3
+
4
+ from dishka import BaseScope, Scope
5
+ from dishka import provide as _provide # pyright: ignore[reportUnknownVariableType]
6
+ from dishka.dependency_source.composite import CompositeDependencySource
7
+ from dishka.provider.make_factory import ProvideSource
8
+
9
+ from ashka_lifecycle.entities.bootstrap import (
10
+ bootstrap_sources, # pyright: ignore[reportUnknownVariableType]
11
+ )
12
+ from ashka_lifecycle.entities.scope import AshkaScope
13
+
14
+ __all__ = ["provide"]
15
+
16
+
17
+ @overload
18
+ def provide(
19
+ *, scope: BaseScope | AshkaScope | None = None, **kwargs: Any
20
+ ) -> Callable[[Callable[..., Any]], CompositeDependencySource]: ...
21
+
22
+
23
+ @overload
24
+ def provide(
25
+ source: ProvideSource, # pyright: ignore[reportUnknownParameterType]
26
+ *,
27
+ scope: BaseScope | AshkaScope | None = None,
28
+ **kwargs: Any,
29
+ ) -> CompositeDependencySource: ...
30
+
31
+
32
+ def provide(
33
+ source: ProvideSource | None = None, # pyright: ignore[reportUnknownParameterType]
34
+ *,
35
+ scope: BaseScope | AshkaScope | None = None,
36
+ **kwargs: Any,
37
+ ) -> (
38
+ CompositeDependencySource
39
+ | Callable[
40
+ [Callable[..., Any]],
41
+ CompositeDependencySource,
42
+ ]
43
+ ):
44
+ if scope is not AshkaScope.BOOTSTRAP:
45
+ return _provide(source, scope=scope, **kwargs)
46
+
47
+ def scoped(source: ProvideSource): # pyright: ignore[reportUnknownParameterType]
48
+ bootstrap_sources.add(getattr(source, "__func__", source)) # pyright: ignore[reportUnknownArgumentType, reportUnknownMemberType]
49
+ return _provide(source, scope=Scope.APP, **kwargs)
50
+
51
+ if source is not None:
52
+ return scoped(source)
53
+
54
+ return scoped # pyright: ignore[reportUnknownVariableType]
@@ -0,0 +1,23 @@
1
+ """
2
+ This type stub file was generated by pyright.
3
+ """
4
+
5
+ from collections.abc import Callable
6
+ from typing import Any, overload
7
+ from dishka import BaseScope
8
+ from dishka.dependency_source.composite import CompositeDependencySource
9
+ from dishka.provider.make_factory import ProvideSource
10
+ from ashka_lifecycle.entities.scope import AshkaScope
11
+
12
+ __all__ = ["provide"]
13
+ @overload
14
+ def provide(*, scope: BaseScope | AshkaScope | None = ..., **kwargs: Any) -> Callable[[Callable[..., Any]], CompositeDependencySource]:
15
+ ...
16
+
17
+ @overload
18
+ def provide(source: ProvideSource, *, scope: BaseScope | AshkaScope | None = ..., **kwargs: Any) -> CompositeDependencySource:
19
+ ...
20
+
21
+ def provide(source: ProvideSource | None = ..., *, scope: BaseScope | AshkaScope | None = ..., **kwargs: Any) -> (CompositeDependencySource | Callable[[Callable[..., Any]], CompositeDependencySource,]):
22
+ ...
23
+
File without changes