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