On Wed, Apr 30, 2008 at 06:17:31PM -0700, Robert Bradshaw wrote: > On Apr 29, 2008, at 5:42 PM, Peter Todd wrote: > > >Is there a __getattribute__ work-alike in Cython? > > > >Essentially I need direct control over an objects tp_getattro and > >tp_setattro slots to implement a wrapper class. Specificly > >wrapped.__class__ should go to the wrapped objects class attribute, > >not > >the wrapping objects __class__ attribute. > > > >__getattr__ outputs C-source that includes a call to > >PyObject_GenericGetAttr first, and won't run my code if that call > >succeeds. > > > >Thanks, > > > >Peter > > Not that I'm aware of, though I'd imagine that implementing > __getattribute__ (if it exists) as being called at the top of this > function would be fairly easy to do. One would want to match Python > symantics exactly.
Here's my first patch. This correctly implements __getattribute__ and __getattr__ in the single class case. FWIW I also have a mercurial tree if it'd be better to pull from it than apply patches. I'm working on making subclasses behave correctly, I've got test cases written up showing where things fail, but no solutions to that written yet. The slot_tp_getattro stuff Stefan mentioned is useful though. -- http://petertodd.org 'peter'[:[EMAIL PROTECTED]
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
signature.asc
Description: Digital signature
_______________________________________________ Cython-dev mailing list [email protected] http://codespeak.net/mailman/listinfo/cython-dev
