On 27 August 2010 09:40, Stefan Behnel <[email protected]> wrote:
>
> BTW, now that you mention it - does anyone know what became of the idea to
> allow a
>
>     cdef __dict__
>
> attribute notation (as for __weakref__) to add a dict to an extension type?
> AFAIR, that was already proposed more than once on this list.
>

Here you have a tentative patch (use "hg import --no-commit ...")

There is a small gotcha, tough... CPython makes an optimization and
the instance dict in created on-demand. My patch unconditionally
creates the dict at tp_new. If not, the dict slot in the object
structure has to be set to NULL, but any usage of __dict__ in cdef
methods will assume non-NULL and segfault. So better safe than sorry.

-- 
Lisandro Dalcin
---------------
CIMEC (INTEC/CONICET-UNL)
Predio CONICET-Santa Fe
Colectora RN 168 Km 472, Paraje El Pozo
Tel: +54-342-4511594 (ext 1011)
Tel/Fax: +54-342-4511169
diff -r 8058fb6244dd Cython/Compiler/ModuleNode.py
--- a/Cython/Compiler/ModuleNode.py	Tue Aug 24 23:41:48 2010 -0700
+++ b/Cython/Compiler/ModuleNode.py	Fri Aug 27 13:04:37 2010 -0300
@@ -23,6 +23,7 @@
 
 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 escape_byte_string, EncodedString
@@ -1022,10 +1023,18 @@
             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__")
@@ -2081,6 +2090,11 @@
                         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 -r 8058fb6244dd Cython/Compiler/Symtab.py
--- a/Cython/Compiler/Symtab.py	Tue Aug 24 23:41:48 2010 -0700
+++ b/Cython/Compiler/Symtab.py	Fri Aug 27 13:04:37 2010 -0300
@@ -37,7 +37,8 @@
     # 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 -r 8058fb6244dd Cython/Compiler/TypeSlots.py
--- a/Cython/Compiler/TypeSlots.py	Tue Aug 24 23:41:48 2010 -0700
+++ b/Cython/Compiler/TypeSlots.py	Fri Aug 27 13:04:37 2010 -0300
@@ -410,6 +410,23 @@
                 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.
 
@@ -686,7 +703,7 @@
     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"),
diff -r 8058fb6244dd tests/run/exttypedict.pyx
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/run/exttypedict.pyx	Fri Aug 27 13:04:37 2010 -0300
@@ -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-dev mailing list
[email protected]
http://codespeak.net/mailman/listinfo/cython-dev

Reply via email to