Skip to content

require-precise-factory-signature

Keep fixed keyword factory calls behind a precise callable signature.

Why

Callable[..., T] and functools.partial[T] erase argument checking at injected boundaries, so misspelled keywords and incompatible factories pass strict typing and can fail at runtime.

Fix

If factory substitution is unnecessary, call the existing constructor directly. Otherwise reuse an existing precise callable contract; use type[T] when the dependency requires a class, accounting for subclass constructor compatibility. Introduce a callable Protocol only when a substitutable keyword factory actually needs a new contract; preserve the accepted keyword names instead of replacing them with unchecked **kwargs.

Examples

Before — flagged An injected partial retains its result type but loses argument checking
app/service.py
from functools import partial
class Result:
pass
def _build(factory: partial[Result]):
return factory(size=4)
_build(Result)
After — preferred Direct partial construction keeps native argument checking
app/service.py
from functools import partial
class Result:
def __init__(self, *, size: int):
self.size = size
def build():
factory = partial(Result)
return factory(size=4)
Before — flagged Keyword arguments escape checking
app/service.py
from collections.abc import Callable
class Result:
pass
def _build(factory: Callable[..., Result]):
return factory(size=4)
_build(Result)
After — preferred Factory declares its keyword contract
app/service.py
from typing import Protocol
class Result:
pass
class Factory(Protocol):
def __call__(self, *, size: int) -> Result: ...
def build(factory: Factory):
return factory(size=4)