Skip to content

LazyProvider inside providers.Dict triggers eager module resolution in v4.0.0, breaking circular import protection #222

Description

@JCHacking

Affected project

that-depends

Versions

that-depends 4.0.0, python 3.14, linux

What happened?

When a LazyProvider is used as a value in providers.Dict, the Dict.init calls _register() → add_child_provider() on the LazyProvider. In v4.0.0, add_child_provider (inherited from AbstractProvider) does with self._lock:. If _lock is not initialized as an instance attribute before getattr is defined, Python falls through to getattr("_lock") → _get_provider() → importlib.import_module(...), eagerly resolving the lazy import — defeating the entire purpose of LazyProvider.

Expected behavior: LazyProvider should remain truly lazy when used as a value in providers.Dict. The module import should only happen when the provider is actually resolved, not during container class definition.

Regression: This worked correctly in v3.9.2.

Minimal reproduction

# a.py
from that_depends import BaseContainer, providers
from that_depends.experimental import LazyProvider

class ContainerA(BaseContainer):
    mapping = providers.Dict(
        b=LazyProvider(
            module_string="b",
            provider_string="ContainerB.some_provider",
        )
    )



# b.py
from that_depends import BaseContainer, providers
from a import ContainerA  # ← import circular

class ContainerB(BaseContainer):
    some_provider = providers.Singleton(lambda: 42)



# main.py
import a  # ← in v4.0.0: AttributeError: partially initialized module 'b'

Logs / traceback

Metadata

Metadata

Assignees

Labels

bugSomething isn't working

Type

Fields

No fields configured for Bug.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions