Commit ac2dac9e authored by Peter Todd's avatar Peter Todd

First stage of __getattribute__ special method support.

Works with test cases for a single class, have not dealt with subclass issues
yet.
parent 7c0cebd3
...@@ -666,7 +666,7 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode): ...@@ -666,7 +666,7 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode):
self.generate_ass_subscript_function(scope, code) self.generate_ass_subscript_function(scope, code)
if scope.defines_any(["__setslice__", "__delslice__"]): if scope.defines_any(["__setslice__", "__delslice__"]):
self.generate_ass_slice_function(scope, code) self.generate_ass_slice_function(scope, code)
if scope.defines_any(["__getattr__"]): if scope.defines_any(["__getattr__","__getattribute__"]):
self.generate_getattro_function(scope, code) self.generate_getattro_function(scope, code)
if scope.defines_any(["__setattr__", "__delattr__"]): if scope.defines_any(["__setattr__", "__delattr__"]):
self.generate_setattro_function(scope, code) self.generate_setattro_function(scope, code)
...@@ -1030,23 +1030,32 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode): ...@@ -1030,23 +1030,32 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode):
"}") "}")
def generate_getattro_function(self, scope, code): 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__ # If that raises an AttributeError, call the user's __getattr__
# method. # method, if defined.
entry = scope.lookup_here("__getattr__") getattr_entry = scope.lookup_here("__getattr__")
getattribute_entry = scope.lookup_here("__getattribute__")
code.putln("") code.putln("")
code.putln( code.putln(
"static PyObject *%s(PyObject *o, PyObject *n) {" "static PyObject *%s(PyObject *o, PyObject *n) {"
% scope.mangle_internal("tp_getattro")) % scope.mangle_internal("tp_getattro"))
if getattribute_entry is not None:
code.putln(
"PyObject *v = %s(o, n);" %
getattribute_entry.func_cname)
else:
code.putln( code.putln(
"PyObject *v = PyObject_GenericGetAttr(o, n);") "PyObject *v = PyObject_GenericGetAttr(o, n);")
if getattr_entry is not None:
code.putln( code.putln(
"if (!v && PyErr_ExceptionMatches(PyExc_AttributeError)) {") "if (!v && PyErr_ExceptionMatches(PyExc_AttributeError)) {")
code.putln( code.putln(
"PyErr_Clear();") "PyErr_Clear();")
code.putln( code.putln(
"v = %s(o, n);" % "v = %s(o, n);" %
entry.func_cname) getattr_entry.func_cname)
code.putln( code.putln(
"}") "}")
code.putln( code.putln(
......
...@@ -610,7 +610,7 @@ slot_table = ( ...@@ -610,7 +610,7 @@ slot_table = (
MethodSlot(callfunc, "tp_call", "__call__"), MethodSlot(callfunc, "tp_call", "__call__"),
MethodSlot(reprfunc, "tp_str", "__str__"), 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"), SyntheticSlot("tp_setattro", ["__setattr__", "__delattr__"], "0"), #"PyObject_GenericSetAttr"),
SuiteSlot(PyBufferProcs, "PyBufferProcs", "tp_as_buffer"), SuiteSlot(PyBufferProcs, "PyBufferProcs", "tp_as_buffer"),
......
__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
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment