-
-
Notifications
You must be signed in to change notification settings - Fork 4.2k
Extend patch decorator to add methods to class types #11932
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nice new feature! just a couple of comments regarding code organization and implementation details
if inspect.isclass(target): | ||
# Special case: when a class type is passed, we bound the function to the class type patching __getattr__ | ||
return Patch._extend_class(target, fn) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i think having this check here doesn't make a ton of sense, since this is explicitly to patch functions. i think this check should be in patch
@@ -111,8 +121,22 @@ def __exit__(self, exc_type, exc_val, exc_tb): | |||
self.undo() | |||
return self | |||
|
|||
@staticmethod | |||
def _extend_class(target: Callable, fn: Callable): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i would put this on the same footing as function
def _extend_class(target: Callable, fn: Callable): | |
def extend_class(target: Callable, fn: Callable): |
try: | ||
self.old = getattr(self.obj, name) | ||
if self.old and name == "__getattr__": | ||
raise Exception("You can't patch class types implementing __getattr__") | ||
except AttributeError as e: | ||
# When we want to add a function to a class type, we want to intercept this exception and check if the | ||
# name is `__getattr__`. | ||
if name != "__getattr__": | ||
raise e | ||
self.old = None |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i think this check should be in apply
so we don't cause exceptions while instantiating the object
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this is much cleaner, thanks! just one minor nit that i missed, please just amend and merge :-)
@@ -111,6 +119,16 @@ def __exit__(self, exc_type, exc_val, exc_tb): | |||
self.undo() | |||
return self | |||
|
|||
@staticmethod | |||
def extend_class(target: Callable, fn: Callable): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: sorry i missed this, but the type hint should by Type
, right?
Motivation
Our
patch
decorator easily allows us to patch a class method and extend its behavior.However, it does not support the case where a given type does not implement a function and we want to add it.
Patch now would support the following use case:
If the class already implements a method with the given name, the patch won't have any effect.
Like our current implementation, a patch can be easily undone:
Changes
patch
to support the use case of adding a new function to a class type;