Bringing up this old post...
On 21 June 2010 15:41, Robert Bradshaw <[email protected]> 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
[email protected]
http://mail.python.org/mailman/listinfo/cython-devel