I thought __getattribute__ was designed for such kind of things
("proxying") but apparently it seems I was wrong.
class NoSuchProcess(Exception): pass
class AccessDenied(Exception): pass
class Process(object):
def __getattribute__(self, name):
# wrap all method calls and attributes lookups so that
# underlying OSError exceptions get translated into
# NSP and AD exceptions
try:
print "here 1!"
return object.__getattribute__(self, name)
except OSError, err:
print "here 2!"
if err.errno == errno.ESRCH:
raise NoSuchProcess
if err.errno == errno.EPERM:
raise AccessDenied
def cmdline(self):
raise OSError("bla bla")
proc = Process()
proc.cmdline()
--- Giampaolo
http://code.google.com/p/pyftpdlib
http://code.google.com/p/psutil
The problem is that when you make this call:
> proc.cmdline()
there are really two steps involved. First you are accessing proc.cmdline,
then you are calling it. You could think of it as this:
func = proc.cmdline
func()
__getattribute__ is able to modify how the first step works, but not the
second. And it is the second step where the OSError gets raised.
You could get around this by returning a wrapper function from
__getattribute__, something like this I think:
def __getattribute__(self, name):
f = object.__getattribute__(self, name)
# here you should really check whether it's a function
def wrapper(self, *args, **kwargs)
print "here 1!"
try:
f(*args, **kwargs)
except OSError, err:
print "here 2!"
if err.errno == errno.ESRCH:
raise NoSuchProcess
if err.errno == errno.EPERM:
raise AccessDenied
return wrapper
That way "func" gets set to the wrapper function, which will handle your
exception as you want.
:) David
You've proxied attribute access here. But no OSError is raised by the
attribute access. It completes successfully. Then, the cmdline method
which was retrieved by the attribute access is called, normally, with
none of your other code getting involved. This raises an OSError, which
your code doesn't handle because it has already returned.
Jean-Paul
Clear, thanks.
Isn't there a prettier/common way to do this?
A __methodcall__(self, method_obj) special method or something? Has
something like that ever been proposed for inclusion?
There is no such thing as a method call in Python. There is attribute
access, and there is calling objects. You could, of course, create a
__methodcall__ method. That might look something like this:
class with_methodcall_trick(object):
def __methodcall__(self, method, *args, **kwa):
raise NotImplementedError("__methodcall__ must be overriden")
def __getattribute__(self, name):
obj = super(with_methodcall_trick, self).__getattribute__(name)
if callable(obj):
wrapper = functools.partial(self.__methodcall__, obj)
functools.update_wrapper(wrapper, obj)
return wrapper
else:
return wrapped
Not really, because that doesn't actually fit into the object model. A
class isn't actually involved with the calling of the method: once the
class returns it, the method's own __call__ is invoked later on. The
class is completely out of touch at that point.
The idiomatic way is really returning a wrapped function-- or using
decorators (which is just more explicit wrapping), I think.
--
Stephen Hansen
... Also: Ixokai
... Mail: me+list/python (AT) ixokai (DOT) io
... Blog: http://meh.ixokai.io/