Skip to content

Commit

Permalink
doc: add docstrings to classes (#83)
Browse files Browse the repository at this point in the history
  • Loading branch information
woile authored Sep 13, 2022
1 parent 33bf9ac commit 30cea33
Show file tree
Hide file tree
Showing 7 changed files with 68 additions and 2 deletions.
5 changes: 4 additions & 1 deletion di/api/solved.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,10 @@


class SolvedDependant(Generic[DependencyType]):
"""Representation of a fully solved dependency as DAG"""
"""Representation of a fully solved dependency as DAG.
A SolvedDependant could be a user's endpoint/controller function.
"""

__slots__ = ("dependency", "dag", "container_cache")

Expand Down
2 changes: 2 additions & 0 deletions di/container/_bind_by_type_hook.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ def bind_by_type(
*,
covariant: bool = False,
) -> BindHook:
"""Hook to substitute the matched dependency"""

def hook(
param: Optional[inspect.Parameter], dependant: DependantBase[Any]
) -> Optional[DependantBase[Any]]:
Expand Down
15 changes: 15 additions & 0 deletions di/container/_container.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,15 @@


class Container:
"""Solve and execute dependencies.
Generally you will want one Container per application.
There is not performance advantage to re-using a container, the only reason to do so is to share binds.
For each "thing" you want to wire with di and execute you'll want to call `Container.solve()`
exactly once and then keep a reference to the returned `SolvedDependant` to pass to `Container.execute`.
Solving is very expensive so avoid doing it in a hot loop.
"""

__slots__ = ("_bind_hooks", "_state")

_bind_hooks: List[BindHook]
Expand Down Expand Up @@ -59,6 +68,12 @@ def solve(
scopes: Sequence[Scope],
scope_resolver: Optional[ScopeResolver] = None,
) -> SolvedDependant[DependencyType]:
"""Build the dependency graph.
Should happen once, maybe during startup.
Solving dependencies can be slow.
"""
return solve(dependency, scopes, self._bind_hooks, scope_resolver)

def execute_sync(
Expand Down
37 changes: 37 additions & 0 deletions di/dependant/_dependant.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,29 @@


class Marker:
"""A dependency marker holds information about a dependency.
Used to tell `di` how to construct another class.
For example:
```py
def endpoint(conn: Annotated[DBConn, Marker(inject_db, scope="request")]):
...
```
Building your own `Marker` can be critical to enable nice functionality.
You could for example create a custom `Marker` "Header" than knows how to construct a `str`
from the headers of a request. Resulting in:
```py
def endpoint(content_type: FromHeader[str]):
...
```
See more in [dependency-markers](https://www.adriangb.com/di/latest/wiring/#dependency-markers).
"""

call: Optional[DependencyProvider]
dependency: Optional[Any]
scope: Scope
Expand Down Expand Up @@ -91,6 +114,20 @@ def inject_default_value() -> Any:


class Dependant(DependantBase[T]):
"""Connect dependencies together.
A `Dependant` can have sub-dependencies (also `Dependant`s).
The first argument is a `Callable`, which is used to find the
sub-dependencies.
Arguments:
call: used to find subdependencies
wire: if True then `call` is introspected to find sub-dependencies.
sync_to_thread: if True synchronous dependencies are run in a separate thread to avoid blocking the event loop
scope: the Scope for this dependency (see https://www.adriangb.com/di/latest/scopes/)
marker: the Marker from which this Defendant was constructed. This is included only for introspection purposes.
"""

call: Optional[DependencyProviderType[T]]
wire: bool
sync_to_thread: bool
Expand Down
2 changes: 2 additions & 0 deletions di/executors/_async.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ async def _execute_task(task: Task[StateType], state: StateType) -> None:


class AsyncExecutor(SupportsAsyncExecutor):
"""An executor that executes sync and async dependencies sequentially."""

async def execute_async(
self, tasks: SupportsTaskGraph[StateType], state: StateType
) -> None:
Expand Down
7 changes: 7 additions & 0 deletions di/executors/_sync.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,13 @@


class SyncExecutor(SupportsSyncExecutor):
"""An executor that executes only sync dependencies.
Dependencies are executed sequentially.
If any async dependencies are encountered a RuntimeError will be raised.
If there are no async dependencies, this will be faster than using `AsyncExecutor` because there is no event loop overhead.
"""

def execute_sync(
self, tasks: SupportsTaskGraph[StateType], state: StateType
) -> None:
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[tool.poetry]
name = "di"
version = "0.70.2"
version = "0.70.3"
description = "Dependency injection toolkit"
authors = ["Adrian Garcia Badaracco <[email protected]>"]
readme = "README.md"
Expand Down

0 comments on commit 30cea33

Please sign in to comment.