From 6b56ec7391dba17f9b8fd02d0b94f4f753e30893 Mon Sep 17 00:00:00 2001 From: Peter Todd Date: Sun, 4 May 2008 23:28:40 -0400 Subject: [PATCH] __getattr(ibute)__ special methods now work with subclasses. --- Cython/Compiler/ModuleNode.py | 19 +++- tests/run/__getattribute_subclasses__.pyx | 126 ++++++++++++++++++++++ 2 files changed, 141 insertions(+), 4 deletions(-) create mode 100644 tests/run/__getattribute_subclasses__.pyx diff --git a/Cython/Compiler/ModuleNode.py b/Cython/Compiler/ModuleNode.py index 2b63c919..eead6a44 100644 --- a/Cython/Compiler/ModuleNode.py +++ b/Cython/Compiler/ModuleNode.py @@ -1033,10 +1033,21 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode): # 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 --git a/tests/run/__getattribute_subclasses__.pyx b/tests/run/__getattribute_subclasses__.pyx new file mode 100644 index 00000000..413e5e9d --- /dev/null +++ b/tests/run/__getattribute_subclasses__.pyx @@ -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 + -- 2.26.2