Bringing up this old post... On 21 June 2010 15:41, Robert Bradshaw <rober...@math.washington.edu> wrote: > On Jun 17, 2010, at 9:31 AM, Lisandro Dalcin wrote: > >> If we special case a __dict__ attribute in extension types, i.e: >> >> cdef class Foo: >> cdef dict __dict__ >> >> and fill type->tp_dictoffset, then we can support __dict__ in >> extension types. >> >> What do you think? > > Sounds like a good idea to me. Note that we check tp_dictoffset for > fast dispatching for cpdef methods (which would be correct as a dict > lookup *would* be needed if __dict__ is available). >
I still have this patch lying around in my disk. I remember Stefan had some objections. For example, when the user ask for __dict__, a new dict is unconditionally created (in CPython, type dict are allocated on-demand). I propose to get this patch pushed now, and optimize later (however, I really don't know how to safely implement this optimization). -- Lisandro Dalcin --------------- CIMEC (INTEC/CONICET-UNL) Predio CONICET-Santa Fe Colectora RN 168 Km 472, Paraje El Pozo 3000 Santa Fe, Argentina Tel: +54-342-4511594 (ext 1011) Tel/Fax: +54-342-4511169
diff --git a/Cython/Compiler/ModuleNode.py b/Cython/Compiler/ModuleNode.py index a66d43b..204d474 100644 --- a/Cython/Compiler/ModuleNode.py +++ b/Cython/Compiler/ModuleNode.py @@ -24,6 +24,7 @@ import DebugFlags from Errors import error, warning from PyrexTypes import py_object_type +from Builtin import dict_type as py_dict_type from Cython.Utils import open_new_file, replace_suffix from Code import UtilityCode from StringEncoding import EncodedString @@ -1088,10 +1089,18 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode): code.putln("p->%s = %s%s;" % ( type.vtabslot_cname, struct_type_cast, type.vtabptr_cname)) + entry = scope.lookup_here("__dict__") + if entry and entry in py_attrs: + code.putln("p->%s = PyDict_New();" % entry.cname) + code.putln("if (p->%s == 0) { Py_DECREF(o); return 0;}" % entry.cname) for entry in py_attrs: - if scope.is_internal or entry.name == "__weakref__": + if scope.is_internal: # internal classes do not need None inits code.putln("p->%s = 0;" % entry.cname) + elif entry.name == "__dict__": + pass + elif entry.name == "__weakref__": + code.putln("p->%s = 0;" % entry.cname) else: code.put_init_var_to_py_none(entry, "p->%s", nanny=False) entry = scope.lookup_here("__new__") @@ -2162,9 +2171,14 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode): code.putln( 'if (__Pyx_SetAttrString(%s, "%s", (PyObject *)&%s) < 0) %s' % ( Naming.module_cname, - scope.class_name, - typeobj_cname, - code.error_goto(entry.pos))) + scope.class_name, + typeobj_cname, + code.error_goto(entry.pos))) + dict_entry = scope.lookup_here("__dict__") + if dict_entry: + if not (dict_entry.type is py_object_type or + dict_entry.type is py_dict_type): + error(dict_entry.pos, "__dict__ slot must be of type 'object' or 'dict'") weakref_entry = scope.lookup_here("__weakref__") if weakref_entry: if weakref_entry.type is py_object_type: diff --git a/Cython/Compiler/Symtab.py b/Cython/Compiler/Symtab.py index 0d0e576..afb54c2 100644 --- a/Cython/Compiler/Symtab.py +++ b/Cython/Compiler/Symtab.py @@ -37,7 +37,8 @@ def c_safe_identifier(cname): # There are some C limitations on struct entry names. if ((cname[:2] == '__' and not (cname.startswith(Naming.pyrex_prefix) - or cname == '__weakref__')) + or cname == '__weakref__' + or cname == '__dict__')) or cname in iso_c99_keywords): cname = Naming.pyrex_prefix + cname return cname diff --git a/Cython/Compiler/TypeSlots.py b/Cython/Compiler/TypeSlots.py index 4565c7e..aaba929 100644 --- a/Cython/Compiler/TypeSlots.py +++ b/Cython/Compiler/TypeSlots.py @@ -427,7 +427,24 @@ class BaseClassSlot(SlotDescriptor): self.slot_name, base_type.typeptr_cname)) +class DictOffsetSlot(SlotDescriptor): + # Slot descriptor for the dict offset slot. + def __init__(self, name): + SlotDescriptor.__init__(self, name, dynamic = 1) + + def generate_dynamic_init_code(self, scope, code): + entry = scope.lookup_here('__dict__') + if entry: + type = scope.parent_type + objstruct = "struct %s" % type.objstruct_cname + code.putln("%s.%s = offsetof(%s, %s);" % ( + type.typeobj_cname, + self.slot_name, + objstruct, + entry.cname)) + + # The following dictionary maps __xxx__ method names to slot descriptors. method_name_to_slot = {} @@ -702,9 +719,9 @@ 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"), InternalMethodSlot("tp_new"), diff --git a/tests/run/exttypedict.pyx b/tests/run/exttypedict.pyx new file mode 100644 index 0000000..2bf1a13 --- /dev/null +++ b/tests/run/exttypedict.pyx @@ -0,0 +1,164 @@ +cdef class Foo: + u""" + >>> o = Foo() + >>> o.a = 1 + >>> o.a + 1 + >>> del o.a + >>> o.a #doctest: +ELLIPSIS + Traceback (most recent call last): + ... + AttributeError: 'exttypedict.Foo' object has no attribute 'a' + >>> o.__dict__ #doctest: +ELLIPSIS + Traceback (most recent call last): + ... + AttributeError: 'exttypedict.Foo' object has no attribute '__dict__' + >>> d = o.get_dict() + >>> d + {} + >>> d['b'] = 7 + >>> o.b + 7 + """ + cdef dict __dict__ # private + + def get_dict(self): + return (self.__dict__) + +cdef class Foo2(Foo): + u""" + >>> o = Foo2() + >>> o.a = 1 + >>> o.a + 1 + >>> del o.a + >>> o.a #doctest: +ELLIPSIS + Traceback (most recent call last): + ... + AttributeError: 'exttypedict.Foo2' object has no attribute 'a' + >>> o.__dict__ #doctest: +ELLIPSIS + Traceback (most recent call last): + ... + AttributeError: 'exttypedict.Foo2' object has no attribute '__dict__' + """ + pass + + +cdef class Bar: + u""" + >>> o = Bar() + >>> o.__dict__ + {} + >>> o.a = 1 + >>> o.a + 1 + >>> o.__dict__ + {'a': 1} + >>> o.__dict__['a'] = 2 + >>> o.a + 2 + >>> del o.a + >>> o.__dict__ + {} + >>> o.a #doctest: +ELLIPSIS + Traceback (most recent call last): + ... + AttributeError: 'exttypedict.Bar' object has no attribute 'a' + >>> d = {'b': 7} + >>> try: o.__dict__ = d + ... except (AttributeError, TypeError): print("ERROR") + ERROR + >>> o.__dict__.update(d) + >>> o.__dict__ == d + True + >>> o.b + 7 + """ + cdef readonly dict __dict__ # public, readonly + +cdef class Bar2(Bar): + u""" + >>> o = Bar2() + >>> o.__dict__ + {} + >>> o.a = 1 + >>> o.a + 1 + >>> o.__dict__ + {'a': 1} + >>> o.__dict__['a'] = 2 + >>> o.a + 2 + >>> del o.a + >>> o.__dict__ + {} + >>> o.a #doctest: +ELLIPSIS + Traceback (most recent call last): + ... + AttributeError: 'exttypedict.Bar2' object has no attribute 'a' + >>> d = {'b': 7} + >>> try: o.__dict__ = d + ... except (AttributeError, TypeError): print("ERROR") + ERROR + >>> o.__dict__.update(d) + >>> o.__dict__ == d + True + >>> o.b + 7 + """ + pass + +# --- + +cdef class Spam: + u""" + >>> o = Spam() + >>> o.__dict__ + {} + >>> o.a = 1 + >>> o.a + 1 + >>> o.__dict__ + {'a': 1} + >>> o.__dict__['a'] = 2 + >>> o.a + 2 + >>> del o.a + >>> o.__dict__ + {} + >>> o.a #doctest: +ELLIPSIS + Traceback (most recent call last): + ... + AttributeError: 'exttypedict.Spam' object has no attribute 'a' + >>> d = {'b': 7} + >>> o.__dict__ = d + >>> o.__dict__ is d + True + >>> o.b + 7 + """ + cdef public dict __dict__ # public, read/write + cdef x, y, z + +cdef class Spam2(Spam): + u""" + >>> o = Spam2() + >>> o.__dict__ + {} + >>> o.a = 1 + >>> o.a + 1 + >>> o.__dict__ + {'a': 1} + >>> o.__dict__['a'] = 2 + >>> o.a + 2 + >>> del o.a + >>> o.__dict__ + {} + >>> o.a #doctest: +ELLIPSIS + Traceback (most recent call last): + ... + AttributeError: 'exttypedict.Spam2' object has no attribute 'a' + """ + pass
_______________________________________________ cython-devel mailing list cython-devel@python.org http://mail.python.org/mailman/listinfo/cython-devel