Check __init__ stores constructor arguments verbatim.
For every constructor parameter with a default, a default-constructed
instance must store that exact default (e.g. metric_params=None stays
None, never {}). Catches the init-mutation bug class.
Parameters
| Name |
Type |
Description |
Default |
estimator
|
BaseEstimator
|
Estimator instance (used only for its type).
|
required
|
Raises
| Type |
Description |
AssertionError
|
If a stored attribute differs from the constructor default.
|
Source Code
View on GitHub
Source code in src/yohou/testing/contract.py
| def check_init_no_param_mutation(estimator) -> None:
"""Check ``__init__`` stores constructor arguments verbatim.
For every constructor parameter with a default, a default-constructed
instance must store that exact default (e.g. ``metric_params=None`` stays
``None``, never ``{}``). Catches the init-mutation bug class.
Parameters
----------
estimator : BaseEstimator
Estimator instance (used only for its type).
Raises
------
AssertionError
If a stored attribute differs from the constructor default.
"""
cls = type(estimator)
name = cls.__name__
signature = inspect.signature(cls.__init__)
if any(
p.default is inspect.Parameter.empty and pname not in ("self",)
for pname, p in signature.parameters.items()
if p.kind in (p.POSITIONAL_OR_KEYWORD, p.KEYWORD_ONLY)
):
return # not default-constructible; skip (covered where representative instances exist)
fresh = cls()
for pname, param in signature.parameters.items():
if pname == "self" or param.default is inspect.Parameter.empty:
continue
if not hasattr(fresh, pname):
continue # stored under a different name (e.g. a property); out of scope here
stored = getattr(fresh, pname)
default = param.default
if default is None:
assert stored is None, f"{name}: __init__ mutated {pname!r} from None to {stored!r}"
else:
assert stored == default, f"{name}: __init__ mutated {pname!r} from {default!r} to {stored!r}"
|