Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

__getattribute__ and methods proxying

26 views
Skip to first unread message

Giampaolo Rodolà

unread,
Jun 12, 2010, 3:01:46 PM6/12/10
to pytho...@python.org
Hi,
I have a class which looks like the one below.
What I'm trying to accomplish is to "wrap" all my method calls and
attribute lookups into a "proxy" method which translates certain
exceptions into others.
The code below *apparently* works: the original method is called but
for some reason the "except" clause is totally ignored.

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

David Zaslavsky

unread,
Jun 12, 2010, 3:36:55 PM6/12/10
to pytho...@python.org
Hi,

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

exa...@twistedmatrix.com

unread,
Jun 12, 2010, 3:47:13 PM6/12/10
to pytho...@python.org
On 07:01 pm, g.ro...@gmail.com wrote:
>Hi,
>I have a class which looks like the one below.
>What I'm trying to accomplish is to "wrap" all my method calls and
>attribute lookups into a "proxy" method which translates certain
>exceptions into others.
>The code below *apparently* works: the original method is called but
>for some reason the "except" clause is totally ignored.
>
>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()

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

Giampaolo Rodolà

unread,
Jun 12, 2010, 3:59:42 PM6/12/10
to David Zaslavsky, pytho...@python.org
2010/6/12 David Zaslavsky <dia...@ellipsix.net>:

> Hi,
>
> 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
> --
> http://mail.python.org/mailman/listinfo/python-list
>

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?

Thomas Jollans

unread,
Jun 12, 2010, 4:42:07 PM6/12/10
to pytho...@python.org

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

Stephen Hansen

unread,
Jun 12, 2010, 5:05:17 PM6/12/10
to pytho...@python.org
On 6/12/10 12:59 PM, Giampaolo Rodolà wrote:
> 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?

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/

signature.asc
0 new messages