2

As i mentioned in this previous post. Im trying to create a decorator which does the following:

The decorated class represents a document in a Documentbased DB like CouchDB or MongoDB. The Decorator accepts on argument which is a instance of a connector to such a database. The Model Class (in this example User) does automatically map undefined attributes to fields in the DB.

Now i get stuck a bit :-/ The mentioned things is all working. But now i cant call any methods from the Model Class. I'm getting the following error.

TypeError: unbound method myfunc() must be called with User instance as first argument (got nothing instead)

class Connector(object):
    def readvar(self, var):
        data = {"emailAddress":"jack.bauer@ctu.org", "lastName":"Bauer"}
        return data[var]

class DocumentDB(object):
    def __init__(self,connector):
        self.connector = connector

    def __call__(self, *args, **kargs):
        _c = self.connector

        class TransparentAttribute:         
            def __getattr__(self, attrname):
                try:
                    return _c.readvar(attrname)
                except:
                    return getattr(args[0], attrname)
        return TransparentAttribute

c = Connector()
@DocumentDB(c)
class User(object):

    username = "JackBauer"
    def doSomething(self):
        print "bla bla"
    def doSomethingElse(self):
        pass
    def myfunc(self):
        print "afadsadsf adsf asdf asdf"

u = User()
u.myfunc() # Does not work!!!
print u.emailAddress
print u.lastName
print u.username
Community
  • 1
  • 1
Thomas Spycher
  • 956
  • 13
  • 32
  • 1
    Could you show the full traceback of the error, which includes the line it occurred on? – David Robinson Jan 16 '13 at 06:26
  • @DavidRobinson I ran the same code, and got simply `Traceback (most recent call last): File "", line 52, in TypeError: unbound method myfunc() must be called with User instance as first argument (got nothing instead)` (on the line `u.myfunc()`) – Alex L Jan 16 '13 at 07:22

2 Answers2

1

args[0] is the User class object, not an instance as you except, hence you get an unbound method (aka class method) and not a bound method.

@DocumentDB(c)
class User(object):
    pass

can be rewritten as

class User(object):
    pass
User = DocumentDB(c)(User)

which makes the problem more clear (btw, does TransparentAttribute deliberately not inherit from object?)

Maybe you can get what you want not with a decorator but by using Connector as an additional base class to User?

Simon
  • 12,018
  • 4
  • 34
  • 39
1

I've had a quick play, and it looks like subclassing the user works.

class DocumentDB(object):
    def __init__(self,connector):
        self.connector = connector

    def __call__(self, user):
        _c = self.connector

        print self, user, _c # <__main__.DocumentDB object at 0x012DAD30> <class '__main__.User'> <__main__.Connector object at 0x012DAD70>

        class TransparentAttribute(user):
            def __getattr__(self, attrname):
                try:
                    return _c.readvar(attrname)
                except:
                    return getattr(user, attrname)

        return TransparentAttribute

u = User()
print type(u) # <class '__main__.TransparentAttribute'>
u.myfunc() # afadsadsf adsf asdf asdf

After u = User(), u is of type TransparentAttribute, and I think if you don't subclass it then you basically replace your User instance with a TransparentAttribute instance (so all the User object's local functions are gone).

(But to be honest, some of this is a bit over my head - feel free to correct me)

Alex L
  • 8,748
  • 5
  • 49
  • 75