Qualifier is ignored on a factory returning Optional[T]
Summary
When a factory is registered with a qualifier and returns an Optional[T] type, the qualifier appears to be dropped during registration. Instead of registering the qualified factory, the container tries to resolve T as a plain (unqualified) dependency, which fails validation with a self-dependency error.
Could you please help me with this?
This is blocking integration of wireup into our codebase.
Environment
- wireup: 2.10.0
- Python: 3.9.x
Minimal reproduction
import asyncio
from typing import Optional
import wireup
from wireup import injectable
class AuthContext:
pass
@injectable
async def require_authentication() -> AuthContext:
return AuthContext()
# Factory with a qualifier that returns Optional[AuthContext] — this combination breaks.
@injectable(qualifier="optional")
async def maybe_get_authentication() -> Optional[AuthContext]:
return None
async def main() -> None:
container = wireup.create_async_container(
injectables=[require_authentication, maybe_get_authentication],
)
if __name__ == "__main__":
asyncio.run(main())
Actual behavior
Container creation fails at registry validation:
WireupError: Parameter 'raw_type_instance' of <class '__main__.AuthContext'>
has an unknown dependency on <class '__main__.AuthContext'>.
Traceback (most recent call last):
check_wireup.py:46 in <module> asyncio.run(main())
check_wireup.py:40 in main container = wireup.create_async_container(...)
wireup/ioc/container/__init__.py:241 in create_async_container → _create_container(...)
wireup/ioc/container/__init__.py:114 in _create_container → ContainerRegistry(...)
wireup/ioc/registry.py:97 in __init__ → self.extend(...)
wireup/ioc/registry.py:137 in extend → validate_registry(self)
wireup/ioc/registry_validation.py:30 in validate_registry → assert_dependency_exists(...)
wireup/ioc/registry_validation.py:118 in assert_dependency_exists → raise WireupError(msg)
Expected behavior
The factory should be registered under the "optional" qualifier and resolve as Optional[AuthContext], without the qualifier being discarded or a spurious self-dependency being created.
Investigation
While debugging, it looks like the compatibility/Optional-unwrapping path does not carry the qualifier through when the return type is Optional[T]. Because the qualifier is lost, the factory ends up registered against the bare type T, which then appears to depend on itself — producing the raw_type_instance self-dependency error above. A non-Optional return type with the same qualifier registers fine.
Suggested fixes
File wireup 2.10.0: https://github.com/maldoinc/wireup/blob/v2.10.0/wireup/ioc/registry.py#L258
File wireup 2.11.0: https://github.com/maldoinc/wireup/blob/v2.11.0/wireup/ioc/registry.py#L396
compat_fn.__signature__ = inspect.Signature( # type: ignore[attr-defined]
parameters=[
inspect.Parameter(
"raw_type_instance",
kind=inspect.Parameter.POSITIONAL_OR_KEYWORD,
- annotation=klass,
+ annotation=klass if qualifier is None else Annotated[klass,
InjectableQualifier(qualifier)],
)
],
)
Qualifier is ignored on a factory returning
Optional[T]Summary
When a factory is registered with a
qualifierand returns anOptional[T]type, the qualifier appears to be dropped during registration. Instead of registering the qualified factory, the container tries to resolveTas a plain (unqualified) dependency, which fails validation with a self-dependency error.Could you please help me with this?
This is blocking integration of wireup into our codebase.
Environment
Minimal reproduction
Actual behavior
Container creation fails at registry validation:
Expected behavior
The factory should be registered under the
"optional"qualifier and resolve asOptional[AuthContext], without the qualifier being discarded or a spurious self-dependency being created.Investigation
While debugging, it looks like the compatibility/
Optional-unwrapping path does not carry the qualifier through when the return type isOptional[T]. Because the qualifier is lost, the factory ends up registered against the bare typeT, which then appears to depend on itself — producing theraw_type_instanceself-dependency error above. A non-Optionalreturn type with the same qualifier registers fine.Suggested fixes
File wireup 2.10.0: https://github.com/maldoinc/wireup/blob/v2.10.0/wireup/ioc/registry.py#L258
File wireup 2.11.0: https://github.com/maldoinc/wireup/blob/v2.11.0/wireup/ioc/registry.py#L396