From 9c56409d3353b8cd4cfc19e0467bbe23fd34fc92 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Martin=20v=2E=20L=C3=B6wis?= <martin@v.loewis.de>
Date: Sat, 23 Jun 2012 23:20:45 +0200
Subject: Issue #15146: Add PyType_FromSpecWithBases. Patch by Robin Schreiber.

---
 Doc/c-api/type.rst   | 12 ++++++++
 Include/object.h     |  3 ++
 Misc/NEWS            |  2 ++
 Objects/typeobject.c | 80 +++++++++++++++++++++++++++++++++++++++++++++++++---
 PC/python3.def       |  2 ++
 PC/python33stub.def  |  1 +
 6 files changed, 96 insertions(+), 4 deletions(-)

diff --git a/Doc/c-api/type.rst b/Doc/c-api/type.rst
index 6f9c7a8..aa5eef0 100644
--- a/Doc/c-api/type.rst
+++ b/Doc/c-api/type.rst
@@ -85,3 +85,15 @@ Type Objects
    their initialization.  This function is responsible for adding inherited slots
    from a type's base class.  Return ``0`` on success, or return ``-1`` and sets an
    exception on error.
+   
+.. c:function:: PyObject* PyType_FromSpec(PyType_Spec *spec)
+
+   Creates and returns a heap type object from the *spec* passed to the function.
+
+.. c:function:: PyObject* PyType_FromSpecWithBases(PyType_Spec *spec, PyObject *bases)
+
+   Creates and returns a heap type object from the *spec*. In addition to that,
+   the created heap type contains all types contained by the *bases* tuple as base
+   types. This allows the caller to reference other heap types as base types.
+
+   .. versionadded:: 3.3
diff --git a/Include/object.h b/Include/object.h
index 68001e7..709a9ff 100644
--- a/Include/object.h
+++ b/Include/object.h
@@ -433,6 +433,9 @@ typedef struct{
 } PyType_Spec;
 
 PyAPI_FUNC(PyObject*) PyType_FromSpec(PyType_Spec*);
+#if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 >= 0x03030000
+PyAPI_FUNC(PyObject*) PyType_FromSpecWithBases(PyType_Spec*, PyObject*);
+#endif
 
 #ifndef Py_LIMITED_API
 /* The *real* layout of a type object when allocated on the heap */
diff --git a/Misc/NEWS b/Misc/NEWS
index 35e8ede..623a5a4 100644
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -10,6 +10,8 @@ What's New in Python 3.3.0 Beta 1?
 Core and Builtins
 -----------------
 
+- Issue #15146: Add PyType_FromSpecWithBases. Patch by Robin Schreiber.
+
 - Issue #15142: Fix reference leak when deallocating instances of types
   created using PyType_FromSpec().
 
diff --git a/Objects/typeobject.c b/Objects/typeobject.c
index d8bdeaf..a2b1619 100644
--- a/Objects/typeobject.c
+++ b/Objects/typeobject.c
@@ -48,6 +48,9 @@ _Py_IDENTIFIER(__new__);
 static PyObject *
 _PyType_LookupId(PyTypeObject *type, struct _Py_Identifier *name);
 
+static PyObject *
+slot_tp_new(PyTypeObject *type, PyObject *args, PyObject *kwds);
+
 unsigned int
 PyType_ClearCache(void)
 {
@@ -2375,22 +2378,75 @@ static short slotoffsets[] = {
 };
 
 PyObject *
-PyType_FromSpec(PyType_Spec *spec)
+PyType_FromSpecWithBases(PyType_Spec *spec, PyObject *bases)
 {
     PyHeapTypeObject *res = (PyHeapTypeObject*)PyType_GenericAlloc(&PyType_Type, 0);
+    PyTypeObject *type, *base;
+    char *s;
     char *res_start = (char*)res;
     PyType_Slot *slot;
+    
+    /* Set the type name and qualname */
+    s = strrchr(spec->name, '.');
+    if (s == NULL)
+        s = (char*)spec->name;
+    else
+        s++;
 
     if (res == NULL)
       return NULL;
-    res->ht_name = PyUnicode_FromString(spec->name);
+    res->ht_name = PyUnicode_FromString(s);
     if (!res->ht_name)
         goto fail;
     res->ht_qualname = res->ht_name;
     Py_INCREF(res->ht_qualname);
-    res->ht_type.tp_name = _PyUnicode_AsString(res->ht_name);
+    res->ht_type.tp_name = spec->name;
     if (!res->ht_type.tp_name)
         goto fail;
+    
+    /* Adjust for empty tuple bases */
+    if (!bases) {
+        base = &PyBaseObject_Type;
+        /* See whether Py_tp_base(s) was specified */
+        for (slot = spec->slots; slot->slot; slot++) {
+            if (slot->slot == Py_tp_base)
+                base = slot->pfunc;
+            else if (slot->slot == Py_tp_bases) {
+                bases = slot->pfunc;
+                Py_INCREF(bases);
+            }
+        }
+        if (!bases)
+            bases = PyTuple_Pack(1, base);
+        if (!bases)
+            goto fail;
+    }
+    else
+        Py_INCREF(bases);
+
+    /* Calculate best base, and check that all bases are type objects */
+    base = best_base(bases);
+    if (base == NULL) {
+        goto fail;
+    }
+    if (!PyType_HasFeature(base, Py_TPFLAGS_BASETYPE)) {
+        PyErr_Format(PyExc_TypeError,
+                     "type '%.100s' is not an acceptable base type",
+                     base->tp_name);
+        goto fail;
+    }
+
+    type = (PyTypeObject *)res;
+    /* Initialize essential fields */
+    type->tp_as_number = &res->as_number;
+    type->tp_as_sequence = &res->as_sequence;
+    type->tp_as_mapping = &res->as_mapping;
+    type->tp_as_buffer = &res->as_buffer;
+    /* Set tp_base and tp_bases */
+    type->tp_bases = bases;
+    bases = NULL;
+    Py_INCREF(base);
+    type->tp_base = base;
 
     res->ht_type.tp_basicsize = spec->basicsize;
     res->ht_type.tp_itemsize = spec->itemsize;
@@ -2401,6 +2457,9 @@ PyType_FromSpec(PyType_Spec *spec)
             PyErr_SetString(PyExc_RuntimeError, "invalid slot offset");
             goto fail;
         }
+        if (slot->slot == Py_tp_base || slot->slot == Py_tp_bases)
+            /* Processed above */
+            continue;
         *(void**)(res_start + slotoffsets[slot->slot]) = slot->pfunc;
 
         /* need to make a copy of the docstring slot, which usually
@@ -2427,6 +2486,13 @@ PyType_FromSpec(PyType_Spec *spec)
     if (PyType_Ready(&res->ht_type) < 0)
         goto fail;
 
+    /* Set type.__module__ */
+    s = strrchr(spec->name, '.');
+    if (s != NULL)
+        _PyDict_SetItemId(type->tp_dict, &PyId___module__, 
+            PyUnicode_FromStringAndSize(
+                spec->name, (Py_ssize_t)(s - spec->name)));
+
     return (PyObject*)res;
 
  fail:
@@ -2434,6 +2500,12 @@ PyType_FromSpec(PyType_Spec *spec)
     return NULL;
 }
 
+PyObject *
+PyType_FromSpec(PyType_Spec *spec)
+{
+    return PyType_FromSpecWithBases(spec, NULL);
+}
+
 
 /* Internal API to look for a name through the MRO.
    This returns a borrowed reference, and doesn't set an exception! */
@@ -4763,7 +4835,7 @@ tp_new_wrapper(PyObject *self, PyObject *args, PyObject *kwds)
        object.__new__(dict).  To do this, we check that the
        most derived base that's not a heap type is this type. */
     staticbase = subtype;
-    while (staticbase && (staticbase->tp_flags & Py_TPFLAGS_HEAPTYPE))
+    while (staticbase && (staticbase->tp_new == slot_tp_new))
         staticbase = staticbase->tp_base;
     /* If staticbase is NULL now, it is a really weird type.
        In the spirit of backwards compatibility (?), just shut up. */
diff --git a/PC/python3.def b/PC/python3.def
index 0c9e25c..f16afca 100644
--- a/PC/python3.def
+++ b/PC/python3.def
@@ -1,3 +1,4 @@
+; When changing this file, run python33gen.py
 LIBRARY	"python3"
 EXPORTS
   PyArg_Parse=python33.PyArg_Parse
@@ -513,6 +514,7 @@ EXPORTS
   PyTuple_Type=python33.PyTuple_Type DATA
   PyType_ClearCache=python33.PyType_ClearCache
   PyType_FromSpec=python33.PyType_FromSpec
+  PyType_FromSpecWithBases=python33.PyType_FromSpecWithBases
   PyType_GenericAlloc=python33.PyType_GenericAlloc
   PyType_GenericNew=python33.PyType_GenericNew
   PyType_GetFlags=python33.PyType_GetFlags
diff --git a/PC/python33stub.def b/PC/python33stub.def
index 20a5868..4111d30 100644
--- a/PC/python33stub.def
+++ b/PC/python33stub.def
@@ -513,6 +513,7 @@ PyTuple_Size
 PyTuple_Type
 PyType_ClearCache
 PyType_FromSpec
+PyType_FromSpecWithBases
 PyType_GenericAlloc
 PyType_GenericNew
 PyType_GetFlags
-- 
cgit v0.12

