[Python-checkins] CVS: python/dist/src/Objects typeobject.c,2.16.8.49,2.16.8.50

Guido van Rossum gvanrossum@users.sourceforge.net
Fri, 29 Jun 2001 08:03:00 -0700


Update of /cvsroot/python/python/dist/src/Objects
In directory usw-pr-cvs1:/tmp/cvs-serv27650

Modified Files:
      Tag: descr-branch
	typeobject.c 
Log Message:
First cut at dynamic types.  Too much code moved around to describe
the changes in detail.  Specification of what changed:

If you set ``__dynamic__ = 1'' is a class statement that creates a new
type, the new type is dynamic.  A new type is also dynamic if at least
one of its base types is dynamic and no __dynamic__ flag is set in the
class statement.  By setting ``__dynamic__ = 0'' in the class
statement you can force a static type even if some bases are dynamic.

For a dynamic type t:

- t.__dict__ is t.__defined__      # they are the same object

- type(t.__dict__) is dictionary   # not a proxy

- t.<name> = <value> is equivalent to t.__dict__['<name>'] = <value>

- attribute lookup for dynamic types and their instances searches the
  __defined__ dict of the type and its base types, in MRO sequence
  (even if the base class is static)


Index: typeobject.c
===================================================================
RCS file: /cvsroot/python/python/dist/src/Objects/typeobject.c,v
retrieving revision 2.16.8.49
retrieving revision 2.16.8.50
diff -C2 -r2.16.8.49 -r2.16.8.50
*** typeobject.c	2001/06/29 14:58:30	2.16.8.49
--- typeobject.c	2001/06/29 15:02:58	2.16.8.50
***************
*** 36,39 ****
--- 36,43 ----
  		return Py_None;
  	}
+ 	if (type->tp_flags & Py_TPFLAGS_DYNAMICTYPE) {
+ 		Py_INCREF(type->tp_dict);
+ 		return type->tp_dict;
+ 	}
  	return PyDictProxy_New(type->tp_dict);
  }
***************
*** 46,49 ****
--- 50,57 ----
  		return Py_None;
  	}
+ 	if (type->tp_flags & Py_TPFLAGS_DYNAMICTYPE) {
+ 		Py_INCREF(type->tp_defined);
+ 		return type->tp_defined;
+ 	}
  	return PyDictProxy_New(type->tp_defined);
  }
***************
*** 371,379 ****
  	PyObject *name, *bases, *dict;
  	static char *kwlist[] = {"name", "bases", "dict", 0};
! 	PyObject *slots;
! 	PyTypeObject *type, *base;
  	etype *et;
  	struct memberlist *mp;
! 	int i, nbases, nslots, slotoffset;
  
  	if (PyTuple_Check(args) && PyTuple_GET_SIZE(args) == 1 &&
--- 379,387 ----
  	PyObject *name, *bases, *dict;
  	static char *kwlist[] = {"name", "bases", "dict", 0};
! 	PyObject *slots, *tmp;
! 	PyTypeObject *type, *base, *tmptype;
  	etype *et;
  	struct memberlist *mp;
! 	int i, nbases, nslots, slotoffset, dynamic;
  
  	if (PyTuple_Check(args) && PyTuple_GET_SIZE(args) == 1 &&
***************
*** 386,414 ****
  
  	/* Check arguments */
! 	if (!PyArg_ParseTupleAndKeywords(args, kwds, "SOO:type", kwlist,
! 					 &name, &bases, &dict))
  		return NULL;
! 	if (PyTuple_Check(bases)) {
! 		int i, n;
! 		n = PyTuple_GET_SIZE(bases);
! 		for (i = 0; i < n; i++) {
! 			PyObject *base_i = PyTuple_GET_ITEM(bases, i);
! 			PyTypeObject *type_i = base_i->ob_type;
! 			if (issubtype(metatype, type_i))
! 				continue;
! 			if (issubtype(type_i, metatype)) {
! 				metatype = type_i;
! 				continue;
! 			}
! 			PyErr_SetString(PyExc_TypeError,
! 					"metaclass conflict among bases");
! 			return NULL;
  		}
! 		if (metatype->tp_new != type_new)
! 			return metatype->tp_new(metatype, args, kwds);
  	}
  
! 	/* Adjust empty bases */
! 	nbases = PyTuple_GET_SIZE(bases);
  	if (nbases == 0) {
  		bases = Py_BuildValue("(O)", &PyBaseObject_Type);
--- 394,425 ----
  
  	/* Check arguments */
! 	if (!PyArg_ParseTupleAndKeywords(args, kwds, "SO!O!:type", kwlist,
! 					 &name,
! 					 &PyTuple_Type, &bases,
! 					 &PyDict_Type, &dict))
  		return NULL;
! 
! 	/* Determine the proper metatype to deal with this,
! 	   and check for metatype conflicts while we're at it.
! 	   Note that if some other metatype wins to contract,
! 	   it's possible that its instances are not types. */
! 	nbases = PyTuple_GET_SIZE(bases);
! 	for (i = 0; i < nbases; i++) {
! 		tmp = PyTuple_GET_ITEM(bases, i);
! 		tmptype = tmp->ob_type;
! 		if (issubtype(metatype, tmptype))
! 			continue;
! 		if (issubtype(tmptype, metatype)) {
! 			metatype = tmptype;
! 			continue;
  		}
! 		PyErr_SetString(PyExc_TypeError,
! 				"metatype conflict among bases");
! 		return NULL;
  	}
+ 	if (metatype->tp_new != type_new) /* Pass it to the winner */
+ 		return metatype->tp_new(metatype, args, kwds);
  
! 	/* Adjust for empty tuple bases */
  	if (nbases == 0) {
  		bases = Py_BuildValue("(O)", &PyBaseObject_Type);
***************
*** 419,424 ****
  	else
  		Py_INCREF(bases);
  
! 	/* Calculate best base */
  	base = best_base(bases);
  	if (base == NULL)
--- 430,437 ----
  	else
  		Py_INCREF(bases);
+ 
+ 	/* XXX From here until type is allocated, "return NULL" leaks bases! */
  
! 	/* Calculate best base, and check that all bases are type objects */
  	base = best_base(bases);
  	if (base == NULL)
***************
*** 431,434 ****
--- 444,475 ----
  	}
  
+ 	/* Should this be a dynamic class (i.e. modifiable __dict__)? */
+ 	tmp = PyDict_GetItemString(dict, "__dynamic__");
+ 	if (tmp != NULL) {
+ 		/* The class author has a preference */
+ 		dynamic = PyObject_IsTrue(tmp);
+ 		Py_DECREF(tmp);
+ 		if (dynamic < 0)
+ 			return NULL;
+ 	}
+ 	else {
+ 		/* Make a new class dynamic if any of its bases is dynamic.
+ 		   This is not always the same as inheriting the __dynamic__
+ 		   class attribute! */
+ 		dynamic = 0;
+ 		for (i = 0; i < nbases; i++) {
+ 			tmptype = (PyTypeObject *)PyTuple_GET_ITEM(bases, i);
+ 			if (tmptype->tp_flags & Py_TPFLAGS_DYNAMICTYPE) {
+ 				dynamic = 1;
+ 				break;
+ 			}
+ 		}
+ 
+ 		/* Set the __dynamic__ attribute */
+ 		if (PyDict_SetItemString(dict, "__dynamic__",
+ 					 dynamic ? Py_True : Py_False) < 0)
+ 			return NULL;
+ 	}
+ 
  	/* Check for a __slots__ sequence variable in dict, and count it */
  	slots = PyDict_GetItemString(dict, "__slots__");
***************
*** 457,460 ****
--- 498,504 ----
  		nslots = 1;
  
+ 	/* XXX From here until type is safely allocated,
+ 	   "return NULL" may leak slots! */
+ 
  	/* Allocate the type object */
  	type = (PyTypeObject *)metatype->tp_alloc(metatype, nslots);
***************
*** 471,474 ****
--- 515,520 ----
  	type->tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HEAPTYPE |
  		Py_TPFLAGS_BASETYPE;
+ 	if (dynamic)
+ 		type->tp_flags |= Py_TPFLAGS_DYNAMICTYPE;
  	type->tp_as_number = &et->as_number;
  	type->tp_as_sequence = &et->as_sequence;
***************
*** 515,519 ****
  
  	/* Special case some slots */
! 	if (type->tp_dictoffset != 0) {
  		if (base->tp_getattr == NULL && base->tp_getattro == NULL)
  			type->tp_getattro = PyObject_GenericGetAttr;
--- 561,565 ----
  
  	/* Special case some slots */
! 	if (type->tp_dictoffset != 0 || nslots > 0) {
  		if (base->tp_getattr == NULL && base->tp_getattro == NULL)
  			type->tp_getattro = PyObject_GenericGetAttr;
***************
*** 537,548 ****
  }
  
  static PyObject *
  type_getattro(PyTypeObject *type, PyObject *name)
  {
  	if (type->tp_dict == NULL) {
  		if (PyType_InitDict(type) < 0)
  			return NULL;
  	}
! 	return PyObject_GenericGetAttr((PyObject *)type, name);
  }
  
--- 583,641 ----
  }
  
+ /* Internal API to look for a name through the MRO.
+    This returns a borrowed reference, and doesn't set an exception! */
+ PyObject *
+ _PyType_Lookup(PyTypeObject *type, PyObject *name)
+ {
+ 	int i, n;
+ 	PyObject *mro = type->tp_mro, *res;
+ 
+ 	assert(PyTuple_Check(mro));
+ 	n = PyTuple_GET_SIZE(mro);
+ 	for (i = 0; i < n; i++) {
+ 		type = (PyTypeObject *) PyTuple_GET_ITEM(mro, i);
+ 		assert(PyType_Check(type));
+ 		assert(type->tp_dict && PyDict_Check(type->tp_dict));
+ 		res = PyDict_GetItem(type->tp_dict, name);
+ 		if (res != NULL)
+ 			return res;
+ 	}
+ 	return NULL;
+ }
+ 
  static PyObject *
  type_getattro(PyTypeObject *type, PyObject *name)
  {
+ 	PyObject *descr, *res;
+ 	descrgetfunc f;
+ 
  	if (type->tp_dict == NULL) {
  		if (PyType_InitDict(type) < 0)
  			return NULL;
+ 	}
+ 	descr = PyObject_GenericGetAttr((PyObject *)type, name);
+ 	if (descr == NULL) {
+ 		descr = _PyType_Lookup(type, name);
+ 		if (descr == NULL)
+ 			return NULL;
+ 		PyErr_Clear();
+ 		Py_INCREF(descr);
+ 	}
+ 	f = descr->ob_type->tp_descr_get;
+ 	if (f != NULL) {
+ 		res = f(descr, NULL);
+ 		Py_DECREF(descr);
+ 		return res;
  	}
! 	return descr;
! }
! 
! static int
! type_setattro(PyTypeObject *type, PyObject *name, PyObject *value)
! {
! 	if (type->tp_flags & Py_TPFLAGS_DYNAMICTYPE)
! 		return PyObject_GenericSetAttr((PyObject *)type, name, value);
! 	PyErr_SetString(PyExc_TypeError, "can't set type attributes");
! 	return -1;
  }
  
***************
*** 589,593 ****
  	0,					/* tp_str */
  	(getattrofunc)type_getattro,		/* tp_getattro */
! 	0,					/* tp_setattro */
  	0,					/* tp_as_buffer */
  	Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */
--- 682,686 ----
  	0,					/* tp_str */
  	(getattrofunc)type_getattro,		/* tp_getattro */
! 	(setattrofunc)type_setattro,		/* tp_setattro */
  	0,					/* tp_as_buffer */
  	Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */
***************
*** 994,1008 ****
  	}
  
- 	/* Initialize tp_dict from tp_defined */
- 	type->tp_dict = PyDict_Copy(dict);
- 	if (type->tp_dict == NULL)
- 		return -1;
- 
- 	/* Inherit base class slots */
- 	if (base) {
- 		if (inherit_slots(type, base) < 0)
- 			return -1;
- 	}
- 
  	/* Calculate method resolution order */
  	x = method_resolution_order(type);
--- 1087,1090 ----
***************
*** 1015,1032 ****
  	Py_DECREF(x);
  
! 	/* Inherit methods, updating from last base backwards */
! 	bases = type->tp_mro;
! 	assert(bases != NULL);
! 	assert(PyTuple_Check(bases));
! 	n = PyTuple_GET_SIZE(bases);
!  	for (i = n; --i >= 0; ) {
! 		base = (PyTypeObject *)PyTuple_GET_ITEM(bases, i);
! 		assert(PyType_Check(base));
! 		x = base->tp_defined;
! 		if (x != NULL) {
! 			x = PyObject_CallMethod(type->tp_dict, "update","O",x);
! 			if (x == NULL)
  				return -1;
- 			Py_DECREF(x); /* throw away None */
  		}
  	}
--- 1097,1125 ----
  	Py_DECREF(x);
  
! 	/* Initialize tp_dict */
! 	if (type->tp_flags & Py_TPFLAGS_DYNAMICTYPE) {
! 		/* For a dynamic type. tp_dict *is* tp_defined */
! 		Py_INCREF(type->tp_defined);
! 		type->tp_dict = type->tp_defined;
! 	}
! 	else {
! 		/* For a static type, tp_dict is the consolidation
! 		   of the tp_defined of its bases in MRO.  Earlier
! 		   bases override later bases; since d.update() works
! 		   the other way, we walk the MRO sequence backwards. */
! 
! 		type->tp_dict = PyDict_New();
! 		if (type->tp_dict == NULL)
! 			return -1;
! 		bases = type->tp_mro;
! 		assert(bases != NULL);
! 		assert(PyTuple_Check(bases));
! 		n = PyTuple_GET_SIZE(bases);
! 		for (i = n; --i >= 0; ) {
! 			base = (PyTypeObject *)PyTuple_GET_ITEM(bases, i);
! 			assert(PyType_Check(base));
! 			x = base->tp_defined;
! 			if (x != NULL && PyDict_Update(type->tp_dict, x) < 0)
  				return -1;
  		}
  	}