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):
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(
"}")
......
......@@ -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"),
......
__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