Skip to content

Commit

Permalink
Fix context manager error (#10)
Browse files Browse the repository at this point in the history
  • Loading branch information
yakimka authored Apr 23, 2024
1 parent 616543b commit 91c8105
Show file tree
Hide file tree
Showing 4 changed files with 30 additions and 11 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,7 @@ We follow [Semantic Versions](https://semver.org/).
## Version 0.1.0

- Initial release

## Version 0.1.1

- Fix context manager error
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
FROM python:3.11-slim-bullseye as builder

ARG WHEEL=picodi-0.1.0-py3-none-any.whl
ARG WHEEL=picodi-0.1.1-py3-none-any.whl
ENV VENV=/venv
ENV PATH="$VENV/bin:$PATH"

Expand Down
33 changes: 24 additions & 9 deletions picodi/picodi.py
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,7 @@ def get_db():
return fn


def init_resources() -> Awaitable:
def init_resources() -> Awaitable | None:
"""
Call this function to close all resources. Usually, it should be called
when your application is shutting down.
Expand All @@ -177,42 +177,49 @@ def init_resources() -> Awaitable:
else:
_get_value_from_depends(depends, _exit_stack)

return asyncio.gather(*async_resources)
if _is_async_environment():
return asyncio.gather(*async_resources)
return None


def shutdown_resources() -> Awaitable:
def shutdown_resources() -> Awaitable | None:
"""
Call this function to close all resources. Usually, it should be called
when your application is shutting down.
"""
_exit_stack.close()
return _async_exit_stack.aclose()
if _is_async_environment():
return _async_exit_stack.aclose()
return None


CallableManager = Callable[..., AsyncContextManager | ContextManager]


@dataclass(frozen=True)
class Depends:
dependency: Dependency
use_cache: bool
context_manager: ContextManager | AsyncContextManager | None = field(compare=False)
context_manager: CallableManager | None = field(compare=False)
is_async: bool = field(compare=False)

def get_scope_name(self) -> str:
return self.dependency._scope_ # type: ignore[attr-defined] # noqa: SF01

def value_as_context_manager(self) -> Any:
if self.context_manager:
return self.context_manager
return self.context_manager()
return nullcontext(self.dependency())

@classmethod
def from_dependency(cls, dependency: Dependency, use_cache: bool) -> Depends:
context_manager: ContextManager | AsyncContextManager | None = None
context_manager: Callable | None = None
is_async = False
if inspect.isasyncgenfunction(dependency):
context_manager = asynccontextmanager(dependency)()
context_manager = asynccontextmanager(dependency)
is_async = True
elif inspect.isgeneratorfunction(dependency):
context_manager = contextmanager(dependency)()
context_manager = contextmanager(dependency)

return cls(dependency, use_cache, context_manager, is_async)

Expand Down Expand Up @@ -280,3 +287,11 @@ async def _get_value_from_depends_async(
value = exit_stack.enter_context(context_manager)
scope.set(depends.dependency, value)
return value


def _is_async_environment() -> bool:
try:
asyncio.get_running_loop()
except RuntimeError:
return False
return True
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ build-backend = "poetry.core.masonry.api"
[tool.poetry]
name = "picodi"
description = "Simple Dependency Injection for Python"
version = "0.1.0"
version = "0.1.1"
license = "MIT"
authors = [
"yakimka"
Expand Down

0 comments on commit 91c8105

Please sign in to comment.