Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

easiest way to "store" a function on a class without binding it?

I am writing some support code to expedite calling Django views (functions declared elsewhere) via RequestFactory. I am storing most of the testing attributes directly on classes, rather than on their instances.

One thing I have to do is to store, on the class, is which function I am interested in, so that I can call it later (using inspect to feed it its correct parameters).

Here's my general intent:

def to_test(var1, var2, var3):
    "this function has nothing to do with MyTest"
    assert isinstance(var1, basestring), "not an instance"

class MyTest(unittest.TestCase):

    #only using this attribute to store the function to 
    #test, not to bind that function
    func_under_test = to_test

    def test_with_abc(self):
        self.func_under_test("a","b", "c")

    def test_with_def(self):
        self.func_under_test("d","e", "f")

But as soon as I assign a function to a class it is bound to the class.

Which is great 99% of the time. Just not here because it gets the wrong parameters when called. Instead, on each class I have re-declare something so that I can assign the function to it, rather than directly on the class. Even metaclasses do not help.

Some sample code

What I'd like is FunctionStore1/2's syntax. The closest I've actually come are FunctionStore3/4/6, but they require you to remember copy & pasting the little _ declaration each time. No big deal, just hacky.

def regular_function(*args, **kwds):
    print ("    regular_function(%s)" % (locals()))

def regular_function2(*args, **kwds):
    print ("    regular_function2(%s)" % (locals()))

class FunctionStore1(object):
    "this fails, expecting an instance"
    func_to_check = regular_function

class FunctionStore2(object):
    "ditto"
    func_to_check = regular_function2

class FunctionStore3Works(object):
    "this works"

    def _(): pass
    _.func_to_check = regular_function


class FunctionStore4Works(object):
    """this too, but I have to redeclare the `_` each time
       can I use MetaClass?
    """

    def _(): pass
    _.func_to_check = regular_function2

class BaseTsupporter(object):
    "this doesnt help..."
    def _(): pass

class FunctionStore5(BaseTsupporter):
    "because there is no `_` here"

    try:
        _.func_to_check = regular_function
    except Exception, e:
            print ("\nno `_` on FunctionStore5:e:%s" % (e))

class FunctionStore6Works(object):
    "trying a dict"

    _ = dict(func_to_check=regular_function)

class MyMeta(type):
    def __new__(meta, name, bases, dct):
        res = super(MyMeta, meta).__new__(meta, name, bases, dct)
        #this works...
        res._ = dict()
        return res

    def __init__(cls, name, bases, dct):
        super(MyMeta, cls).__init__(name, bases, dct)

try:
    class FunctionStore7Meta(object):
        "using meta"

        __metaclass__ = MyMeta

        try:
            _.update(func_to_check=regular_function)                
        except Exception, e:
            print ("\nno `_` dict on FunctionStore7:e:%s" % (e))

except Exception, e:
    print ("\nno luck creating FunctionStore7 class :( exception:\n  %s" % (e))

#never mind the locals() + globals() hack, that's because this code is actually in a function to 
#allow SO's indenting...
li_to_call = [(k,v) for k, v in (locals().items() + globals().items()) if k.startswith("FunctionStore")]
li_to_call.sort()

for name, cls_ in li_to_call:
    print ("\n calling %s" % (name))
    try:
        if getattr(cls_, "func_to_check", None):
            cls_.func_to_check(name)
        elif hasattr(cls_, "_") and hasattr(cls_._, "func_to_check"):
            cls_._.func_to_check(name)
        elif hasattr(cls_, "_") and isinstance(cls_._, dict) and cls_._.get("func_to_check"):
            cls_._["func_to_check"](name)
        else:
            print ("    %s: no func_to_check" % (name))

            if "Meta" in name:
                print("        even if %s does have a `_`, now:%s" % (name, cls_._))

    except Exception, e:
            print ("    %s: exception:%s" % (name, e))

Output:

no `_` on FunctionStore5:e:name '_' is not defined

no `_` dict on FunctionStore7:e:name '_' is not defined

 calling FunctionStore1
    FunctionStore1: exception:unbound method regular_function() must be called with FunctionStore1 instance as first argument (got str instance instead)

 calling FunctionStore2
    FunctionStore2: exception:unbound method regular_function2() must be called with FunctionStore2 instance as first argument (got str instance instead)

 calling FunctionStore3Works
    regular_function({'args': ('FunctionStore3Works',), 'kwds': {}})

 calling FunctionStore4Works
    regular_function2({'args': ('FunctionStore4Works',), 'kwds': {}})

 calling FunctionStore5
    FunctionStore5: no func_to_check

 calling FunctionStore6Works
    regular_function({'args': ('FunctionStore6Works',), 'kwds': {}})

 calling FunctionStore7Meta
    FunctionStore7Meta: no func_to_check
        even if FunctionStore7Meta does have a `_`, now:{}
like image 276
JL Peyret Avatar asked Sep 25 '22 04:09

JL Peyret


1 Answers

You can wrap the functions in staticmethod:

class FunctionStore1(object):
    "this fails, expecting an instance"
    func_to_check = staticmethod(regular_function)
like image 131
Daniel Avatar answered Sep 28 '22 04:09

Daniel