Commit ffa59aa7 authored by empyrical's avatar empyrical

Use property system for __dict__ attribute

parent 5efe391e
......@@ -6432,8 +6432,8 @@ class AttributeNode(ExprNode):
return
self.entry = entry
if entry:
if obj_type.is_extension_type and entry.name in ("__weakref__", "__dict__"):
error(self.pos, "Illegal use of special attribute %s" % entry.name)
if obj_type.is_extension_type and entry.name == "__weakref__":
error(self.pos, "Illegal use of special attribute __weakref__")
# def methods need the normal attribute lookup
# because they do not have struct entries
......
......@@ -1152,8 +1152,8 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode):
self.generate_descr_get_function(scope, code)
if scope.defines_any(["__set__", "__delete__"]):
self.generate_descr_set_function(scope, code)
if scope.lookup_here("__dict__"):
self.generate_dict_getter(scope, code)
if scope.defines_any(["__dict__"]):
self.generate_dict_getter_function(scope, code)
self.generate_property_accessors(scope, code)
self.generate_method_table(scope, code)
self.generate_getset_table(scope, code)
......@@ -1278,7 +1278,10 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode):
(entry.cname, entry.type.empty_declaration_code()))
for entry in py_attrs:
code.put_init_var_to_py_none(entry, "p->%s", nanny=False)
if entry.name == "__dict__":
code.putln("p->%s = PyDict_New(); Py_INCREF(p->%s);" % (entry.cname, entry.cname))
else:
code.put_init_var_to_py_none(entry, "p->%s", nanny=False)
for entry in memoryview_slices:
code.putln("p->%s.data = NULL;" % entry.cname)
......@@ -1974,9 +1977,7 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode):
code.putln(
"};")
def generate_dict_getter(self, scope, code):
#if scope.name == 'QApplication':
# import ipdb;ipdb.set_trace()
def generate_dict_getter_function(self, scope, code):
func_name = scope.mangle_internal("__dict__getter")
dict_attr = scope.lookup_here("__dict__")
dict_name = dict_attr.cname
......@@ -1991,34 +1992,25 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode):
code.putln("}")
def generate_getset_table(self, env, code):
dynamic_attributes = env.lookup_here("__dict__")
if env.property_entries or dynamic_attributes:
if env.property_entries:
code.putln("")
code.putln(
"static struct PyGetSetDef %s[] = {" %
env.getset_table_cname)
if dynamic_attributes:
dict_getter_cname = env.mangle_internal("__dict__getter")
code.putln(
'{(char *)"__dict__", %s, 0, 0, 0},' % (
dict_getter_cname))
for entry in env.property_entries:
if entry.name == "__dict__":
continue
doc = entry.doc
if doc:
if doc.is_unicode:
doc = doc.as_utf8_string()
doc_code = doc.as_c_string_literal()
else:
doc = entry.doc
if doc:
if doc.is_unicode:
doc = doc.as_utf8_string()
doc_code = doc.as_c_string_literal()
else:
doc_code = "0"
code.putln(
'{(char *)"%s", %s, %s, (char *)%s, 0},' % (
entry.name,
entry.getter_cname or "0",
entry.setter_cname or "0",
doc_code))
doc_code = "0"
code.putln(
'{(char *)"%s", %s, %s, (char *)%s, 0},' % (
entry.name,
entry.getter_cname or "0",
entry.setter_cname or "0",
doc_code))
code.putln(
"{0, 0, 0, 0, 0}")
code.putln(
......@@ -2799,21 +2791,6 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode):
weakref_entry.cname))
else:
error(weakref_entry.pos, "__weakref__ slot must be of type 'object'")
dict_entry = scope.lookup_here("__dict__")
if dict_entry:
if dict_entry.type.cname == 'PyDict_Type':
tp_dictoffset = "%s.tp_dictoffset" % typeobj_cname
if type.typedef_flag:
objstruct = type.objstruct_cname
else:
objstruct = "struct %s" % type.objstruct_cname
code.putln("if (%s == 0) %s = offsetof(%s, %s);" % (
tp_dictoffset,
tp_dictoffset,
objstruct,
dict_entry.cname))
else:
error(dict_entry.pos, "__dict__ slot must be of type 'dict'")
def generate_exttype_vtable_init_code(self, entry, code):
# Generate code to initialise the C method table of an
......
......@@ -4615,10 +4615,15 @@ class CClassDefNode(ClassDefNode):
if has_body:
self.body.analyse_declarations(scope)
dict_entry = self.scope.lookup_here("__dict__")
if dict_entry and (not scope.defined and not scope.implemented):
dict_entry.getter_cname = self.scope.mangle_internal("__dict__getter")
self.scope.declare_property("__dict__", dict_entry.doc, dict_entry.pos)
if self.in_pxd:
scope.defined = 1
else:
scope.implemented = 1
env.allocate_vtable_names(self.entry)
for thunk in self.entry.type.defered_declarations:
......
......@@ -488,7 +488,7 @@ class GetSetSlot(SlotDescriptor):
# Slot descriptor for the table of attribute get & set methods.
def slot_code(self, scope):
if scope.property_entries or scope.lookup_here("__dict__"):
if scope.property_entries:
return scope.getset_table_cname
else:
return "0"
......@@ -509,6 +509,27 @@ class BaseClassSlot(SlotDescriptor):
base_type.typeptr_cname))
class DictOffsetSlot(SlotDescriptor):
# Slot descriptor for a class' dict offset, for dynamic attributes.
def slot_code(self, scope):
dict_entry = scope.lookup_here("__dict__")
if dict_entry:
if dict_entry.type.cname != 'PyDict_Type':
error(dict_entry.pos, "__dict__ slot must be of type 'dict'")
return "0"
type = scope.parent_type
if type.typedef_flag:
objstruct = type.objstruct_cname
else:
objstruct = "struct %s" % type.objstruct_cname
return ("offsetof(%s, %s)" % (
objstruct,
dict_entry.cname))
else:
return "0"
# The following dictionary maps __xxx__ method names to slot descriptors.
method_name_to_slot = {}
......@@ -814,7 +835,7 @@ slot_table = (
SyntheticSlot("tp_descr_get", ["__get__"], "0"),
SyntheticSlot("tp_descr_set", ["__set__", "__delete__"], "0"),
EmptySlot("tp_dictoffset"),
DictOffsetSlot("tp_dictoffset"),
MethodSlot(initproc, "tp_init", "__init__"),
EmptySlot("tp_alloc"), #FixedSlot("tp_alloc", "PyType_GenericAlloc"),
......
# mode: compile
cimport cython
cdef class Spam:
pass
cdef class SuperSpam(Spam):
cdef dict __dict__
cdef class SuperSpam2(SuperSpam):
pass
cdef class SuperSpam3(SuperSpam2):
pass
cdef public class UltraSpam [type UltraSpam_Type, object UltraSpam_Object]:
cdef dict __dict__
cdef class MegaSpam:
cdef dict __dict__
# mode: run
cimport cython
cdef class Spam:
cdef dict __dict__
cdef class SuperSpam(Spam):
pass
cdef class MegaSpam:
pass
cdef public class UltraSpam [type UltraSpam_Type, object UltraSpam_Object]:
cdef dict __dict__
def test_class_attributes():
"""
>>> test_class_attributes()
'bar'
"""
o = Spam()
o.foo = "bar"
return o.foo
def test_subclass_attributes():
"""
>>> test_subclass_attributes()
'bar'
"""
o = SuperSpam()
o.foo = "bar"
return o.foo
def test_defined_class_attributes():
"""
>>> test_defined_class_attributes()
'bar'
"""
o = MegaSpam()
o.foo = "bar"
return o.foo
def test_public_class_attributes():
"""
>>> test_public_class_attributes()
'bar'
"""
o = UltraSpam()
o.foo = "bar"
return o.foo
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