Bypassing __getattribute__ for attribute access

Adam Donahue adam.donahue at gmail.com
Thu Oct 25 17:07:31 CEST 2007


As an exercise I'm attempting to write a metaclass that causes an
exception to be thrown whenever a user tries to access
'attributes' (in the traditional sense) via a direct reference.

Consider:

    class X( object ):
        y = 'private value'
        def get_y( self ): return self.y

Normally one can access y here via:

    X().y

or

    X().get_y()

I want the former case, however, to throw an exception.

I figured the way to do this would be to introduce a metaclass that
overrides the default __getattrribute__ call and throws an exception.
So my first attempt was something like:

    class XType( type ):
        def __my_getattribute__( self, name ):
             raise AttributeError()
        def __init__( klass, name, bases, dict ):
            super( XType, klass ).__init__( name, bases, dict )
            setattr( klass, '__getattribute__',
klass.__my_getattribute__ )

But whereas the X().y attribute behaves as I intend, the X().get_y()
returns raises that exception as well:

>>> X().y
Traceback (most recent call last):
  File "<stdin>", line 1, in ?
  File "<stdin>", line 3, in __my_getattribute__
AttributeError
>>> X().get_y()
Traceback (most recent call last):
  File "<stdin>", line 1, in ?
  File "<stdin>", line 3, in __my_getattribute__
AttributeError

So it looks as if 'attribute' here means any key in self.__dict__,
whether referenced via self.var, self.__dict__['var'] (because this
references __dict__), or getattr( self, 'var' ) (which is the same as
a direct self.var access, I believe).

So I tried:

    class XType( type ):
        def __my_getattribute__( self, name ):
            if name != '__dict__':
                raise AttributeError()
            return super( self.__class__,
self ).__getattribute__( name )
        def __init__( klass, name, bases, dict ):
            super( XType, klass ).__init__( name, bases, dict )
            setattr( klass, '__getattribute__',
klass.__my_getattribute__ )

This allows me to access X().__dict__ directly (and then
X().__dict__['y']), but it still limits caller access to the get_y()
method.

It sounds then like the "solution" will be to check whether the name
referenced is called __dict__ or is a method or function type,
otherwise throw the exception, and to ensure all internal calls are
handled via self.__dict__[name] not self.name.

Something like:

    import types
    class XType( type ):
        def __my_getattribute__( self, name ):
            if name != '__dict__' and not
isinstance( self.__dict__[name], types.FunctionType ):
                raise AttributeError()
            return super( self.__class__,
self ).__getattribute__( name )
        def __init__( klass, name, bases, dict ):
            super( XType, klass ).__init__( name, bases, dict )
            setattr( klass, '__getattribute__',
klass.__my_getattribute__ )

Of course this is imperfect as a user can simply bypass the
__getattribute__ call too and access __dict__ directly, but it's
closer to what I was thinking.  The problem is the return value for
functions is not bound - how do I bind these to the associated
instance?

(Caveat - I am not sure whether using __get__ itself in lieu of
__getattribute__ would be a better solution; but I would like to see
how binding would be done here for general knowledge.)

Thanks.

Adam




More information about the Python-list mailing list