2

I would like to create a contextmanager that would intercept any object instantiation from a given class or subclasses of this classes and do something with the object.

I sudo code it could look like something like this:

with my_context_watchdog(my_callback, BaseClass):
     obj_1 = BaseClass(name) # launch callback
     obj_2 = SubClass(name) # launch callback

     obj_3 = RandomClass(name) # does not launch callback

def my_callback(new_obj):
     print("new object name:", new_obj.name)

Is there anyway I could do something like this in Python?

The best would be that the callback is executed only after the object has been fully instantiated.

Important: I can't modify any of the object classes, they are in a library I don't have any control on it. I just want to raise a callback when some of the Classes of this library are instantiated.

Maybe the contextmanager is not the best idea, I'm opened to any other solution.

2 Answers 2

2

The context manager doesn't actually see code in the with block. If you see a library doing something "magical" like this, there is usually a hidden global variable somewhere in the background, and the functions called inside the with block actively cooperate with the context manager.

That said, there's two options for you. One makes sense and one is very ugly.

The one that makes sense:

# somewhere in the context manager:
class ContextManagerReturnVal:
    def instantiate(self, cls, *args, **kwargs):
        obj = cls(*args, **kwargs)
        if issubclass(cls, self.base_class):
            self.callback(cls, *args, **kwargs)
        return obj

with watchdog(callback, BaseClass) as instantiate:
    obj1 = instantiate(BaseClass, name)
    obj2 = instantiate(SubClass, name)

And the ugly one is monkey-patching the BaseClass. You said in a comment that you can't modify the BaseClass - depends on what exactly you mean by that. If you can't modify the source code, you could still modify the class at run-time, like here: Monkey patching a class in another module in Python You would have to store the original __init__ method and return it after your context manager exits... This is of course not recommended because you're basically sticking your fingers where you shouldn't and possibly introducing hard-to-find bugs.

Other than that, I'm afraid it can't be done.

Sign up to request clarification or add additional context in comments.

1 Comment

Hum... I'm not a big fan of having to use instantiate() to create objects... It will break a large part of the code base and would like to keep it as transparent as possible. Thanks anyway, I'm gonna have a look to monkey_patching may looks like promising.
1

I can see you can implement rather easily a slightly different context manager:

with my_context_watchdog(some_instance):
     pass

... but this is probably as good as a checker function and not the functionality you'd want. The magic of a context manager is __enter__ method, so there is just that much what can happen there, no any kind of massive overloads. There is probably a way to voerride __init__ method

You can also make some constructor that can differentiate the classes names and think of applying it to __new__ method too:

def new(cls):
     if cls.__name__ == 'BaseClass':
          pass
          # do something
     else:
          pass
          # do a different thing

6 Comments

I should have precised, I can't modify BaseClass nor SubClass. So impossible to modify init and new. I will edit my post
you can achive some tracktion with standalone new(cls) function, but that would be a bit of wierd code, like obj_1 = new(BaseClass, name).
That would definitely be a solution, however it's not intercepting the object creation, it's more encapsulating it. Why would require to rewrite a large part of the code base and break code habits. I would highly prefer not doing it something like this
Oh that one is nice, I'll have a look. Thanks buddy!
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.