Skip to content

[BUG] @validate_callback decorator applied at class level doesn't validate overridden methods in subclasses #573

Description

@Tushar8466

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

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions