[Python-checkins] CVS: python/dist/src/Objects classobject.c,2.127.2.2,2.127.2.3 funcobject.c,2.37.4.1,2.37.4.2 object.c,2.124.4.7,2.124.4.8 typeobject.c,2.16.8.10,2.16.8.11

Guido van Rossum gvanrossum@users.sourceforge.net
Sat, 05 May 2001 19:31:15 -0700


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

Modified Files:
      Tag: descr-branch
	classobject.c funcobject.c object.c typeobject.c 
Log Message:
Finally we're having fun.  This set of changes makes it possible to
subclass (some) built-in types in Python.

- Introduce another metatype, the type of TypeType, dubbed TurtleType.
  Its reason for existence is so that its tp_call slot can be the
  function that is called when a regular type object is present in the
  list of base classes of a class statement; the (modified) Don
  Beaudry hook will call the type's type with (name, bases, dict), and
  the type's type happens to be TypeType; in order to make TypeType
  (itself) callable, it must have a type that defines the appropriate
  tp_call slot.  This is TurtleType.

- The tp_construct slot is changed to take args and kwds.
  TurtleType's tp_call passes those through, and so does TypeType's
  tp_call.  (The tp_constrict slots in the dict and list types don't
  do anything with their arguments yet.)

- The instance method type was generalized not to require that the
  class argument is an actual class.  Since the class wasn't used in
  an essential way, this was easy; only its repr() had to be changed.
  (It could stand some more cleanup, and should be moved to its own
  file.)

- Python function objects now have a tp_descr_get slot, which returns
  a bound (instance) method object when an object is given; without an
  object, this returns the function itself unchanged.  The latter
  behavior is not ideal, but the best I can do without changing the
  tp_descr_get API (since without an object, the class whose unbound
  method this is isn't known).

- PyGeneric_GetAttr no longer requires that all objects it finds in
  tp_dict are descriptors; non-descriptors are returned unchanged.

- The tp_construct slot for TypeType (which defines how to construct
  regular types) is defined; it takes an argument list of the form
  (name, bases, dict), so it can be used as the class creation
  function.  It currently requires that there's exactly one base,
  which must be a type object.  The subtypes it defines can override
  existing methods and define new methods, but it cannot yet override
  operators (__foo__ methods in the dict don't have an effect yet).

- The Don Beaudry hook is changed -- alternative bases now must be
  types.  If this condition is met, the same thing is done as before:
  the base's type is called with (name, bases, dict) to construct the
  new class.

Not yet implemented:

- New instance variables in subtypes (the subtypes don't have a
  __dict__ to store these).

- Overriding operators in subtypes with __foo__ methods.

- Any form of multiple inheritance.



Index: classobject.c
===================================================================
RCS file: /cvsroot/python/python/dist/src/Objects/classobject.c,v
retrieving revision 2.127.2.2
retrieving revision 2.127.2.3
diff -C2 -r2.127.2.2 -r2.127.2.3
*** classobject.c	2001/05/05 11:37:29	2.127.2.2
--- classobject.c	2001/05/06 02:31:13	2.127.2.3
***************
*** 2023,2063 ****
  instancemethod_repr(PyMethodObject *a)
  {
! 	char buf[240];
! 	PyInstanceObject *self = (PyInstanceObject *)(a->im_self);
  	PyObject *func = a->im_func;
! 	PyClassObject *class = (PyClassObject *)(a->im_class);
! 	PyObject *fclassname, *iclassname, *funcname;
! 	char *fcname, *icname, *fname;
! 	fclassname = class->cl_name;
! 	if (PyFunction_Check(func)) {
! 		funcname = ((PyFunctionObject *)func)->func_name;
! 		Py_INCREF(funcname);
  	}
- 	else {
- 		funcname = PyObject_GetAttrString(func,"__name__");
- 		if (funcname == NULL)
- 			PyErr_Clear();
- 	}
- 	if (funcname != NULL && PyString_Check(funcname))
- 		fname = PyString_AS_STRING(funcname);
  	else
! 		fname = "?";
! 	if (fclassname != NULL && PyString_Check(fclassname))
! 		fcname = PyString_AsString(fclassname);
  	else
! 		fcname = "?";
  	if (self == NULL)
! 		sprintf(buf, "<unbound method %.100s.%.100s>", fcname, fname);
  	else {
! 		iclassname = self->in_class->cl_name;
! 		if (iclassname != NULL && PyString_Check(iclassname))
! 			icname = PyString_AsString(iclassname);
! 		else
! 			icname = "?";
! 		sprintf(buf, "<method %.60s.%.60s of %.60s instance at %p>",
! 			fcname, fname, icname, self);
  	}
  	Py_XDECREF(funcname);
! 	return PyString_FromString(buf);
  }
  
--- 2023,2071 ----
  instancemethod_repr(PyMethodObject *a)
  {
! 	char buffer[240];
! 	PyObject *self = a->im_self;
  	PyObject *func = a->im_func;
! 	PyObject *klass = a->im_class;
! 	PyObject *funcname = NULL, *klassname = NULL, *result = NULL;
! 	char *sfuncname = "?", *sklassname = "?";
! 
! 	funcname = PyObject_GetAttrString(func, "__name__");
! 	if (funcname == NULL)
! 		PyErr_Clear();
! 	else if (!PyString_Check(funcname)) {
! 		Py_DECREF(funcname);
! 		funcname = NULL;
  	}
  	else
! 		sfuncname = PyString_AS_STRING(funcname);
! 	klassname = PyObject_GetAttrString(klass, "__name__");
! 	if (klassname == NULL)
! 		PyErr_Clear();
! 	else if (!PyString_Check(klassname)) {
! 		Py_DECREF(klassname);
! 		klassname = NULL;
! 	}
  	else
! 		sklassname = PyString_AS_STRING(klassname);
  	if (self == NULL)
! 		sprintf(buffer, "<unbound method %.100s.%.100s>",
! 			sklassname, sfuncname);
  	else {
! 		PyObject *selfrepr = PyObject_Repr(self);
! 		if (selfrepr == NULL)
! 			goto fail;
! 		if (!PyString_Check(selfrepr)) {
! 			Py_DECREF(selfrepr);
! 			goto fail;
! 		}
! 		sprintf(buffer, "<bound method %.60s.%.60s of %.60s>",
! 			sklassname, sfuncname, PyString_AS_STRING(selfrepr));
! 		Py_DECREF(selfrepr);
  	}
+ 	result = PyString_FromString(buffer);
+   fail:
  	Py_XDECREF(funcname);
! 	Py_XDECREF(klassname);
! 	return result;
  }
  

Index: funcobject.c
===================================================================
RCS file: /cvsroot/python/python/dist/src/Objects/funcobject.c,v
retrieving revision 2.37.4.1
retrieving revision 2.37.4.2
diff -C2 -r2.37.4.1 -r2.37.4.2
*** funcobject.c	2001/05/05 11:37:29	2.37.4.1
--- funcobject.c	2001/05/06 02:31:13	2.37.4.2
***************
*** 367,370 ****
--- 367,382 ----
  }
  
+ /* Bind a function to an object */
+ static PyObject *
+ func_descr_get(PyObject *func, PyObject *obj)
+ {
+ 	if (obj == NULL) {
+ 		Py_INCREF(func);
+ 		return func;
+ 	}
+ 	else
+ 		return PyMethod_New(func, obj, (PyObject *)obj->ob_type);
+ }
+ 
  PyTypeObject PyFunction_Type = {
  	PyObject_HEAD_INIT(&PyType_Type)
***************
*** 373,396 ****
  	sizeof(PyFunctionObject) + PyGC_HEAD_SIZE,
  	0,
! 	(destructor)func_dealloc, /*tp_dealloc*/
! 	0,		/*tp_print*/
! 	0, /*tp_getattr*/
! 	0, /*tp_setattr*/
! 	0, /*tp_compare*/
! 	(reprfunc)func_repr, /*tp_repr*/
! 	0,		/*tp_as_number*/
! 	0,		/*tp_as_sequence*/
! 	0,		/*tp_as_mapping*/
! 	0,		/*tp_hash*/
! 	function_call,	/*tp_call*/
! 	0,		/*tp_str*/
! 	(getattrofunc)func_getattro,	     /*tp_getattro*/
! 	(setattrofunc)func_setattro,	     /*tp_setattro*/
! 	0,		/* tp_as_buffer */
! 	Py_TPFLAGS_DEFAULT | Py_TPFLAGS_GC,
! 	0,		/* tp_doc */
! 	(traverseproc)func_traverse,	/* tp_traverse */
! 	0,		/* tp_clear */
! 	0,		/* tp_richcompare */
  	offsetof(PyFunctionObject, func_weakreflist), /* tp_weaklistoffset */
  };
--- 385,417 ----
  	sizeof(PyFunctionObject) + PyGC_HEAD_SIZE,
  	0,
! 	(destructor)func_dealloc,		/* tp_dealloc */
! 	0,					/* tp_print */
! 	0,					/* tp_getattr */
! 	0,					/* tp_setattr */
! 	0,					/* tp_compare */
! 	(reprfunc)func_repr,			/* tp_repr */
! 	0,					/* tp_as_number */
! 	0,					/* tp_as_sequence */
! 	0,					/* tp_as_mapping */
! 	0,					/* tp_hash */
! 	function_call,				/* tp_call */
! 	0,					/* tp_str */
! 	(getattrofunc)func_getattro,		/* tp_getattro */
! 	(setattrofunc)func_setattro,		/* tp_setattro */
! 	0,					/* tp_as_buffer */
! 	Py_TPFLAGS_DEFAULT | Py_TPFLAGS_GC,	/* tp_flags */
! 	0,					/* tp_doc */
! 	(traverseproc)func_traverse,		/* tp_traverse */
! 	0,					/* tp_clear */
! 	0,					/* tp_richcompare */
  	offsetof(PyFunctionObject, func_weakreflist), /* tp_weaklistoffset */
+ 	0,					/* tp_iter */
+ 	0,					/* tp_iternext */
+ 	0,					/* tp_methods */
+ 	0,					/* tp_members */
+ 	0,					/* tp_getset */
+ 	0,					/* tp_base */
+ 	0,					/* tp_dict */
+ 	func_descr_get,				/* tp_descr_get */
+ 	0,					/* tp_descr_set */
  };

Index: object.c
===================================================================
RCS file: /cvsroot/python/python/dist/src/Objects/object.c,v
retrieving revision 2.124.4.7
retrieving revision 2.124.4.8
diff -C2 -r2.124.4.7 -r2.124.4.8
*** object.c	2001/05/05 11:37:29	2.124.4.7
--- object.c	2001/05/06 02:31:13	2.124.4.8
***************
*** 1088,1093 ****
  	}
  	descr = PyDict_GetItem(tp->tp_dict, name);
! 	if (descr != NULL && (f = descr->ob_type->tp_descr_get) != NULL)
! 		return (*f)(descr, obj);
  	PyErr_Format(PyExc_AttributeError,
  		     "'%.50s' object has no attribute '%.400s'",
--- 1088,1098 ----
  	}
  	descr = PyDict_GetItem(tp->tp_dict, name);
! 	if (descr != NULL) {
! 		f = descr->ob_type->tp_descr_get;
! 		if (f != NULL)
! 			return (*f)(descr, obj);
! 		Py_INCREF(descr);
! 		return descr;
! 	}
  	PyErr_Format(PyExc_AttributeError,
  		     "'%.50s' object has no attribute '%.400s'",

Index: typeobject.c
===================================================================
RCS file: /cvsroot/python/python/dist/src/Objects/typeobject.c,v
retrieving revision 2.16.8.10
retrieving revision 2.16.8.11
diff -C2 -r2.16.8.10 -r2.16.8.11
*** typeobject.c	2001/05/04 21:56:53	2.16.8.10
--- typeobject.c	2001/05/06 02:31:13	2.16.8.11
***************
*** 54,64 ****
  type_call(PyTypeObject *type, PyObject *args, PyObject *kwds)
  {
- 	char *dummy = NULL;
  	PyObject *obj, *res;
- 	char buffer[100];
- 
- 	sprintf(buffer, ":<type '%.80s'>", type->tp_name);
- 	if (!PyArg_ParseTupleAndKeywords(args, kwds, buffer, &dummy))
- 		return NULL;
  
  	if (type->tp_construct == NULL) {
--- 54,58 ----
***************
*** 71,75 ****
  	if (obj == NULL)
  		return NULL;
! 	res = (type->tp_construct)(obj);
  	if (res != obj) {
  		Py_DECREF(obj);
--- 65,69 ----
  	if (obj == NULL)
  		return NULL;
! 	res = (type->tp_construct)(obj, args, kwds);
  	if (res != obj) {
  		Py_DECREF(obj);
***************
*** 115,121 ****
  	if (type->tp_flags & Py_TPFLAGS_HAVE_CLASS) {
  		descr = PyDict_GetItem(type->tp_dict, name);
! 		if (descr != NULL &&
! 		    (f = descr->ob_type->tp_descr_get) != NULL)
! 			return (*f)(descr, NULL);
  	}
  
--- 109,120 ----
  	if (type->tp_flags & Py_TPFLAGS_HAVE_CLASS) {
  		descr = PyDict_GetItem(type->tp_dict, name);
! 		if (descr != NULL) {
! 			f = descr->ob_type->tp_descr_get;
! 			if (f != NULL)
! 				return (*f)(descr, NULL);
! 			/* Not a descriptor -- a plain value */
! 			Py_INCREF(descr);
! 			return descr;
! 		}
  	}
  
***************
*** 126,136 ****
  }
  
  PyTypeObject PyType_Type = {
! 	PyObject_HEAD_INIT(&PyType_Type)
  	0,			/* Number of items for varobject */
  	"type",			/* Name of this type */
  	sizeof(PyTypeObject),	/* Basic object size */
  	0,			/* Item size for varobject */
! 	0,					/* tp_dealloc */
  	0,					/* tp_print */
  	0,			 		/* tp_getattr */
--- 125,232 ----
  }
  
+ /* Helpers for subtyping */
+ static PyObject *
+ subtype_construct(PyObject *self, PyObject *args, PyObject *kwds)
+ {
+ 	PyObject *res;
+ 
+ 	if (self == NULL) {
+ 		PyErr_SetString(PyExc_TypeError,
+ 				"can't allocate subtype instances");
+ 		return NULL;
+ 	}
+ 	res = self->ob_type->tp_base->tp_construct(self, args, kwds);
+ 	if (res == self)
+ 		Py_INCREF(self->ob_type);
+ 	return res;
+ }
+ 
+ static void
+ subtype_dealloc(PyObject *self)
+ {
+ 	self->ob_type->tp_base->tp_dealloc(self);
+ 	Py_DECREF(self->ob_type);
+ }
+ 
+ /* TypeType's constructor is called when a type is subclassed */
+ static PyObject *
+ type_construct(PyTypeObject *type, PyObject *args, PyObject *kwds)
+ {
+ 	PyObject *name, *bases, *dict, *x;
+ 	PyTypeObject *base;
+ 	char *dummy = NULL;
+ 
+ 	if (type != NULL) {
+ 		PyErr_SetString(PyExc_TypeError,
+ 				"can't construct a preallocated type");
+ 		return NULL;
+ 	}
+ 	if (!PyArg_ParseTupleAndKeywords(args, kwds, "SOO", &dummy,
+ 					 &name, &bases, &dict))
+ 		return NULL;
+ 	if (!PyTuple_Check(bases) || !PyDict_Check(dict)) {
+ 		PyErr_SetString(PyExc_TypeError,
+ 				"usage: TypeType(name, bases, dict) ");
+ 		return NULL;
+ 	}
+ 	if (PyTuple_GET_SIZE(bases) > 1) {
+ 		PyErr_SetString(PyExc_TypeError,
+ 				"can't multiple-inherit from types");
+ 		return NULL;
+ 	}
+ 	if (PyTuple_GET_SIZE(bases) < 1) {
+ 		PyErr_SetString(PyExc_TypeError,
+ 				"can't create a new type without a base type");
+ 		return NULL;
+ 	}
+ 	base = (PyTypeObject *)PyTuple_GET_ITEM(bases, 0);
+ 	if (!PyType_Check((PyObject *)base)) {
+ 		PyErr_SetString(PyExc_TypeError,
+ 				"base type must be a type");
+ 		return NULL;
+ 	}
+ 	type = PyObject_New(PyTypeObject, &PyType_Type);
+ 	if (type == NULL)
+ 		return NULL;
+ 	memset(((void *)type) + offsetof(PyTypeObject, tp_name), '\0',
+ 	       sizeof(PyTypeObject) - offsetof(PyTypeObject, tp_name));
+ 	type->tp_name = PyString_AS_STRING(name);
+ 	type->tp_flags = Py_TPFLAGS_DEFAULT;
+ 	Py_INCREF(base);
+ 	type->tp_base = base;
+ 	if (base->tp_construct)
+ 		type->tp_construct = subtype_construct;
+ 	if (base->tp_dealloc)
+ 		type->tp_dealloc = subtype_dealloc;
+ 	if (PyType_InitDict(type) < 0) {
+ 		Py_DECREF(type);
+ 		return NULL;
+ 	}
+ 	x = PyObject_CallMethod(type->tp_dict, "update", "O", dict);
+ 	if (x == NULL) {
+ 		Py_DECREF(type);
+ 		return NULL;
+ 	}
+ 	Py_DECREF(x); /* throw away None */
+ 	PyDict_SetItemString(type->tp_dict, "__name__", name);
+ 	return (PyObject *)type;
+ }
+ 
+ /* Only for dynamic types, created by type_construct() above */
+ static void
+ type_dealloc(PyTypeObject *type)
+ {
+ 	Py_XDECREF(type->tp_base);
+ 	Py_XDECREF(type->tp_dict);
+ 	PyObject_DEL(type);
+ }
+ 
  PyTypeObject PyType_Type = {
! 	PyObject_HEAD_INIT(&PyTurtle_Type)
  	0,			/* Number of items for varobject */
  	"type",			/* Name of this type */
  	sizeof(PyTypeObject),	/* Basic object size */
  	0,			/* Item size for varobject */
! 	(destructor)type_dealloc,		/* tp_dealloc */
  	0,					/* tp_print */
  	0,			 		/* tp_getattr */
***************
*** 160,163 ****
--- 256,328 ----
  	0,					/* tp_base */
  	0,					/* tp_dict */
+ 	0,					/* tp_descr_get */
+ 	0,					/* tp_descr_set */
+ 	(ternaryfunc)type_construct,		/* tp_construct */
+ };
+ 
+ 
+ /* The "turtle" type is named after the expression "turtles all the way down",
+    a reference to the fact that it is its own type.  We get the progression:
+    x                => {} # for example
+    type(x)          => DictType
+    type(DictType)   => TypeType
+    type(TypeType)   => TurtleType
+    type(TurtleType) => TurtleType
+    type(TurtleType) => TurtleType
+    type(TurtleType) => TurtleType
+    .
+    .
+    .
+    It's from an old story often told about Bertrand Russel, popularized most
+    recently by Stephen Hawking; do a Google search for the phrase to find out
+    more.  The oldest turtle reference in the Python archives seems to be:
+    http://mail.python.org/pipermail/types-sig/1998-November/000084.html */
+ 
+ static PyObject *
+ turtle_call(PyTypeObject *metatype, PyObject *args, PyObject *kwds)
+ {
+ 	if (metatype->tp_construct == NULL) {
+ 		PyErr_Format(PyExc_TypeError,
+ 			     "can't subclass '.%100s' objects yet",
+ 			     metatype->tp_name);
+ 		return NULL;
+ 	}
+ 	return (*metatype->tp_construct)(NULL, args, kwds);
+ }
+ 
+ PyTypeObject PyTurtle_Type = {
+ 	PyObject_HEAD_INIT(&PyTurtle_Type)
+ 	0,			/* Number of items for varobject */
+ 	"turtle",		/* Name of this type */
+ 	sizeof(PyTypeObject),	/* Basic object size */
+ 	0,			/* Item size for varobject */
+ 	0,					/* tp_dealloc */
+ 	0,					/* tp_print */
+ 	0,			 		/* tp_getattr */
+ 	0,					/* tp_setattr */
+ 	0,					/* tp_compare */
+ 	(reprfunc)type_repr,			/* tp_repr */
+ 	0,					/* tp_as_number */
+ 	0,					/* tp_as_sequence */
+ 	0,					/* tp_as_mapping */
+ 	0,					/* tp_hash */
+ 	(ternaryfunc)turtle_call,		/* tp_call */
+ 	0,					/* tp_str */
+ 	(getattrofunc)type_getattro,		/* tp_getattro */
+ 	0,					/* tp_setattro */
+ 	0,					/* tp_as_buffer */
+ 	Py_TPFLAGS_DEFAULT,			/* tp_flags */
+ 	"Define the behavior of a particular type of object.", /* tp_doc */
+ 	0,					/* tp_traverse */
+ 	0,					/* tp_clear */
+ 	0,					/* tp_richcompare */
+ 	0,					/* tp_weaklistoffset */
+ 	0,					/* tp_iter */
+ 	0,					/* tp_iternext */
+ 	0,					/* tp_methods */
+ 	type_members,				/* tp_members */
+ 	type_getsets,				/* tp_getset */
+ 	&PyType_Type,				/* tp_base */
+ 	0,					/* tp_dict */
  };
  
***************
*** 331,343 ****
  		type->tp_flags |= base->tp_flags & Py_TPFLAGS_HAVE_SEQUENCE_IN;
  	}
- 	if (!(type->tp_flags & Py_TPFLAGS_GC) &&
- 	    (base->tp_flags & Py_TPFLAGS_GC) &&
- 	    (type->tp_flags & Py_TPFLAGS_HAVE_RICHCOMPARE/*GC slots exist*/) &&
- 	    (!type->tp_traverse && !type->tp_clear)) {
- 		type->tp_flags |= Py_TPFLAGS_GC;
- 		type->tp_basicsize += PyGC_HEAD_SIZE;
- 		COPYSLOT(tp_traverse);
- 		COPYSLOT(tp_clear);
- 	}
  	if ((type->tp_flags & Py_TPFLAGS_HAVE_INPLACEOPS) !=
  	    (base->tp_flags & Py_TPFLAGS_HAVE_INPLACEOPS)) {
--- 496,499 ----
***************
*** 359,362 ****
--- 515,527 ----
  	COPYSLOT(tp_name);
  	COPYSLOT(tp_basicsize);
+ 	if (!(type->tp_flags & Py_TPFLAGS_GC) &&
+ 	    (base->tp_flags & Py_TPFLAGS_GC) &&
+ 	    (type->tp_flags & Py_TPFLAGS_HAVE_RICHCOMPARE/*GC slots exist*/) &&
+ 	    (!type->tp_traverse && !type->tp_clear)) {
+ 		type->tp_flags |= Py_TPFLAGS_GC;
+ 		type->tp_basicsize += PyGC_HEAD_SIZE;
+ 		COPYSLOT(tp_traverse);
+ 		COPYSLOT(tp_clear);
+ 	}
  	COPYSLOT(tp_itemsize);
  	COPYSLOT(tp_dealloc);