Note that there are some explanatory texts on larger screens.

plurals
  1. POHow to dynamically create module level functions from methods in a class
    primarykey
    data
    text
    <p>I am trying to dynamically create module level functions from the methods in a class. So for every method in a class, I want to create a function with the same name which creates an instance of the class and then calls the method.</p> <p>The reason I want to do this is so I can take an object-oriented approach to creating Fabric files. Since Fabric will call module level functions but not methods of a class, this is my work-around.</p> <p>I have used the following links to get me started</p> <ul> <li><a href="https://stackoverflow.com/questions/1911281/how-do-you-get-list-of-methods-in-a-python-class">How do I get list of methods in a Python class?</a></li> <li><a href="https://stackoverflow.com/questions/1621350/dynamically-adding-functions-to-a-python-module">dynamically adding functions to a Python module</a></li> <li><a href="https://stackoverflow.com/questions/2933470/how-do-i-call-setattr-on-the-current-module">How do I call setattr() on the current module?</a></li> <li><a href="http://effbot.org/zone/python-getattr.htm" rel="nofollow noreferrer">http://effbot.org/zone/python-getattr.htm</a></li> <li><a href="https://stackoverflow.com/questions/3061/calling-a-function-from-a-string-with-the-functions-name-in-python">Calling a function of a module from a string with the function&#39;s name in Python</a></li> <li><a href="https://stackoverflow.com/questions/1142068/how-to-modify-the-local-namespace-in-python">How to modify the local namespace in python</a></li> </ul> <p>And I have come up with the following code</p> <pre><code>import inspect import sys import types class TestClass(object): def __init__(self): pass def method1(self, arg1): print 'method 1 %s' % arg1 def method2(self): print 'method 2' def fabric_class_to_function_magic(module_name): # get the module as an object print module_name module_obj = sys.modules[module_name] print dir(module_obj) # Iterate over the methods of the class and dynamically create a function # for each method that calls the method and add it to the current module for method in inspect.getmembers(TestClass, predicate=inspect.ismethod): print print method method_name, method_obj = method # create a new template function which calls the method def newfunc_template(*args, **kwargs): tc = TestClass() func = getattr(tc, method_name) return func(*args, **kwargs) # create the actual function print 'code: ', newfunc_template.func_code print 'method_name: ', method_name newfunc = types.FunctionType(newfunc_template.func_code, {'TestClass': TestClass, 'getattr': getattr, 'method_name': method_name, }, name=method_name, argdefs=newfunc_template.func_defaults, closure=newfunc_template.func_closure, ) # add the new function to the current module setattr(module_obj, method_name, newfunc) # test the dynamically created module level function thismodule = sys.modules[__name__] print dir(thismodule) fabric_class_to_function_magic(__name__) print dir(thismodule) method1('arg1') method2() </code></pre> <p>And I get the following error</p> <pre><code>['TestClass', '__builtins__', '__doc__', '__file__', '__name__', '__package__', 'fabric_class_to_function_magic', 'inspect', 'sys', 'thismodule', 'types'] __main__ ['TestClass', '__builtins__', '__doc__', '__file__', '__name__', '__package__', 'fabric_class_to_function_magic', 'inspect', 'sys', 'thismodule', 'types'] ('__init__', &lt;unbound method TestClass.__init__&gt;) code: &lt;code object newfunc_template at 0x7f8800a28d50, file "test.py", line 85&gt; method_name: __init__ ('method1', &lt;unbound method TestClass.method1&gt;) code: &lt;code object newfunc_template at 0x7f8800a28d50, file "test.py", line 85&gt; method_name: method1 ('method2', &lt;unbound method TestClass.method2&gt;) code: &lt;code object newfunc_template at 0x7f8800a28d50, file "test.py", line 85&gt; method_name: method2 ['TestClass', '__builtins__', '__doc__', '__file__', '__init__', '__name__', '__package__', 'fabric_class_to_function_magic', 'inspect', 'method1', 'method2', 'sys', 'thismodule', 'types'] Traceback (most recent call last): File "test.py", line 111, in &lt;module&gt; method1('arg1') File "test.py", line 88, in newfunc_template return func(*args, **kwargs) TypeError: method2() takes exactly 1 argument (2 given) </code></pre> <p>It seems to be reusing the reference to the function? Any ideas?</p> <p>UPDATE: Here is the working code with Ned Batchelder's fix</p> <pre><code>def fabric_class_to_function_magic(module_name): # get the module as an object module_obj = sys.modules[module_name] # Iterate over the methods of the class and dynamically create a function # for each method that calls the method and add it to the current module for method in inspect.getmembers(TestClass, predicate=inspect.ismethod): method_name, method_obj = method # get the bound method tc = TestClass() func = getattr(tc, method_name) # add the function to the current module setattr(module_obj, method_name, func) </code></pre> <p>UPDATE 2: Here is my blog post on the subject: <a href="http://www.saltycrane.com/blog/2010/09/class-based-fabric-scripts-metaprogramming-hack/" rel="nofollow noreferrer">http://www.saltycrane.com/blog/2010/09/class-based-fabric-scripts-metaprogramming-hack/</a></p>
    singulars
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload