Ididi

Latest version: v1.3.0

Safety actively analyzes 697357 Python packages for vulnerabilities to keep your Python projects secure.

Scan your dependencies

Page 1 of 8

1.3.0

performance boost

**400%** performance improvements on `Graph.resolve`, by apply cache whever possible,

In previous benchmark,

1.2.7

renaming

- rename `DependencyGraph` to `Graph`
- rename `DependencyGraph.static_resolve` to `Graph.analyze`
- rename `DependencyGraph.static_resolve_all` to `Graph.analyze_nodes`

Original names are kept as alias to new names to avoid breaking changes


typing support

- support Unpack
py
class DataBase: ...
class Cache: ...
class Config: ...

class KWARGS(TypedDict):
db: DataBase
cache: Cache

class Extra(KWARGS):
config: Config

class Repo:
def __init__(self, **kwargs: Unpack[KWARGS]) -> None:
self.db = kwargs["db"]
self.cache = kwargs["cache"]

class SubRepo(Repo):
def __init__(self, **kwargs: Unpack[Extra]):
super().__init__(db=kwargs["db"], cache=kwargs["cache"])
self.config = kwargs["config"]

def test_resolve_unpack():
dg = Graph()
repo = dg.resolve(Repo)
assert isinstance(repo.db, DataBase)
assert isinstance(repo.cache, Cache)
subrepo = dg.resolve(SubRepo)
assert isinstance(subrepo.config, Config)


- support Literal

py
def test_resolve_literal():
dg = Graph()

class User:
def __init__(self, name: Literal["user"] = "user"):
self.name = name

u = dg.resolve(User)
assert u.name == "user"



Features

py
Graph.remove_singleton(self, dependent_type: type) -> None:
"Remove the registered singleton from current graph, return if not found"

Graph.remove_dependent(self, dependent_type: type) -> None
"Remove the dependent from current graph, return if not found"

1.2.6

- override class dependencies, entry dependencies

py
class Repo:
db: DataBase = use(db_factory)


def fake_db_factory()->DataBase:
return FakeDB()

dg.node(fake_db_factory)


override entry

py
def test_entry_replace():
dg = DependencyGraph()

def create_user(
user_name: str, user_email: str, service: UserService
) -> UserService:
return service

class FakeUserService(UserService): ...

create_user = dg.entry(reuse=False)(create_user)
create_user.replace(UserService, FakeUserService)

res = create_user("user", "useremail.com")
assert isinstance(res, FakeUserService)


class EvenFaker(UserService):
...

create_user.replace(service=EvenFaker)
create_user.replace(UserService, service=EvenFaker)

res = create_user("user", "useremail.com")
assert isinstance(res, EvenFaker)

1.2.5

- remove `LazyDependent`, since `DependencyGraph` can inject itself to any dependencies it resolve,
`LazyDependent` can be easily achieved by injecting `DependencyGraph` and lazy resolve

py
from ididi import ignore

class UserRepo:
def __init__(self, graph: DependencyGraph, db: Ignore[DataBase]=None):
self._graph = graph
self._db = db

property
def db(self):
return self._graph.resolve(DataBase)



- performance boost

25% performance increase to `DependencyGraph.resolve` compare to 1.2.4

Current implementation of dg.resolve is 17.356008 times slower than a hard-coded dependency construction.
given how much extra work ididi does resolving dependency,
our ultimate goal would be make dg.resolve < 10 times slower than a hard-coded solution.

1.2.4

Improvements:

- minor performance gain, 60% faster for instance resolve, should resolve 0.2 million instances in 1 second, although still 40 times slower than hard-coded factory, we tried replace recursive appraoch with iterative, no luck, only tiny improvements, so we stay at recursive appraoch

Fix:
in previous patch

py
def service_factory(
*, db: Database = use(db_fact), auth: AuthenticationService, name: str
) -> UserService:
return UserService(db=db, auth=auth)


def test_resolve():
dg = DependencyGraph()

dg.resolve(service_factory, name="aloha")


woud raise `UnsolvableDependencyError`, because `name` is a str without default values,
when when we resolve it, we will first static_resolve it, which would raise error,
now if dependencies that are provided with overrides won't be statically resolved.

1.2.3

- change `_itypes` to `interfaces`, make it public, since it is unlikely to cause breaking change

- remove `partial_resolve` from DependencyGraph

`partial_resolve` was introduced in `1.2.0`. it is not a good design,
it is like a global `try except`, which hides problems instead of solving them.


- `Ignore` Annotation


py
from ididi import Ignore

class User:
def __init__(self, name: Ignore[str]):
self.name = name


which is equivalent to


DependencyGraph().node(ignore="name")(User)



both `use` and `Ignore` are designed to be user friendly alternative to `DependencyGraph.node`

Page 1 of 8

© 2025 Safety CLI Cybersecurity Inc. All Rights Reserved.