Describe the Bug
In pygam/callbacks.py, the @validate_callback decorator is applied directly
on the class definitions at module load time:
@validate_callback
class Deviance(CallBack):
...
Inside validate_callback, the wrapping is done using setattr on
on_loop_start / on_loop_end methods once, when the file is first
imported. This means if a user subclasses Deviance (or any other callback)
and overrides on_loop_start or on_loop_end, the new method will not
be wrapped by validate_callback_data.
Steps to Reproduce
from pygam.callbacks import Deviance, validate_callback_data
class MyCallback(Deviance):
def on_loop_start(self, gam, y, mu, some_invalid_arg):
return 42 # This override is never validated
cb = MyCallback()
# No error is raised, even though the method signature is wrong
# The validation was only applied to Deviance, not MyCallback
Expected Behavior
Subclasses that override on_loop_start or on_loop_end should either:
- Be automatically re-validated when instantiated, or
- Raise a clear error if their method signature references arguments
not available in the callback data vars dict
Actual Behavior
No validation occurs on overridden methods in subclasses. The bug can
silently pass through and only fail at runtime during model fitting,
with a confusing error message unrelated to the real cause.
Root Cause
validate_callback uses setattr to wrap methods on the instance or
class passed to it. Since @validate_callback is applied at class
definition time, it only wraps the methods of the decorated class —
not any future subclasses.
# This only wraps Deviance.on_loop_start once at import time
@validate_callback
class Deviance(CallBack):
...
Suggested Fix
Move validation to __init_subclass__ or to the CallBack base class
__init__ method, so that every new subclass or instance is validated
at creation time:
class CallBack(Core):
def __init__(self, name=None):
super().__init__(name=name)
validate_callback(self) # validate at instance creation
This ensures all subclasses — including user-defined ones — are validated
regardless of when or how they are created.
Environment
| Detail |
Info |
| File |
pygam/callbacks.py |
| Affected Classes |
Deviance, Accuracy, Diffs, Coef and any user-defined subclasses |
| Decorator |
@validate_callback |
| Impact |
All versions using the current decorator pattern |
Labels
bug callbacks subclassing validation
Describe the Bug
In
pygam/callbacks.py, the@validate_callbackdecorator is applied directlyon the class definitions at module load time:
Inside
validate_callback, the wrapping is done usingsetattronon_loop_start/on_loop_endmethods once, when the file is firstimported. This means if a user subclasses
Deviance(or any other callback)and overrides
on_loop_startoron_loop_end, the new method will notbe wrapped by
validate_callback_data.Steps to Reproduce
Expected Behavior
Subclasses that override
on_loop_startoron_loop_endshould either:not available in the callback data vars dict
Actual Behavior
No validation occurs on overridden methods in subclasses. The bug can
silently pass through and only fail at runtime during model fitting,
with a confusing error message unrelated to the real cause.
Root Cause
validate_callbackusessetattrto wrap methods on the instance orclass passed to it. Since
@validate_callbackis applied at classdefinition time, it only wraps the methods of the decorated class —
not any future subclasses.
Suggested Fix
Move validation to
__init_subclass__or to theCallBackbase class__init__method, so that every new subclass or instance is validatedat creation time:
This ensures all subclasses — including user-defined ones — are validated
regardless of when or how they are created.
Environment
pygam/callbacks.pyDeviance,Accuracy,Diffs,Coefand any user-defined subclasses@validate_callbackLabels
bugcallbackssubclassingvalidation