Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 15 additions & 3 deletions fastapi_startkit/src/fastapi_startkit/container/container.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
"""Core of the IOC Container."""

import inspect
from typing import Any
from typing import Any, TypeVar, overload

from ..exceptions import (
ContainerError,
MissingContainerBindingNotFound,
StrictContainerException,
)

T = TypeVar("T")


class Container:
"""Core of the Service Container.
Expand Down Expand Up @@ -103,11 +105,21 @@ def singleton(self, name, class_obj):
obj = self.resolve(class_obj)
self.bind(name, obj)

def make(self, name, *arguments):
@overload
def make(self, name: type[T], *arguments: Any) -> T: ...

@overload
def make(self, name: str, *arguments: Any) -> Any: ...

def make(self, name: str | type[T], *arguments: Any) -> Any:
"""Retrieve a class from the container by key.

Class keys resolve to an instance of that class (make(SomeClass) -> SomeClass);
string keys resolve to whatever was bound (Any). A missing key raises rather
than returning None, so the return type is deliberately non-Optional.

Arguments:
name {string} -- Key in the container that you want to get.
name {string | type} -- Key in the container that you want to get.

Raises:
MissingContainerBindingNotFound -- Raised if the key is not in the container.
Expand Down
33 changes: 33 additions & 0 deletions fastapi_startkit/tests/core/test_container.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
"""Tests for the IoC service container."""

from typing import Any, assert_type

import pytest

from fastapi_startkit.container.container import Container
Expand Down Expand Up @@ -515,3 +517,34 @@ def fn(unknown_param):

with pytest.raises(ContainerError):
container.resolve(fn)


# ---------------------------------------------------------------------------
# make() — static typing assertions (checked by basedpyright/pyright,
# executed at runtime as ordinary asserts)
# ---------------------------------------------------------------------------


class TestMakeTyping:
def test_make_with_class_key_is_typed_as_that_class(self, container):
container.bind("service_a", ServiceA)

instance = container.make(ServiceA)
assert_type(instance, ServiceA)
assert isinstance(instance, ServiceA)

def test_make_with_string_key_is_typed_as_any(self, container):
container.bind("service_a", ServiceA)

instance = container.make("service_a")
assert_type(instance, Any)
assert isinstance(instance, ServiceA)

def test_application_make_inherits_typing(self, tmp_path):
from fastapi_startkit.application import Application

app = Application(base_path=tmp_path, env="testing")
app.bind("service_a", ServiceA)

assert_type(app.make(ServiceA), ServiceA)
assert_type(app.make("service_a"), Any)
Loading