2

I have the following command:

a = imp.load_source("a", r"some_path\some_source.py")

and so a is a module object from which I can access and run all functions inside some_source.py. Is there a way in python to accomplish that additional functionality will run before using any of some_source's functions but without changing some_source.py? For instance if some_source.py has functions foo1, foo2 ... foo100, then I want the command

a.foo5() 

to actually execute:

imp.reload(a)
a.foo(5)

and this should be applied to all 100 foo functions.

Just to be clear, I want the added functionality to happen simply by running the same command as everybody in my team are used to - a.foo5(). I don't want them to have to create some sort of class or call a different method than what they're used to.

2 Answers 2

3

You could try to decorate your module like this:

class ReloadingModule:
    def __init__(self, module):
        self.module = module

    def __getattr__(self, attr):
        print("reloading {}...".format(self.module.__name__))
        imp.reload(self.module)
        return getattr(self.module, attr)


import this
this = ReloadingModule(this)

print('\n\n',this.c, '\n\n')
print('\n\n',this.s, '\n\n')

Output:

reloading this...
The Zen of Python, by Tim Peters

Beautiful is better than ugly.
Explicit is better than implicit.
[...]


 97 


reloading this...
The Zen of Python, by Tim Peters

Beautiful is better than ugly.
Explicit is better than implicit.
[...]

 Gur Mra bs Clguba, ol Gvz Crgref

Ornhgvshy vf orggre guna htyl.
Rkcyvpvg vf orggre guna vzcyvpvg.
Fvzcyr vf orggre guna pbzcyrk.
[...]

With some math:

import math
math = ReloadingModule(math)

print(math.pi)
print(math.sin(math.pi/2))

Output:

reloading math...
3.141592653589793
reloading math...
reloading math...
1.0
Sign up to request clarification or add additional context in comments.

Comments

0

You could do the following :

Option A :

class example:
    def __init__(self):
        self.a = 2
        self.b = 3

    def some_func(self, argument_a):
        print(argument_a)

    def some_func_2(self, argument_b):
        print(argument_b)


def make_method(method_name):
    def _method(self, *args):
        # Insert what ever extra stuff you want to do here ...
        function_to_call = getattr(self.class_reference, method_name)
        function_to_call(self, *args)
    return _method


class a_class:
    def __init__(self, class_reference):
        self.class_reference = class_reference
        object_method_list = [func for func in dir(object) if callable(getattr(object, func))]
        method_list = [func for func in dir(class_reference) if callable(getattr(class_reference, func))]
        for method_name in method_list:
            if method_name in object_method_list:
                continue

            _method = make_method(method_name)
            setattr(type(self), method_name, _method)


module_class = a_class(example)
module_class.some_func()

This creates a class that creates all function from a dynamically (with an extra something).

Option B :

def do_func_with_extra(function_to_do, *args):
    # Do something extra...
    function_to_do(*args)
do_func_with_extra(a.some_func, argument0, argument1, ..)

Option B isnt exactly as you asked, but it would work as well.

I am also pretty certain there is a better way. but not one I can think of right now. Let me know if it works for you.

7 Comments

Thanks for the reply! I'm not sure these options suit me because they both require changing the way that I work with a, right? For option A I need to create a class from a and then use the class, and for option B I need to use the do_func_with_extra each time. The end goal is to keep using a the same way: a.foo5()
@felisimo Not for option A, if you dont even want to change the name of a, you could do the following : a = a_class(a), nothing should really change.
@felisimo For option A, if you call the new class a, you would still be able to call your functions in exactly the same way, although ill be honest and Ill recommend using theirry answer, I think its better and simpler.
I know it's been a while but this subject became relevant again. I actually prefer your option A than theirry's answer because your answer creates a class with the same attributes as the module's, so I get auto-completion which is important. Anyway, I tried option A but I am getting the following error when creating the method list: Traceback (most recent call last): File "<string>", line 1, in <module> File "<string>", line 4, in init AttributeError: attribute 'call' of 'namespace#' object is read-only
@felisimo Hey ill have a look at this on Sunday.
|

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.