Cosmetics:

- Add comment blocks explaining add_operators() and override_slots().
  (This file could use some more explaining, but this is all I had
  breath for today. :)

- Renamed the argument 'base' of add_wrappers() to 'wraps' because
  it's not a base class (which is what the 'base' identifier is used
  for elsewhere).

Small nits:

- Fix add_tp_new_wrapper() to avoid overwriting an existing __new__
  descriptor in tp_defined.

- In add_operators(), check the return value of add_tp_new_wrapper().

Functional change:

- Remove the tp_new functionality from PyBaseObject_Type; this means
  you can no longer instantiate the 'object' type.  It's only useful
  as a base class.

- To make up for the above loss, add tp_new to dynamic types.  This
  has to be done in a hackish way (after override_slots() has been
  called, with an explicit call to add_tp_new_wrapper() at the very
  end) because otherwise I ran into recursive calls of slot_tp_new().
  Sigh.
This commit is contained in:
Guido van Rossum 2001-08-07 16:40:56 +00:00
parent cd738364ce
commit f040ede6e8
1 changed files with 65 additions and 10 deletions

View File

@ -447,6 +447,7 @@ solid_base(PyTypeObject *type)
staticforward void object_dealloc(PyObject *);
staticforward int object_init(PyObject *, PyObject *, PyObject *);
staticforward int add_tp_new_wrapper(PyTypeObject *);
static PyObject *
type_new(PyTypeObject *metatype, PyObject *args, PyObject *kwds)
@ -662,6 +663,17 @@ type_new(PyTypeObject *metatype, PyObject *args, PyObject *kwds)
/* Override slots that deserve it */
override_slots(type, type->tp_defined);
/* Special hack for __new__ */
if (type->tp_new == NULL) {
/* Can't do this earlier, or some nasty recursion happens. */
type->tp_new = PyType_GenericNew;
if (add_tp_new_wrapper(type) < 0) {
Py_DECREF(type);
return NULL;
}
}
return (PyObject *)type;
}
@ -893,7 +905,7 @@ PyTypeObject PyBaseObject_Type = {
0, /* tp_dictoffset */
object_init, /* tp_init */
PyType_GenericAlloc, /* tp_alloc */
PyType_GenericNew, /* tp_new */
0, /* tp_new */
object_free, /* tp_free */
};
@ -920,18 +932,18 @@ add_methods(PyTypeObject *type, PyMethodDef *meth)
}
static int
add_wrappers(PyTypeObject *type, struct wrapperbase *base, void *wrapped)
add_wrappers(PyTypeObject *type, struct wrapperbase *wraps, void *wrapped)
{
PyObject *dict = type->tp_defined;
for (; base->name != NULL; base++) {
for (; wraps->name != NULL; wraps++) {
PyObject *descr;
if (PyDict_GetItemString(dict, base->name))
if (PyDict_GetItemString(dict, wraps->name))
continue;
descr = PyDescr_NewWrapper(type, base, wrapped);
descr = PyDescr_NewWrapper(type, wraps, wrapped);
if (descr == NULL)
return -1;
if (PyDict_SetItemString(dict, base->name, descr) < 0)
if (PyDict_SetItemString(dict, wraps->name, descr) < 0)
return -1;
Py_DECREF(descr);
}
@ -1870,13 +1882,37 @@ static struct PyMethodDef tp_new_methoddef[] = {
static int
add_tp_new_wrapper(PyTypeObject *type)
{
PyObject *func = PyCFunction_New(tp_new_methoddef, (PyObject *)type);
PyObject *func;
if (PyDict_GetItemString(type->tp_defined, "__new__") != NULL)
return 0;
func = PyCFunction_New(tp_new_methoddef, (PyObject *)type);
if (func == NULL)
return -1;
return PyDict_SetItemString(type->tp_defined, "__new__", func);
}
/* This function is called by PyType_InitDict() to populate the type's
dictionary with method descriptors for function slots. For each
function slot (like tp_repr) that's defined in the type, one or
more corresponding descriptors are added in the type's tp_defined
dictionary under the appropriate name (like __repr__). Some
function slots cause more than one descriptor to be added (for
example, the nb_add slot adds both __add__ and __radd__
descriptors) and some function slots compete for the same
descriptor (for example both sq_item and mp_subscript generate a
__getitem__ descriptor). This only adds new descriptors and
doesn't overwrite entries in tp_defined that were previously
defined. The descriptors contain a reference to the C function
they must call, so that it's safe if they are copied into a
subtype's __dict__ and the subtype has a different C function in
its slot -- calling the method defined by the descriptor will call
the C function that was used to create it, rather than the C
function present in the slot when it is called. (This is important
because a subtype may have a C function in the slot that calls the
method from the dictionary, and we want to avoid infinite recursion
here.) */
static int
add_operators(PyTypeObject *type)
{
@ -1966,13 +2002,16 @@ add_operators(PyTypeObject *type)
ADD(type->tp_descr_set, tab_descr_set);
ADD(type->tp_init, tab_init);
if (type->tp_new != NULL)
add_tp_new_wrapper(type);
if (type->tp_new != NULL) {
if (add_tp_new_wrapper(type) < 0)
return -1;
}
return 0;
}
/* Slot wrappers that call the corresponding __foo__ slot */
/* Slot wrappers that call the corresponding __foo__ slot. See comments
below at override_slots() for more explanation. */
#define SLOT0(SLOTNAME, OPNAME) \
static PyObject * \
@ -2295,6 +2334,22 @@ slot_tp_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
return x;
}
/* This is called at the very end of type_new() (even after
PyType_InitDict()) to complete the initialization of dynamic types.
The dict argument is the dictionary argument passed to type_new(),
which is the local namespace of the class statement, in other
words, it contains the methods. For each special method (like
__repr__) defined in the dictionary, the corresponding function
slot in the type object (like tp_repr) is set to a special function
whose name is 'slot_' followed by the slot name and whose signature
is whatever is required for that slot. These slot functions look
up the corresponding method in the type's dictionary and call it.
The slot functions have to take care of the various peculiarities
of the mapping between slots and special methods, such as mapping
one slot to multiple methods (tp_richcompare <--> __le__, __lt__
etc.) or mapping multiple slots to a single method (sq_item,
mp_subscript <--> __getitem__). */
static void
override_slots(PyTypeObject *type, PyObject *dict)
{