Re: Learning to use decorators with classes



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


.



Relevant Pages

  • Host resolution problems with socket module
    ... I'm getting a rather bizarre error while using the socket module. ... exception depending on the status of the internet connection. ... raise the same exception no matter if the connection has been restored. ... I've been testing this on Linux with both python 2.3 and ...
    (comp.lang.python)
  • Re: Try Catch Else Finally
    ... using try catch is that without it, or some other error handling, if a ... cause the line to throw and exception as it can not execute. ... the finally block should be used to close resources and do other necessary cleanup to get your program back to a valid state. ... messagebox.show("Problem opening connection") ...
    (microsoft.public.dotnet.languages.vb)
  • get your potentially resuming element on board my squad
    ... exclusive era. ... require a transmission! ... it eases a contest too positive in connection with her ...
    (sci.crypt)
  • Re: decorators tutorials
    ... I am learning python by learning django, ... allow the SystemExit exception to continue to propogate without doing ... # The func argument is a Pythong function object ... trying to get the result of DivXY, ...
    (comp.lang.python)
  • Re: Error looping through controls on form in Access 2003
    ... >>code apply only to the controltypes that I wanted it to apply to. ... > I see 2 reasons to choose error handling over pre-checking conditions to avoid ... > new cases of the same condition in new versions of the underlying libraries. ... Now those are the exceptional cases that the word exception implies. ...
    (comp.databases.ms-access)