Learning to use decorators with classes
Bruno Desthuilliers
bruno.42.desthuilliers at websiteburo.invalid
Tue Jun 30 08:48:23 EDT 2009
Mr SZ a écrit :
> Hi,
>
> I'm writing an LDAP plugin for my TG2 application. In this I wrote a small class based decorator with args to set up a connection and call the necessary
> functionality but I'm having problems with it. Here's my code:
>
(snip code)
>
> class LdapPlugin(Plugin):
> ...
> def __init__(self, **kw):
> Plugin.__init__(self)
>
> @getConnection(self._settings, self.__cred__)
<ot>
Don't use '__name__', they are reserved for the implementation. And
FWIW, don't use '__name' unless you have a really compelling reason to
do so.
</ot>
> def search(self, **kw):
> print 'Searching'
> ...
This can't work, and it's a FAQ FWIW - but since there's no official
c.l.py FAQ, we won't hold it against you !-)
def and class are both *executable* statements (yes, classes and
functions creation - like almost anything in Python - are run-time
operations).
The first one creates a function object - *wherever* it happens - and
bind the function object to the function's name in the current
namespace. Think of it as an equivalent of the following javascript snippet:
var func_name = function(arg) { /* function's body */ };
The second statement - class - builds a class object from the names
defined in the class statement's body (that is, names defined in the
class statement's body will become attributes of the class object).
Now about the 'methods' and 'self' stuff...
First understand that there's *nothing* magical with 'self'. It's *not*
a keyword. It's only a naming convention, and you could use any legal
Python identified instead.
The reason we have to explicitly mention it as first argument of the
function is that it's the only way the function's body can get access to
the current instance. What happens is (overly simplified) that during
attribute resolution, when the found attribute happens to be a function,
this function is wrapped - together with the instance on which the
attribute was looked up and it's class - into a callable method object.
Then when you call this method object, it inserts the instance as first
argument to the function call, and returns the result of the function
call (if you want to read more about this and how computed attributes
are implemented in Python, google for 'descriptor protocol').
IOW, and to make a long story short, calling instance.method is the same
as calling Class.method(instance).
Ok, now to the point: when you call getConnection within the class
statement's body, there's no magical "self" keyword poiting to an
instance, and since the class itself doesn't yet exists, so there's *no*
way you could get at an instance of it anyway !-)
There are many ways to solve your problem, the simplest bing probably to
write another decorator calling on the first one, ie:
def connected_method(func):
def connected(self, *args, **kw):
wrapped = getConnection(self.__this, self.__that)(func)
return wrapped(*args, **kw)
return connected
Note that this may not be that efficient - you'll have quite a few
function calls involved here.
While we're at it, a couple comments on your code... First, please read
pep08 (naming and coding conventions) on python.org. Conventions are
very important in Python.
wrt/ error handling:
try:
if tls:
connection.start_tls_s()
if anon:
con.simple_bind_s()
else:
con.simple_bind_s(dn, pw)
except ldap.LDAPError, e:
print e.message['info']
This kind of "error handling" is more than useless - it's worse than no
error handling at all. If you cannot handle the problem (I really mean
*handle*, you know, like do something to fix it), just let the exception
propagate - you'll get a nice traceback with all the necessary debugging
informations. Users of your package can always add a top-level
"catch-all" exception handler that will log tracebacks, send alert mails
to the team, and present the end-user with a nicely formatted error
message (and even possibly a way to handle the problem - like providing
appropriate connection data (credentials, url, whatever) !-)
Also note that sys.stdout is for *normal* program outputs. Error
messages belongs to sys.stderr.
else:
kw['conn'] = connection
try:
f(*args, **kw)
except Exception, e:
print 'Exception in Plugin execution: %s' % e
Same as above : if you can't handle the exception, leave it alone.
HTH
More information about the Python-list
mailing list