Skip to content

Move callback validation to __init__#574

Open
Tushar8466 wants to merge 2 commits into
dswah:mainfrom
Tushar8466:fix-callback-validation
Open

Move callback validation to __init__#574
Tushar8466 wants to merge 2 commits into
dswah:mainfrom
Tushar8466:fix-callback-validation

Conversation

@Tushar8466

Copy link
Copy Markdown

📌 Summary

This PR fixes a bug where the @validate_callback decorator applied at
class definition time failed to validate overridden on_loop_start or
on_loop_end methods in subclasses of CallBack.


🐛 Problem

In the original code, @validate_callback was applied as a class-level
decorator:

@validate_callback
class Deviance(CallBack):
    ...

This means validation ran once at import time on the parent class
methods only. If a user subclassed Deviance and overrode on_loop_start,
the new method was never validated:

# This override was NEVER validated — silent bug!
class MyCallback(Deviance):
    def on_loop_start(self, gam, y, mu, some_invalid_arg):
        return 42

This could silently pass through and only crash at runtime during model
fitting
, with a confusing error unrelated to the real cause.


✅ Fix

Moved validation into CallBack.__init__ so it runs at instance
creation time
on the actual instance — including any overridden methods:

class CallBack(Core):
    def __init__(self, name=None):
        super(CallBack, self).__init__(name=name)
        validate_callback(self)  # ← runs on every instance, catches overrides

Also removed @validate_callback decorators from all subclass definitions
(Deviance, Accuracy, Diffs, Coef) since validation is now handled
automatically by the base class.


📂 Files Changed

File Change
pygam/callbacks.py Added validate_callback(self) in CallBack.__init__
pygam/callbacks.py Removed @validate_callback from Deviance, Accuracy, Diffs, Coef

🔍 Before vs After

Before After
When validation runs Once at import time Every instance creation
Parent class methods validated ✅ Yes ✅ Yes
Subclass overrides validated ❌ No ✅ Yes
User-defined subclasses validated ❌ No ✅ Yes
Invalid signature caught early ❌ No ✅ Yes

🧪 How to Test

from pygam.callbacks import Deviance

# ✅ Valid subclass — should work fine
class GoodCallback(Deviance):
    def on_loop_start(self, gam, y, mu):
        return 99

cb = GoodCallback()  # No error


# ❌ Invalid subclass — should now raise AssertionError at instantiation
class BadCallback(Deviance):
    def on_loop_start(self, gam, y, mu, some_invalid_arg):
        return 42

cb = BadCallback()
# AssertionError: CallBack cannot reference: some_invalid_arg

🔗 Related Issue

Closes #[ISSUE_NUMBER]


📝 Notes

  • The validate_callback function itself is unchanged — only when
    it is called has changed.
  • The _validated flag inside validate_callback ensures there is
    no double validation if the function is somehow called twice on
    the same instance.
  • This is a non-breaking change — all existing callbacks and
    correctly written subclasses will continue to work exactly as before.

Copilot AI review requested due to automatic review settings April 3, 2026 20:34

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR aims to ensure callback method signatures are validated on the actual overridden methods (including user subclasses), by moving callback validation from class-definition time to instance creation time.

Changes:

  • Run validate_callback(self) inside CallBack.__init__ so overridden on_loop_start/on_loop_end methods are validated per instance.
  • Remove @validate_callback usage from built-in callback class definitions (Deviance, Accuracy, Diffs, Coef).
  • Add inline notes describing the new validation approach.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread pygam/callbacks.py
Comment thread pygam/callbacks.py
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants