[Python-Dev] Evil setattr hack

Guido van Rossum guido@python.org
Mon, 14 Apr 2003 20:35:34 -0400


> Someone accidentally discovered a way to set attributes of built-in
> types, even though the implementation tries to prevent this.

I've checked in what I believe is an adequate block for at least this
particular hack.  wrap_setattr(), which is called in response to
<type>.__setattr__(), now compares if the C function it is about to
call is the same as the C function in the built-in base class closest
to the object's class.  This means that if B is a built-in class and P
is a Python class derived from B, P.__setattr__ can call
B.__setattr__, but not A.__setattr__ where A is an (also built-in)
base class of B (unless B inherits A.__setattr__).

The following session shows that object.__setattr__ can no longer be
used to set a type's attributes:

Remind us that 'str' is an instance of 'type':

  >>> isinstance(str, type)
  True

'type' has a __setattr__ method that forbids setting all attributes.
Try type.__setattr__; nothing new here:

  >>> type.__setattr__(str, "foo", 42)
  Traceback (most recent call last):
    File "<stdin>", line 1, in ?
  TypeError: can't set attributes of built-in/extension type 'str'

Remind us that 'object' is a base class of 'type':

  >>> issubclass(type, object)
  True

Now try object.__setattr__.  This used to work; now it shows the new
error message:

  >>> object.__setattr__(str, "foo", 42)
  Traceback (most recent call last):
    File "<stdin>", line 1, in ?
  TypeError: can't apply this __setattr__ to type object

__delattr__ has the same restriction, or else you would be able to
remove existing str methods -- not good:

  >>> object.__delattr__(str, "foo")
  Traceback (most recent call last):
    File "<stdin>", line 1, in ?
  TypeError: can't apply this __delattr__ to type object

In other (normal) circumstances object.__setattr__ still works:

  >>> class C(object):
  ...     pass
  ...    
  >>> x = C()
  >>> object.__setattr__(x, "foo", 42)
  >>> object.__delattr__(x, "foo")

I'll backport this to Python 2.2 as well.

--Guido van Rossum (home page: http://www.python.org/~guido/)