comparing with /Users/robert/Desktop/getattr_support.hg searching for changes changeset: 492:e0b4c3db1245 parent: 483:b869698d6f22 user: Peter Todd date: Fri May 02 04:22:48 2008 -0400 summary: First stage of __getattribute__ special method support. diff -r b869698d6f22 -r e0b4c3db1245 Cython/Compiler/ModuleNode.py --- a/Cython/Compiler/ModuleNode.py Thu May 01 20:09:34 2008 +0200 +++ b/Cython/Compiler/ModuleNode.py Fri May 02 04:22:48 2008 -0400 @@ -666,7 +666,7 @@ class ModuleNode(Nodes.Node, Nodes.Block self.generate_ass_subscript_function(scope, code) if scope.defines_any(["__setslice__", "__delslice__"]): self.generate_ass_slice_function(scope, code) - if scope.defines_any(["__getattr__"]): + if scope.defines_any(["__getattr__","__getattribute__"]): self.generate_getattro_function(scope, code) if scope.defines_any(["__setattr__", "__delattr__"]): self.generate_setattro_function(scope, code) @@ -1030,27 +1030,36 @@ class ModuleNode(Nodes.Node, Nodes.Block "}") def generate_getattro_function(self, scope, code): - # First try to get the attribute using PyObject_GenericGetAttr. + # First try to get the attribute using __getattribute__, if defined, or + # PyObject_GenericGetAttr. + # # If that raises an AttributeError, call the user's __getattr__ - # method. - entry = scope.lookup_here("__getattr__") + # method, if defined. + getattr_entry = scope.lookup_here("__getattr__") + getattribute_entry = scope.lookup_here("__getattribute__") code.putln("") code.putln( "static PyObject *%s(PyObject *o, PyObject *n) {" % scope.mangle_internal("tp_getattro")) - code.putln( + if getattribute_entry is not None: + code.putln( + "PyObject *v = %s(o, n);" % + getattribute_entry.func_cname) + else: + code.putln( "PyObject *v = PyObject_GenericGetAttr(o, n);") - code.putln( + if getattr_entry is not None: + code.putln( "if (!v && PyErr_ExceptionMatches(PyExc_AttributeError)) {") - code.putln( - "PyErr_Clear();") - code.putln( - "v = %s(o, n);" % - entry.func_cname) - code.putln( + code.putln( + "PyErr_Clear();") + code.putln( + "v = %s(o, n);" % + getattr_entry.func_cname) + code.putln( "}") code.putln( - "return v;") + "return v;") code.putln( "}") diff -r b869698d6f22 -r e0b4c3db1245 Cython/Compiler/TypeSlots.py --- a/Cython/Compiler/TypeSlots.py Thu May 01 20:09:34 2008 +0200 +++ b/Cython/Compiler/TypeSlots.py Fri May 02 04:22:48 2008 -0400 @@ -610,7 +610,7 @@ slot_table = ( MethodSlot(callfunc, "tp_call", "__call__"), MethodSlot(reprfunc, "tp_str", "__str__"), - SyntheticSlot("tp_getattro", ["__getattr__"], "0"), #"PyObject_GenericGetAttr"), + SyntheticSlot("tp_getattro", ["__getattr__","__getattribute__"], "0"), #"PyObject_GenericGetAttr"), SyntheticSlot("tp_setattro", ["__setattr__", "__delattr__"], "0"), #"PyObject_GenericSetAttr"), SuiteSlot(PyBufferProcs, "PyBufferProcs", "tp_as_buffer"), diff -r b869698d6f22 -r e0b4c3db1245 tests/run/__getattribute__.pyx --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/run/__getattribute__.pyx Fri May 02 04:22:48 2008 -0400 @@ -0,0 +1,60 @@ +__doc__ = """ +__getattribute__ and __getattr__ special methods for a single class. + + >>> a = just_getattribute() + >>> a.bar + 'bar' + >>> a.invalid + Traceback (most recent call last): + AttributeError + + >>> a = just_getattr() + >>> a.foo + 10 + >>> a.bar + 'bar' + >>> a.invalid + Traceback (most recent call last): + AttributeError + + >>> a = both() + >>> a.foo + 10 + >>> a.bar + 'bar' + >>> a.invalid + Traceback (most recent call last): + AttributeError +""" + +cdef class just_getattribute: + def __getattribute__(self,n): + if n == 'bar': + return n + else: + raise AttributeError + +cdef class just_getattr: + cdef readonly int foo + def __init__(self): + self.foo = 10 + def __getattr__(self,n): + if n == 'bar': + return n + else: + raise AttributeError + +cdef class both: + cdef readonly int foo + def __init__(self): + self.foo = 10 + def __getattribute__(self,n): + if n == 'foo': + return self.foo + else: + raise AttributeError + def __getattr__(self,n): + if n == 'bar': + return n + else: + raise AttributeError changeset: 493:8582cdefc1f4 tag: tip user: Peter Todd date: Sun May 04 23:28:40 2008 -0400 summary: __getattr(ibute)__ special methods now work with subclasses. diff -r e0b4c3db1245 -r 8582cdefc1f4 Cython/Compiler/ModuleNode.py --- a/Cython/Compiler/ModuleNode.py Fri May 02 04:22:48 2008 -0400 +++ b/Cython/Compiler/ModuleNode.py Sun May 04 23:28:40 2008 -0400 @@ -1033,10 +1033,21 @@ class ModuleNode(Nodes.Node, Nodes.Block # First try to get the attribute using __getattribute__, if defined, or # PyObject_GenericGetAttr. # - # If that raises an AttributeError, call the user's __getattr__ - # method, if defined. - getattr_entry = scope.lookup_here("__getattr__") - getattribute_entry = scope.lookup_here("__getattribute__") + # If that raises an AttributeError, call the __getattr__ if defined. + # + # In both cases, defined can be in this class, or any base class. + def lookup_here_or_base(n,type=None): + # Recursive lookup + if type is None: + type = scope.parent_type + r = type.scope.lookup_here(n) + if r is None and \ + type.base_type is not None: + return lookup_here_or_base(n,type.base_type) + else: + return r + getattr_entry = lookup_here_or_base("__getattr__") + getattribute_entry = lookup_here_or_base("__getattribute__") code.putln("") code.putln( "static PyObject *%s(PyObject *o, PyObject *n) {" diff -r e0b4c3db1245 -r 8582cdefc1f4 tests/run/__getattribute_subclasses__.pyx --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/run/__getattribute_subclasses__.pyx Sun May 04 23:28:40 2008 -0400 @@ -0,0 +1,126 @@ +__doc__ = """ +__getattribute__ and __getattr__ special methods and subclasses. + +getattr does not override members. + >>> a = getattr_boring() + >>> a.boring_member + 10 + >>> a.resolved_by + 'getattr_boring' + +getattribute does. + >>> a = getattribute_boring() + >>> a.boring_member + Traceback (most recent call last): + AttributeError + >>> a.resolved_by + 'getattribute_boring' + +Is inherited. + >>> a = boring_boring_getattribute() + >>> a.boring_getattribute_member + Traceback (most recent call last): + AttributeError + >>> a.boring_boring_getattribute_member + Traceback (most recent call last): + AttributeError + >>> a.resolved_by + '_getattribute' + +__getattribute__ is always tried first, then __getattr__, regardless of where +in the inheritance hiarchy they came from. + >>> a = getattribute_boring_boring_getattr() + >>> a.foo + Traceback (most recent call last): + AttributeError + >>> a.resolved_by + 'getattribute_boring_boring_getattr' + >>> a.getattribute_boring_boring_getattr + True + >>> a._getattr + True + + >>> a = getattr_boring_boring_getattribute() + >>> a.foo + Traceback (most recent call last): + AttributeError + >>> a.resolved_by + '_getattribute' + >>> a.getattr_boring_boring_getattribute + True + >>> a._getattribute + True + +""" + +cdef class boring: + cdef readonly int boring_member + def __init__(self): + self.boring_member = 10 + +cdef class getattr_boring(boring): + def __getattr__(self,n): + if n == 'resolved_by': + return 'getattr_boring' + elif n == 'getattr_boring': + return True + else: + raise AttributeError + +cdef class getattribute_boring(boring): + def __getattribute__(self,n): + if n == 'resolved_by': + return 'getattribute_boring' + elif n == 'getattribute_boring': + return True + else: + raise AttributeError + +cdef class _getattr: + def __getattr__(self,n): + if n == 'resolved_by': + return '_getattr' + elif n == '_getattr': + return True + else: + raise AttributeError + +cdef class _getattribute(boring): + def __getattribute__(self,n): + if n == 'resolved_by': + return '_getattribute' + elif n == '_getattribute': + return True + else: + raise AttributeError + +cdef class boring_getattribute(_getattribute): + cdef readonly int boring_getattribute_member + +cdef class boring_boring_getattribute(boring_getattribute): + cdef readonly int boring_boring_getattribute_member + +cdef class boring_getattr(_getattr): + cdef readonly int boring_getattr_member + +cdef class boring_boring_getattr(boring_getattr): + cdef readonly int boring_boring_getattr_member + +cdef class getattribute_boring_boring_getattr(boring_boring_getattr): + def __getattribute__(self,n): + if n == 'resolved_by': + return 'getattribute_boring_boring_getattr' + elif n == 'getattribute_boring_boring_getattr': + return True + else: + raise AttributeError + +cdef class getattr_boring_boring_getattribute(boring_boring_getattribute): + def __getattr__(self,n): + if n == 'resolved_by': + return 'getattr_boring_boring_getattribute' + elif n == 'getattr_boring_boring_getattribute': + return True + else: + raise AttributeError +