From ac2dac9e99568a7582b00d091afaffde7ed79681 Mon Sep 17 00:00:00 2001 From: Peter Todd Date: Fri, 2 May 2008 04:22:48 -0400 Subject: [PATCH] First stage of __getattribute__ special method support. Works with test cases for a single class, have not dealt with subclass issues yet. --- Cython/Compiler/ModuleNode.py | 35 ++++++++++++-------- Cython/Compiler/TypeSlots.py | 2 +- tests/run/__getattribute__.pyx | 60 ++++++++++++++++++++++++++++++++++ 3 files changed, 83 insertions(+), 14 deletions(-) create mode 100644 tests/run/__getattribute__.pyx diff --git a/Cython/Compiler/ModuleNode.py b/Cython/Compiler/ModuleNode.py index ade6af22..2b63c919 100644 --- a/Cython/Compiler/ModuleNode.py +++ b/Cython/Compiler/ModuleNode.py @@ -666,7 +666,7 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode): 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.BlockNode): "}") 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 --git a/Cython/Compiler/TypeSlots.py b/Cython/Compiler/TypeSlots.py index 29179b75..52c41c71 100644 --- a/Cython/Compiler/TypeSlots.py +++ b/Cython/Compiler/TypeSlots.py @@ -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 --git a/tests/run/__getattribute__.pyx b/tests/run/__getattribute__.pyx new file mode 100644 index 00000000..5ed494a4 --- /dev/null +++ b/tests/run/__getattribute__.pyx @@ -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 -- 2.26.2