First stage of __getattribute__ special method support.
authorPeter Todd <pete@petertodd.org>
Fri, 2 May 2008 08:22:48 +0000 (04:22 -0400)
committerPeter Todd <pete@petertodd.org>
Fri, 2 May 2008 08:22:48 +0000 (04:22 -0400)
Works with test cases for a single class, have not dealt with subclass issues
yet.

Cython/Compiler/ModuleNode.py
Cython/Compiler/TypeSlots.py
tests/run/__getattribute__.pyx [new file with mode: 0644]

index ade6af22874836ffc2f55b4643f72230b138a4ab..2b63c919ff1e7f63e392114840591bfbf9e1356e 100644 (file)
@@ -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(
             "}")
     
index 29179b75b30e4eee5d5af87523b5f9e420487967..52c41c71e3dd5dcf4481951abb3160102e7f8cfc 100644 (file)
@@ -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 (file)
index 0000000..5ed494a
--- /dev/null
@@ -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