summaryrefslogtreecommitdiffstats
path: root/Objects/rangeobject.c
Commit message (Collapse)AuthorAgeFilesLines
* Issue #14783: Backport changes from 3.2.Chris Jerdonek2012-10-081-1/+2
|
* Issues #16029, #16030: Fix pickling and repr of large xranges.Mark Dickinson2012-09-281-5/+29
|
* Untabify C files. Will watch buildbots.Antoine Pitrou2010-05-091-210/+210
|
* Silence MSVC warning about unary minus applied to unsigned type.Mark Dickinson2009-11-161-1/+1
|
* Avoid signed overflow in some xrange calculations, and extendMark Dickinson2009-11-151-34/+44
| | | | | | xrange tests to cover some special cases that caused problems in py3k. This is a partial backport of r76292-76293 (see issue #7298.)
* Added better pickling support to xrange objects.Alexandre Vassalotti2008-06-101-3/+3
| | | | Cleaned up the unit test.
* Issue 2582: Fix pickling of xrange objects.Alexandre Vassalotti2008-06-101-0/+11
|
* This reverts r63675 based on the discussion in this thread:Gregory P. Smith2008-06-091-3/+3
| | | | | | | http://mail.python.org/pipermail/python-dev/2008-June/079988.html Python 2.6 should stick with PyString_* in its codebase. The PyBytes_* names in the spirit of 3.0 are available via a #define only. See the email thread.
* Renamed PyString to PyBytesChristian Heimes2008-05-261-3/+3
|
* Remove unnecessary modulo division.Raymond Hettinger2008-02-081-1/+1
| | | | The preceding test guarantees that 0 <= i < len.
* Remove "static forward" declaration. Move constructorsMartin v. Löwis2006-04-111-47/+45
| | | | after the type objects.
* Make xrange more Py_ssize_t aware, by assuming a Py_ssize_t is always atThomas Wouters2006-04-041-7/+0
| | | | | | | | | | | least as big as a long. I believe this to be a safe assumption that is being made in many parts of CPython, but a check could be added. len(xrange(sys.maxint)) works now, so fix the testsuite's odd exception for 64-bit platforms too. It also fixes 'zip(xrange(sys.maxint), it)' as a portable-ish (if expensive) alternative to enumerate(it); since zip() now calls len(), this was breaking on (real) 64-bit platforms. No additional test was added for that behaviour.
* Remove unnecessary casts in type object initializers.Georg Brandl2006-03-301-38/+38
|
* Merge ssize_t branch.Martin v. Löwis2006-02-151-7/+7
|
* Renamed _length_cue() to __length_hint__(). See:Armin Rigo2006-02-111-2/+2
| | | | http://mail.python.org/pipermail/python-dev/2006-February/060524.html
* Convert iterator __len__() methods to a private API.Raymond Hettinger2005-09-241-8/+10
|
* Disallow keyword arguments for type constructors that don't use them.Georg Brandl2005-08-261-0/+3
| | | | (fixes bug #1119418)
* Remove PyRange_New().Raymond Hettinger2004-12-031-45/+0
|
* Bug 1003935: xrange overflowsTim Peters2004-08-081-1/+16
| | | | | | | | | | | | | | | Added XXX comment about why the undocumented PyRange_New() API function is too broken to be worth the considerable pain of repairing. Changed range_new() to stop using PyRange_New(). This fixes a variety of bogus errors. Nothing in the core uses PyRange_New() now. Documented that xrange() is intended to be simple and fast, and that CPython restricts its arguments, and length of its result sequence, to native C longs. Added some tests that failed before the patch, and repaired a test that relied on a bogus OverflowError getting raised.
* Trimmed trailing whitespace.Tim Peters2004-08-081-5/+5
|
* Add a missing decref.Michael W. Hudson2004-08-021-0/+1
|
* Tidied up the implementations of reversed (including the custom onesRaymond Hettinger2004-03-101-1/+13
| | | | | | | | | | | | | | | | | for xrange and list objects). * list.__reversed__ now checks the length of the sequence object before calling PyList_GET_ITEM() because the mutable could have changed length. * all three implementations are now tranparent with respect to length and maintain the invariant len(it) == len(list(it)) even when the underlying sequence mutates. * __builtin__.reversed() now frees the underlying sequence as soon as the iterator is exhausted. * the code paths were rearranged so that the most common paths do not require a jump.
* Implement and apply PEP 322, reverse iterationRaymond Hettinger2003-11-061-1/+36
|
* Renamed PyObject_GenericGetIter to PyObject_SelfIterRaymond Hettinger2003-03-171-1/+1
| | | | | | to more accurately describe what the function does. Suggested by Thomas Wouters.
* Created PyObject_GenericGetIter().Raymond Hettinger2003-03-171-8/+1
| | | | Factors out the common case of returning self.
* Update comments about the performance of xrange().Raymond Hettinger2002-12-111-2/+2
|
* Restore attribute access so that the following work again:Raymond Hettinger2002-11-071-1/+1
| | | | | dir(xrange(10)) xrange(10).__getitem__(4)
* Untested code for 64-bit platforms. range_length() is declared as intGuido van Rossum2002-09-111-1/+8
| | | | | | | | | | but returns r->len which is a long. This doesn't even cause a warning on 32-bit platforms, but can return bogus values on 64-bit platforms (and should cause a compiler warning). Fix this by inserting a range check when LONG_MAX != INT_MAX, and adding an explicit cast to (int) when the test passes. When r->len is out of range, PySequence_Size() and hence len() will report an error (but an iterator will still work).
* staticforward bites the dust.Jeremy Hylton2002-07-171-2/+2
| | | | | | | | | | | | | | | The staticforward define was needed to support certain broken C compilers (notably SCO ODT 3.0, perhaps early AIX as well) botched the static keyword when it was used with a forward declaration of a static initialized structure. Standard C allows the forward declaration with static, and we've decided to stop catering to broken C compilers. (In fact, we expect that the compilers are all fixed eight years later.) I'm leaving staticforward and statichere defined in object.h as static. This is only for backwards compatibility with C extensions that might still use it. XXX I haven't updated the documentation.
* Remove the next() method -- one is supplied automatically byGuido van Rossum2002-07-161-10/+1
| | | | | | PyType_Ready() because the tp_iternext slot is set. Also removed the redundant (and expensive!) call to raise StopIteration from rangeiter_next().
* Patch #568124: Add doc string macros.Martin v. Löwis2002-06-131-2/+2
|
* Pyrangeiter_Type && range_iter should be staticNeal Norwitz2002-06-061-5/+5
|
* Skip Montanaro's patch, SF 559833, exposing xrange type in builtins.Raymond Hettinger2002-06-051-2/+84
| | | | | Also, added more regression tests to cover the new type and test its conformity with range().
* SF 564601 adding rangeiterobject to make xrange() iterate like range().Raymond Hettinger2002-06-051-46/+91
|
* Inverted test for small speedupRaymond Hettinger2002-06-041-5/+4
|
* Patch #551410: Implement tp_getiter.Martin v. Löwis2002-05-081-1/+51
|
* Remove old deprecated features from the xrange object.Fred Drake2002-05-021-273/+40
|
* Fix attribute access for the xrange objects. The tp_getattr and tp_getattroFred Drake2002-05-021-31/+38
| | | | | | handlers were both set, but were not compatible. This change uses only the tp_getattro handler with a more "modern" approach. This fixes SF bug #551285.
* Remove PyMalloc_New and PyMalloc_Del.Neil Schemenauer2002-04-121-2/+2
|
* Use pymalloc if it's enabled.Neil Schemenauer2002-03-221-2/+2
|
* Fix spelling mistakes. Bugfix candidates.Neal Norwitz2002-01-291-1/+1
|
* long_mul(): The PyNumber_Multiply() call can return a long if theGuido van Rossum2001-12-041-0/+6
| | | | | result would overflow an int. Check for this. (SF bug #488482, Armin Rigo.)
* repr's converted to using PyString_FromFormat() instead of sprintf'ingBarry Warsaw2001-08-241-19/+19
| | | | | | into a hardcoded char* buffer. Closes patch #454743.
* Patch #427190: Implement and use METH_NOARGS and METH_O.Martin v. Löwis2001-08-161-4/+1
|
* Merge of descr-branch back into trunk.Tim Peters2001-08-021-16/+28
|
* Re-add 'advanced' xrange features, adding DeprecationWarnings as discussedThomas Wouters2001-07-091-16/+219
| | | | | on python-dev. The features will still vanish, however, just one release later.
* Rip out the fancy behaviors of xrange that nobody uses: repeat, slice,Guido van Rossum2001-07-051-222/+15
| | | | | contains, tolist(), and the start/stop/step attributes. This includes removing the 4th ('repeat') argument to PyRange_New().
* SF patch #103158 by Greg Ball: Don't do unsafe arithmetic in xrangeGuido van Rossum2001-01-151-10/+80
| | | | | | | | | | | | | | | object. This fixes potential overflows in xrange()'s internal calculations on 64-bit platforms. The fix is complicated because the sq_length slot function can only return an int; we want to support xrange(sys.maxint), which is a 64-bit quantity on most 64-bit platforms (except Win64). The solution is hacky but the best possible: when the range is that long, we can use it in a for loop but we can't ask for its length (nor can we actually iterate beyond 2**31-1, because the sq_item slot function has the same restrictions on its arguments. Fixing those restrictions is a project for another day...
* Test for NULL returned from PyObject_NEW().Guido van Rossum2000-12-141-0/+3
|
* Fixed support for containment test when a negative step is used; thisFred Drake2000-11-081-8/+34
| | | | | | | *really* closes bug #121965. Added three attributes to the xrange object: start, stop, and step. These are the same as for the slice objects.
' href='#n1083'>1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344
/* Descriptors -- a new, flexible way to describe attributes */

#include "Python.h"
#include "structmember.h" /* Why is this not included in Python.h? */

static void
descr_dealloc(PyDescrObject *descr)
{
	_PyObject_GC_UNTRACK(descr);
	Py_XDECREF(descr->d_type);
	Py_XDECREF(descr->d_name);
	PyObject_GC_Del(descr);
}

static PyObject *
descr_name(PyDescrObject *descr)
{
	if (descr->d_name != NULL && PyUnicode_Check(descr->d_name))
		return descr->d_name;
	return NULL;
}

static PyObject *
descr_repr(PyDescrObject *descr, char *format)
{
	PyObject *name = NULL;
	if (descr->d_name != NULL && PyUnicode_Check(descr->d_name))
		name = descr->d_name;

	return PyUnicode_FromFormat(format, name, "?", descr->d_type->tp_name);
}

static PyObject *
method_repr(PyMethodDescrObject *descr)
{
	return descr_repr((PyDescrObject *)descr,
			  "<method '%V' of '%s' objects>");
}

static PyObject *
member_repr(PyMemberDescrObject *descr)
{
	return descr_repr((PyDescrObject *)descr,
			  "<member '%V' of '%s' objects>");
}

static PyObject *
getset_repr(PyGetSetDescrObject *descr)
{
	return descr_repr((PyDescrObject *)descr,
			  "<attribute '%V' of '%s' objects>");
}

static PyObject *
wrapperdescr_repr(PyWrapperDescrObject *descr)
{
	return descr_repr((PyDescrObject *)descr,
			  "<slot wrapper '%V' of '%s' objects>");
}

static int
descr_check(PyDescrObject *descr, PyObject *obj, PyObject **pres)
{
	if (obj == NULL) {
		Py_INCREF(descr);
		*pres = (PyObject *)descr;
		return 1;
	}
	if (!PyObject_TypeCheck(obj, descr->d_type)) {
		PyErr_Format(PyExc_TypeError,
			     "descriptor '%V' for '%s' objects "
			     "doesn't apply to '%s' object",
			     descr_name((PyDescrObject *)descr), "?",
			     descr->d_type->tp_name,
			     obj->ob_type->tp_name);
		*pres = NULL;
		return 1;
	}
	return 0;
}

static PyObject *
classmethod_get(PyMethodDescrObject *descr, PyObject *obj, PyObject *type)
{
	/* Ensure a valid type.  Class methods ignore obj. */
	if (type == NULL) {
		if (obj != NULL)
			type = (PyObject *)obj->ob_type;
		else {
			/* Wot - no type?! */
			PyErr_Format(PyExc_TypeError,
				     "descriptor '%V' for type '%s' "
				     "needs either an object or a type",
				     descr_name((PyDescrObject *)descr), "?",
				     descr->d_type->tp_name);
			return NULL;
		}
	}
	if (!PyType_Check(type)) {
		PyErr_Format(PyExc_TypeError,
			     "descriptor '%V' for type '%s' "
			     "needs a type, not a '%s' as arg 2",
			     descr_name((PyDescrObject *)descr), "?",
			     descr->d_type->tp_name,
			     type->ob_type->tp_name);
		return NULL;
	}
	if (!PyType_IsSubtype((PyTypeObject *)type, descr->d_type)) {
		PyErr_Format(PyExc_TypeError,
			     "descriptor '%V' for type '%s' "
			     "doesn't apply to type '%s'",
			     descr_name((PyDescrObject *)descr), "?",
			     descr->d_type->tp_name,
			     ((PyTypeObject *)type)->tp_name);
		return NULL;
	}
	return PyCFunction_New(descr->d_method, type);
}

static PyObject *
method_get(PyMethodDescrObject *descr, PyObject *obj, PyObject *type)
{
	PyObject *res;

	if (descr_check((PyDescrObject *)descr, obj, &res))
		return res;
	return PyCFunction_New(descr->d_method, obj);
}

static PyObject *
member_get(PyMemberDescrObject *descr, PyObject *obj, PyObject *type)
{
	PyObject *res;

	if (descr_check((PyDescrObject *)descr, obj, &res))
		return res;
	return PyMember_GetOne((char *)obj, descr->d_member);
}

static PyObject *
getset_get(PyGetSetDescrObject *descr, PyObject *obj, PyObject *type)
{
	PyObject *res;

	if (descr_check((PyDescrObject *)descr, obj, &res))
		return res;
	if (descr->d_getset->get != NULL)
		return descr->d_getset->get(obj, descr->d_getset->closure);
	PyErr_Format(PyExc_AttributeError,
		     "attribute '%V' of '%.100s' objects is not readable",
		     descr_name((PyDescrObject *)descr), "?",
		     descr->d_type->tp_name);
	return NULL;
}

static PyObject *
wrapperdescr_get(PyWrapperDescrObject *descr, PyObject *obj, PyObject *type)
{
	PyObject *res;

	if (descr_check((PyDescrObject *)descr, obj, &res))
		return res;
	return PyWrapper_New((PyObject *)descr, obj);
}

static int
descr_setcheck(PyDescrObject *descr, PyObject *obj, PyObject *value,
	       int *pres)
{
	assert(obj != NULL);
	if (!PyObject_TypeCheck(obj, descr->d_type)) {
		PyErr_Format(PyExc_TypeError,
			     "descriptor '%V' for '%.100s' objects "
			     "doesn't apply to '%.100s' object",
			     descr_name(descr), "?",
			     descr->d_type->tp_name,
			     obj->ob_type->tp_name);
		*pres = -1;
		return 1;
	}
	return 0;
}

static int
member_set(PyMemberDescrObject *descr, PyObject *obj, PyObject *value)
{
	int res;

	if (descr_setcheck((PyDescrObject *)descr, obj, value, &res))
		return res;
	return PyMember_SetOne((char *)obj, descr->d_member, value);
}

static int
getset_set(PyGetSetDescrObject *descr, PyObject *obj, PyObject *value)
{
	int res;

	if (descr_setcheck((PyDescrObject *)descr, obj, value, &res))
		return res;
	if (descr->d_getset->set != NULL)
		return descr->d_getset->set(obj, value,
					    descr->d_getset->closure);
	PyErr_Format(PyExc_AttributeError,
		     "attribute '%V' of '%.100s' objects is not writable",
		     descr_name((PyDescrObject *)descr), "?",
		     descr->d_type->tp_name);
	return -1;
}

static PyObject *
methoddescr_call(PyMethodDescrObject *descr, PyObject *args, PyObject *kwds)
{
	Py_ssize_t argc;
	PyObject *self, *func, *result;

	/* Make sure that the first argument is acceptable as 'self' */
	assert(PyTuple_Check(args));
	argc = PyTuple_GET_SIZE(args);
	if (argc < 1) {
		PyErr_Format(PyExc_TypeError,
			     "descriptor '%V' of '%.100s' "
			     "object needs an argument",
			     descr_name((PyDescrObject *)descr), "?",
			     descr->d_type->tp_name);
		return NULL;
	}
	self = PyTuple_GET_ITEM(args, 0);
	if (!PyObject_IsInstance(self, (PyObject *)(descr->d_type))) {
		PyErr_Format(PyExc_TypeError,
			     "descriptor '%V' "
			     "requires a '%.100s' object "
			     "but received a '%.100s'",
			     descr_name((PyDescrObject *)descr), "?",
			     descr->d_type->tp_name,
			     self->ob_type->tp_name);
		return NULL;
	}

	func = PyCFunction_New(descr->d_method, self);
	if (func == NULL)
		return NULL;
	args = PyTuple_GetSlice(args, 1, argc);
	if (args == NULL) {
		Py_DECREF(func);
		return NULL;
	}
	result = PyEval_CallObjectWithKeywords(func, args, kwds);
	Py_DECREF(args);
	Py_DECREF(func);
	return result;
}

static PyObject *
classmethoddescr_call(PyMethodDescrObject *descr, PyObject *args,
		      PyObject *kwds)
{
	PyObject *func, *result;

	func = PyCFunction_New(descr->d_method, (PyObject *)descr->d_type);
	if (func == NULL)
		return NULL;

	result = PyEval_CallObjectWithKeywords(func, args, kwds);
	Py_DECREF(func);
	return result;
}

static PyObject *
wrapperdescr_call(PyWrapperDescrObject *descr, PyObject *args, PyObject *kwds)
{
	Py_ssize_t argc;
	PyObject *self, *func, *result;

	/* Make sure that the first argument is acceptable as 'self' */
	assert(PyTuple_Check(args));
	argc = PyTuple_GET_SIZE(args);
	if (argc < 1) {
		PyErr_Format(PyExc_TypeError,
			     "descriptor '%V' of '%.100s' "
			     "object needs an argument",
			     descr_name((PyDescrObject *)descr), "?",
			     descr->d_type->tp_name);
		return NULL;
	}
	self = PyTuple_GET_ITEM(args, 0);
	if (!PyObject_IsInstance(self, (PyObject *)(descr->d_type))) {
		PyErr_Format(PyExc_TypeError,
			     "descriptor '%V' "
			     "requires a '%.100s' object "
			     "but received a '%.100s'",
			     descr_name((PyDescrObject *)descr), "?",
			     descr->d_type->tp_name,
			     self->ob_type->tp_name);
		return NULL;
	}

	func = PyWrapper_New((PyObject *)descr, self);
	if (func == NULL)
		return NULL;
	args = PyTuple_GetSlice(args, 1, argc);
	if (args == NULL) {
		Py_DECREF(func);
		return NULL;
	}
	result = PyEval_CallObjectWithKeywords(func, args, kwds);
	Py_DECREF(args);
	Py_DECREF(func);
	return result;
}

static PyObject *
method_get_doc(PyMethodDescrObject *descr, void *closure)
{
	if (descr->d_method->ml_doc == NULL) {
		Py_INCREF(Py_None);
		return Py_None;
	}
	return PyUnicode_FromString(descr->d_method->ml_doc);
}

static PyMemberDef descr_members[] = {
	{"__objclass__", T_OBJECT, offsetof(PyDescrObject, d_type), READONLY},
	{"__name__", T_OBJECT, offsetof(PyDescrObject, d_name), READONLY},
	{0}
};

static PyGetSetDef method_getset[] = {
	{"__doc__", (getter)method_get_doc},
	{0}
};

static PyObject *
member_get_doc(PyMemberDescrObject *descr, void *closure)
{
	if (descr->d_member->doc == NULL) {
		Py_INCREF(Py_None);
		return Py_None;
	}
	return PyUnicode_FromString(descr->d_member->doc);
}

static PyGetSetDef member_getset[] = {
	{"__doc__", (getter)member_get_doc},
	{0}
};

static PyObject *
getset_get_doc(PyGetSetDescrObject *descr, void *closure)
{
	if (descr->d_getset->doc == NULL) {
		Py_INCREF(Py_None);
		return Py_None;
	}
	return PyUnicode_FromString(descr->d_getset->doc);
}

static PyGetSetDef getset_getset[] = {
	{"__doc__", (getter)getset_get_doc},
	{0}
};

static PyObject *
wrapperdescr_get_doc(PyWrapperDescrObject *descr, void *closure)
{
	if (descr->d_base->doc == NULL) {
		Py_INCREF(Py_None);
		return Py_None;
	}
	return PyUnicode_FromString(descr->d_base->doc);
}

static PyGetSetDef wrapperdescr_getset[] = {
	{"__doc__", (getter)wrapperdescr_get_doc},
	{0}
};

static int
descr_traverse(PyObject *self, visitproc visit, void *arg)
{
	PyDescrObject *descr = (PyDescrObject *)self;
	Py_VISIT(descr->d_type);
	return 0;
}

PyTypeObject PyMethodDescr_Type = {
	PyVarObject_HEAD_INIT(&PyType_Type, 0)
	"method_descriptor",
	sizeof(PyMethodDescrObject),
	0,
	(destructor)descr_dealloc,		/* tp_dealloc */
	0,					/* tp_print */
	0,					/* tp_getattr */
	0,					/* tp_setattr */
	0,					/* tp_compare */
	(reprfunc)method_repr,			/* tp_repr */
	0,					/* tp_as_number */
	0,					/* tp_as_sequence */
	0,					/* tp_as_mapping */
	0,					/* tp_hash */
	(ternaryfunc)methoddescr_call,		/* tp_call */
	0,					/* tp_str */
	PyObject_GenericGetAttr,		/* tp_getattro */
	0,					/* tp_setattro */
	0,					/* tp_as_buffer */
	Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC, /* tp_flags */
	0,					/* tp_doc */
	descr_traverse,				/* tp_traverse */
	0,					/* tp_clear */
	0,					/* tp_richcompare */
	0,					/* tp_weaklistoffset */
	0,					/* tp_iter */
	0,					/* tp_iternext */
	0,					/* tp_methods */
	descr_members,				/* tp_members */
	method_getset,				/* tp_getset */
	0,					/* tp_base */
	0,					/* tp_dict */
	(descrgetfunc)method_get,		/* tp_descr_get */
	0,					/* tp_descr_set */
};

/* This is for METH_CLASS in C, not for "f = classmethod(f)" in Python! */
PyTypeObject PyClassMethodDescr_Type = {
	PyVarObject_HEAD_INIT(&PyType_Type, 0)
	"classmethod_descriptor",
	sizeof(PyMethodDescrObject),
	0,
	(destructor)descr_dealloc,		/* tp_dealloc */
	0,					/* tp_print */
	0,					/* tp_getattr */
	0,					/* tp_setattr */
	0,					/* tp_compare */
	(reprfunc)method_repr,			/* tp_repr */
	0,					/* tp_as_number */
	0,					/* tp_as_sequence */
	0,					/* tp_as_mapping */
	0,					/* tp_hash */
	(ternaryfunc)classmethoddescr_call,	/* tp_call */
	0,					/* tp_str */
	PyObject_GenericGetAttr,		/* tp_getattro */
	0,					/* tp_setattro */
	0,					/* tp_as_buffer */
	Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC, /* tp_flags */
	0,					/* tp_doc */
	descr_traverse,				/* tp_traverse */
	0,					/* tp_clear */
	0,					/* tp_richcompare */
	0,					/* tp_weaklistoffset */
	0,					/* tp_iter */
	0,					/* tp_iternext */
	0,					/* tp_methods */
	descr_members,				/* tp_members */
	method_getset,				/* tp_getset */
	0,					/* tp_base */
	0,					/* tp_dict */
	(descrgetfunc)classmethod_get,		/* tp_descr_get */
	0,					/* tp_descr_set */
};

PyTypeObject PyMemberDescr_Type = {
	PyVarObject_HEAD_INIT(&PyType_Type, 0)
	"member_descriptor",
	sizeof(PyMemberDescrObject),
	0,
	(destructor)descr_dealloc,		/* tp_dealloc */
	0,					/* tp_print */
	0,					/* tp_getattr */
	0,					/* tp_setattr */
	0,					/* tp_compare */
	(reprfunc)member_repr,			/* tp_repr */
	0,					/* tp_as_number */
	0,					/* tp_as_sequence */
	0,					/* tp_as_mapping */
	0,					/* tp_hash */
	0,					/* tp_call */
	0,					/* tp_str */
	PyObject_GenericGetAttr,		/* tp_getattro */
	0,					/* tp_setattro */
	0,					/* tp_as_buffer */
	Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC, /* tp_flags */
	0,					/* tp_doc */
	descr_traverse,				/* tp_traverse */
	0,					/* tp_clear */
	0,					/* tp_richcompare */
	0,					/* tp_weaklistoffset */
	0,					/* tp_iter */
	0,					/* tp_iternext */
	0,					/* tp_methods */
	descr_members,				/* tp_members */
	member_getset,				/* tp_getset */
	0,					/* tp_base */
	0,					/* tp_dict */
	(descrgetfunc)member_get,		/* tp_descr_get */
	(descrsetfunc)member_set,		/* tp_descr_set */
};

PyTypeObject PyGetSetDescr_Type = {
	PyVarObject_HEAD_INIT(&PyType_Type, 0)
	"getset_descriptor",
	sizeof(PyGetSetDescrObject),
	0,
	(destructor)descr_dealloc,		/* tp_dealloc */
	0,					/* tp_print */
	0,					/* tp_getattr */
	0,					/* tp_setattr */
	0,					/* tp_compare */
	(reprfunc)getset_repr,			/* tp_repr */
	0,					/* tp_as_number */
	0,					/* tp_as_sequence */
	0,					/* tp_as_mapping */
	0,					/* tp_hash */
	0,					/* tp_call */
	0,					/* tp_str */
	PyObject_GenericGetAttr,		/* tp_getattro */
	0,					/* tp_setattro */
	0,					/* tp_as_buffer */
	Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC, /* tp_flags */
	0,					/* tp_doc */
	descr_traverse,				/* tp_traverse */
	0,					/* tp_clear */
	0,					/* tp_richcompare */
	0,					/* tp_weaklistoffset */
	0,					/* tp_iter */
	0,					/* tp_iternext */
	0,					/* tp_methods */
	descr_members,				/* tp_members */
	getset_getset,				/* tp_getset */
	0,					/* tp_base */
	0,					/* tp_dict */
	(descrgetfunc)getset_get,		/* tp_descr_get */
	(descrsetfunc)getset_set,		/* tp_descr_set */
};

PyTypeObject PyWrapperDescr_Type = {
	PyVarObject_HEAD_INIT(&PyType_Type, 0)
	"wrapper_descriptor",
	sizeof(PyWrapperDescrObject),
	0,
	(destructor)descr_dealloc,		/* tp_dealloc */
	0,					/* tp_print */
	0,					/* tp_getattr */
	0,					/* tp_setattr */
	0,					/* tp_compare */
	(reprfunc)wrapperdescr_repr,		/* tp_repr */
	0,					/* tp_as_number */
	0,					/* tp_as_sequence */
	0,					/* tp_as_mapping */
	0,					/* tp_hash */
	(ternaryfunc)wrapperdescr_call,		/* tp_call */
	0,					/* tp_str */
	PyObject_GenericGetAttr,		/* tp_getattro */
	0,					/* tp_setattro */
	0,					/* tp_as_buffer */
	Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC, /* tp_flags */
	0,					/* tp_doc */
	descr_traverse,				/* tp_traverse */
	0,					/* tp_clear */
	0,					/* tp_richcompare */
	0,					/* tp_weaklistoffset */
	0,					/* tp_iter */
	0,					/* tp_iternext */
	0,					/* tp_methods */
	descr_members,				/* tp_members */
	wrapperdescr_getset,			/* tp_getset */
	0,					/* tp_base */
	0,					/* tp_dict */
	(descrgetfunc)wrapperdescr_get,		/* tp_descr_get */
	0,					/* tp_descr_set */
};

static PyDescrObject *
descr_new(PyTypeObject *descrtype, PyTypeObject *type, const char *name)
{
	PyDescrObject *descr;

	descr = (PyDescrObject *)PyType_GenericAlloc(descrtype, 0);
	if (descr != NULL) {
		Py_XINCREF(type);
		descr->d_type = type;
		descr->d_name = PyUnicode_InternFromString(name);
		if (descr->d_name == NULL) {
			Py_DECREF(descr);
			descr = NULL;
		}
	}
	return descr;
}

PyObject *
PyDescr_NewMethod(PyTypeObject *type, PyMethodDef *method)
{
	PyMethodDescrObject *descr;

	descr = (PyMethodDescrObject *)descr_new(&PyMethodDescr_Type,
						 type, method->ml_name);
	if (descr != NULL)
		descr->d_method = method;
	return (PyObject *)descr;
}

PyObject *
PyDescr_NewClassMethod(PyTypeObject *type, PyMethodDef *method)
{
	PyMethodDescrObject *descr;

	descr = (PyMethodDescrObject *)descr_new(&PyClassMethodDescr_Type,
						 type, method->ml_name);
	if (descr != NULL)
		descr->d_method = method;
	return (PyObject *)descr;
}

PyObject *
PyDescr_NewMember(PyTypeObject *type, PyMemberDef *member)
{
	PyMemberDescrObject *descr;

	descr = (PyMemberDescrObject *)descr_new(&PyMemberDescr_Type,
						 type, member->name);
	if (descr != NULL)
		descr->d_member = member;
	return (PyObject *)descr;
}

PyObject *
PyDescr_NewGetSet(PyTypeObject *type, PyGetSetDef *getset)
{
	PyGetSetDescrObject *descr;

	descr = (PyGetSetDescrObject *)descr_new(&PyGetSetDescr_Type,
						 type, getset->name);
	if (descr != NULL)
		descr->d_getset = getset;
	return (PyObject *)descr;
}

PyObject *
PyDescr_NewWrapper(PyTypeObject *type, struct wrapperbase *base, void *wrapped)
{
	PyWrapperDescrObject *descr;

	descr = (PyWrapperDescrObject *)descr_new(&PyWrapperDescr_Type,
						 type, base->name);
	if (descr != NULL) {
		descr->d_base = base;
		descr->d_wrapped = wrapped;
	}
	return (PyObject *)descr;
}


/* --- Readonly proxy for dictionaries (actually any mapping) --- */

/* This has no reason to be in this file except that adding new files is a
   bit of a pain */

typedef struct {
	PyObject_HEAD
	PyObject *dict;
} proxyobject;

static Py_ssize_t
proxy_len(proxyobject *pp)
{
	return PyObject_Size(pp->dict);
}

static PyObject *
proxy_getitem(proxyobject *pp, PyObject *key)
{
	return PyObject_GetItem(pp->dict, key);
}

static PyMappingMethods proxy_as_mapping = {
	(lenfunc)proxy_len,			/* mp_length */
	(binaryfunc)proxy_getitem,		/* mp_subscript */
	0,					/* mp_ass_subscript */
};

static int
proxy_contains(proxyobject *pp, PyObject *key)
{
	return PyDict_Contains(pp->dict, key);
}

static PySequenceMethods proxy_as_sequence = {
	0,					/* sq_length */
	0,					/* sq_concat */
	0,					/* sq_repeat */
	0,					/* sq_item */
	0,					/* sq_slice */
	0,					/* sq_ass_item */
	0,					/* sq_ass_slice */
	(objobjproc)proxy_contains,		/* sq_contains */
	0,					/* sq_inplace_concat */
	0,					/* sq_inplace_repeat */
};

static PyObject *
proxy_get(proxyobject *pp, PyObject *args)
{
	PyObject *key, *def = Py_None;

	if (!PyArg_UnpackTuple(args, "get", 1, 2, &key, &def))
		return NULL;
	return PyObject_CallMethod(pp->dict, "get", "(OO)", key, def);
}

static PyObject *
proxy_keys(proxyobject *pp)
{
	return PyMapping_Keys(pp->dict);
}

static PyObject *
proxy_values(proxyobject *pp)
{
	return PyMapping_Values(pp->dict);
}

static PyObject *
proxy_items(proxyobject *pp)
{
	return PyMapping_Items(pp->dict);
}

static PyObject *
proxy_copy(proxyobject *pp)
{
	return PyObject_CallMethod(pp->dict, "copy", NULL);
}

static PyMethodDef proxy_methods[] = {
	{"get",       (PyCFunction)proxy_get,        METH_VARARGS,
	 PyDoc_STR("D.get(k[,d]) -> D[k] if k in D, else d."
	 				"  d defaults to None.")},
	{"keys",      (PyCFunction)proxy_keys,       METH_NOARGS,
	 PyDoc_STR("D.keys() -> list of D's keys")},
	{"values",    (PyCFunction)proxy_values,     METH_NOARGS,
	 PyDoc_STR("D.values() -> list of D's values")},
	{"items",     (PyCFunction)proxy_items,      METH_NOARGS,
	 PyDoc_STR("D.items() -> list of D's (key, value) pairs, as 2-tuples")},
	{"copy",      (PyCFunction)proxy_copy,       METH_NOARGS,
	 PyDoc_STR("D.copy() -> a shallow copy of D")},
	{0}
};

static void
proxy_dealloc(proxyobject *pp)
{
	_PyObject_GC_UNTRACK(pp);
	Py_DECREF(pp->dict);
	PyObject_GC_Del(pp);
}

static PyObject *
proxy_getiter(proxyobject *pp)
{
	return PyObject_GetIter(pp->dict);
}

static PyObject *
proxy_str(proxyobject *pp)
{
	return PyObject_Str(pp->dict);
}

static int
proxy_traverse(PyObject *self, visitproc visit, void *arg)
{
	proxyobject *pp = (proxyobject *)self;
	Py_VISIT(pp->dict);
	return 0;
}

static int
proxy_compare(proxyobject *v, PyObject *w)
{
	return PyObject_Compare(v->dict, w);
}

static PyObject *
proxy_richcompare(proxyobject *v, PyObject *w, int op)
{
	return PyObject_RichCompare(v->dict, w, op);
}

PyTypeObject PyDictProxy_Type = {
	PyVarObject_HEAD_INIT(&PyType_Type, 0)
	"dict_proxy",				/* tp_name */
	sizeof(proxyobject),			/* tp_basicsize */
	0,					/* tp_itemsize */
	/* methods */
	(destructor)proxy_dealloc, 		/* tp_dealloc */
	0,					/* tp_print */
	0,					/* tp_getattr */
	0,					/* tp_setattr */
	(cmpfunc)proxy_compare,			/* tp_compare */
	0,					/* tp_repr */
	0,					/* tp_as_number */
	&proxy_as_sequence,			/* tp_as_sequence */
	&proxy_as_mapping,			/* tp_as_mapping */
	0,					/* tp_hash */
	0,					/* tp_call */
	(reprfunc)proxy_str,			/* tp_str */
	PyObject_GenericGetAttr,		/* tp_getattro */
	0,					/* tp_setattro */
	0,					/* tp_as_buffer */
	Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC, /* tp_flags */
 	0,					/* tp_doc */
	proxy_traverse,				/* tp_traverse */
 	0,					/* tp_clear */
	(richcmpfunc)proxy_richcompare,		/* tp_richcompare */
	0,					/* tp_weaklistoffset */
	(getiterfunc)proxy_getiter,		/* tp_iter */
	0,					/* tp_iternext */
	proxy_methods,				/* tp_methods */
	0,					/* tp_members */
	0,					/* tp_getset */
	0,					/* tp_base */
	0,					/* tp_dict */
	0,					/* tp_descr_get */
	0,					/* tp_descr_set */
};

PyObject *
PyDictProxy_New(PyObject *dict)
{
	proxyobject *pp;

	pp = PyObject_GC_New(proxyobject, &PyDictProxy_Type);
	if (pp != NULL) {
		Py_INCREF(dict);
		pp->dict = dict;
		_PyObject_GC_TRACK(pp);
	}
	return (PyObject *)pp;
}


/* --- Wrapper object for "slot" methods --- */

/* This has no reason to be in this file except that adding new files is a
   bit of a pain */

typedef struct {
	PyObject_HEAD
	PyWrapperDescrObject *descr;
	PyObject *self;
} wrapperobject;

static void
wrapper_dealloc(wrapperobject *wp)
{
	PyObject_GC_UnTrack(wp);
	Py_TRASHCAN_SAFE_BEGIN(wp)
	Py_XDECREF(wp->descr);
	Py_XDECREF(wp->self);
	PyObject_GC_Del(wp);
	Py_TRASHCAN_SAFE_END(wp)
}

static int
wrapper_compare(wrapperobject *a, wrapperobject *b)
{
	if (a->descr == b->descr)
		return PyObject_Compare(a->self, b->self);
	else
		return (a->descr < b->descr) ? -1 : 1;
}

static long
wrapper_hash(wrapperobject *wp)
{
	int x, y;
	x = _Py_HashPointer(wp->descr);
	if (x == -1)
		return -1;
	y = PyObject_Hash(wp->self);
	if (y == -1)
		return -1;
	x = x ^ y;
	if (x == -1)
		x = -2;
	return x;
}

static PyObject *
wrapper_repr(wrapperobject *wp)
{
	return PyUnicode_FromFormat("<method-wrapper '%s' of %s object at %p>",
				   wp->descr->d_base->name,
				   wp->self->ob_type->tp_name,
				   wp->self);
}

static PyMemberDef wrapper_members[] = {
	{"__self__", T_OBJECT, offsetof(wrapperobject, self), READONLY},
	{0}
};

static PyObject *
wrapper_objclass(wrapperobject *wp)
{
	PyObject *c = (PyObject *)wp->descr->d_type;

	Py_INCREF(c);
	return c;
}

static PyObject *
wrapper_name(wrapperobject *wp)
{
	const char *s = wp->descr->d_base->name;

	return PyUnicode_FromString(s);
}

static PyObject *
wrapper_doc(wrapperobject *wp)
{
	const char *s = wp->descr->d_base->doc;

	if (s == NULL) {
		Py_INCREF(Py_None);
		return Py_None;
	}
	else {
		return PyUnicode_FromString(s);
	}
}

static PyGetSetDef wrapper_getsets[] = {
	{"__objclass__", (getter)wrapper_objclass},
	{"__name__", (getter)wrapper_name},
	{"__doc__", (getter)wrapper_doc},
	{0}
};

static PyObject *
wrapper_call(wrapperobject *wp, PyObject *args, PyObject *kwds)
{
	wrapperfunc wrapper = wp->descr->d_base->wrapper;
	PyObject *self = wp->self;

	if (wp->descr->d_base->flags & PyWrapperFlag_KEYWORDS) {
		wrapperfunc_kwds wk = (wrapperfunc_kwds)wrapper;
		return (*wk)(self, args, wp->descr->d_wrapped, kwds);
	}

	if (kwds != NULL && (!PyDict_Check(kwds) || PyDict_Size(kwds) != 0)) {
		PyErr_Format(PyExc_TypeError,
			     "wrapper %s doesn't take keyword arguments",
			     wp->descr->d_base->name);
		return NULL;
	}
	return (*wrapper)(self, args, wp->descr->d_wrapped);
}

static int
wrapper_traverse(PyObject *self, visitproc visit, void *arg)
{
	wrapperobject *wp = (wrapperobject *)self;
	Py_VISIT(wp->descr);
	Py_VISIT(wp->self);
	return 0;
}

static PyTypeObject wrappertype = {
	PyVarObject_HEAD_INIT(&PyType_Type, 0)
	"method-wrapper",			/* tp_name */
	sizeof(wrapperobject),			/* tp_basicsize */
	0,					/* tp_itemsize */
	/* methods */
	(destructor)wrapper_dealloc, 		/* tp_dealloc */
	0,					/* tp_print */
	0,					/* tp_getattr */
	0,					/* tp_setattr */
	(cmpfunc)wrapper_compare,		/* tp_compare */
	(reprfunc)wrapper_repr,			/* tp_repr */
	0,					/* tp_as_number */
	0,					/* tp_as_sequence */
	0,		       			/* tp_as_mapping */
	(hashfunc)wrapper_hash,			/* tp_hash */
	(ternaryfunc)wrapper_call,		/* tp_call */
	0,					/* tp_str */
	PyObject_GenericGetAttr,		/* tp_getattro */
	0,					/* tp_setattro */
	0,					/* tp_as_buffer */
	Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC, /* tp_flags */
 	0,					/* tp_doc */
	wrapper_traverse,			/* tp_traverse */
 	0,					/* tp_clear */
	0,					/* tp_richcompare */
	0,					/* tp_weaklistoffset */
	0,					/* tp_iter */
	0,					/* tp_iternext */
	0,					/* tp_methods */
	wrapper_members,			/* tp_members */
	wrapper_getsets,			/* tp_getset */
	0,					/* tp_base */
	0,					/* tp_dict */
	0,					/* tp_descr_get */
	0,					/* tp_descr_set */
};

PyObject *
PyWrapper_New(PyObject *d, PyObject *self)
{
	wrapperobject *wp;
	PyWrapperDescrObject *descr;

	assert(PyObject_TypeCheck(d, &PyWrapperDescr_Type));
	descr = (PyWrapperDescrObject *)d;
	assert(PyObject_IsInstance(self, (PyObject *)(descr->d_type)));

	wp = PyObject_GC_New(wrapperobject, &wrappertype);
	if (wp != NULL) {
		Py_INCREF(descr);
		wp->descr = descr;
		Py_INCREF(self);
		wp->self = self;
		_PyObject_GC_TRACK(wp);
	}
	return (PyObject *)wp;
}


/* A built-in 'property' type */

/*
    class property(object):

        def __init__(self, fget=None, fset=None, fdel=None, doc=None):
            if doc is None and fget is not None and hasattr(fget, "__doc__"):
                doc = fget.__doc__
            self.__get = fget
            self.__set = fset
            self.__del = fdel
            self.__doc__ = doc

        def __get__(self, inst, type=None):
            if inst is None:
                return self
            if self.__get is None:
                raise AttributeError, "unreadable attribute"
            return self.__get(inst)

        def __set__(self, inst, value):
            if self.__set is None:
                raise AttributeError, "can't set attribute"
            return self.__set(inst, value)

        def __delete__(self, inst):
            if self.__del is None:
                raise AttributeError, "can't delete attribute"
            return self.__del(inst)

*/

typedef struct {
	PyObject_HEAD
	PyObject *prop_get;
	PyObject *prop_set;
	PyObject *prop_del;
	PyObject *prop_doc;
	int getter_doc;
} propertyobject;

static PyObject * property_copy(PyObject *, PyObject *, PyObject *,
				  PyObject *, PyObject *);

static PyMemberDef property_members[] = {
	{"fget", T_OBJECT, offsetof(propertyobject, prop_get), READONLY},
	{"fset", T_OBJECT, offsetof(propertyobject, prop_set), READONLY},
	{"fdel", T_OBJECT, offsetof(propertyobject, prop_del), READONLY},
	{"__doc__",  T_OBJECT, offsetof(propertyobject, prop_doc), READONLY},
	{0}
};


PyDoc_STRVAR(getter_doc,
	     "Descriptor to change the getter on a property.");

static PyObject *
property_getter(PyObject *self, PyObject *getter)
{
	return property_copy(self, getter, NULL, NULL, NULL);
}


PyDoc_STRVAR(setter_doc,
	     "Descriptor to change the setter on a property.");

static PyObject *
property_setter(PyObject *self, PyObject *setter)
{
	return property_copy(self, NULL, setter, NULL, NULL);
}


PyDoc_STRVAR(deleter_doc,
	     "Descriptor to change the deleter on a property.");

static PyObject *
property_deleter(PyObject *self, PyObject *deleter)
{
	return property_copy(self, NULL, NULL, deleter, NULL);
}


static PyMethodDef property_methods[] = {
	{"getter", property_getter, METH_O, getter_doc},
	{"setter", property_setter, METH_O, setter_doc},
	{"deleter", property_deleter, METH_O, deleter_doc},
	{0}
};


static void
property_dealloc(PyObject *self)
{
	propertyobject *gs = (propertyobject *)self;

	_PyObject_GC_UNTRACK(self);
	Py_XDECREF(gs->prop_get);
	Py_XDECREF(gs->prop_set);
	Py_XDECREF(gs->prop_del);
	Py_XDECREF(gs->prop_doc);
	self->ob_type->tp_free(self);
}

static PyObject *
property_descr_get(PyObject *self, PyObject *obj, PyObject *type)
{
	propertyobject *gs = (propertyobject *)self;

	if (obj == NULL || obj == Py_None) {
		Py_INCREF(self);
		return self;
	}
	if (gs->prop_get == NULL) {
		PyErr_SetString(PyExc_AttributeError, "unreadable attribute");
		return NULL;
	}
	return PyObject_CallFunction(gs->prop_get, "(O)", obj);
}

static int
property_descr_set(PyObject *self, PyObject *obj, PyObject *value)
{
	propertyobject *gs = (propertyobject *)self;
	PyObject *func, *res;

	if (value == NULL)
		func = gs->prop_del;
	else
		func = gs->prop_set;
	if (func == NULL) {
		PyErr_SetString(PyExc_AttributeError,
				value == NULL ?
				"can't delete attribute" :
				"can't set attribute");
		return -1;
	}
	if (value == NULL)
		res = PyObject_CallFunction(func, "(O)", obj);
	else
		res = PyObject_CallFunction(func, "(OO)", obj, value);
	if (res == NULL)
		return -1;
	Py_DECREF(res);
	return 0;
}

static PyObject *
property_copy(PyObject *old, PyObject *get, PyObject *set, PyObject *del,
		PyObject *doc)
{
	propertyobject *pold = (propertyobject *)old;
	propertyobject *pnew = NULL;
	PyObject *new, *type;

	type = PyObject_Type(old);
	if (type == NULL)
		return NULL;

	if (get == NULL || get == Py_None) {
		Py_XDECREF(get);
		get = pold->prop_get ? pold->prop_get : Py_None;
	}
	if (set == NULL || set == Py_None) {
		Py_XDECREF(set);
		set = pold->prop_set ? pold->prop_set : Py_None;
	}
	if (del == NULL || del == Py_None) {
		Py_XDECREF(del);
		del = pold->prop_del ? pold->prop_del : Py_None;
	}
	if (doc == NULL || doc == Py_None) {
		Py_XDECREF(doc);
		doc = pold->prop_doc ? pold->prop_doc : Py_None;
	}
	
	new =  PyObject_CallFunction(type, "OOOO", get, set, del, doc);
	Py_DECREF(type);
	if (new == NULL)
		return NULL;
	pnew = (propertyobject *)new;
	
	if (pold->getter_doc && get != Py_None) {
		PyObject *get_doc = PyObject_GetAttrString(get, "__doc__");
		if (get_doc != NULL) {
			Py_XDECREF(pnew->prop_doc);
			pnew->prop_doc = get_doc;  /* get_doc already INCREF'd by GetAttr */
			pnew->getter_doc = 1;
		} else {
			PyErr_Clear();
		}
	}
	return new;
}

static int
property_init(PyObject *self, PyObject *args, PyObject *kwds)
{
	PyObject *get = NULL, *set = NULL, *del = NULL, *doc = NULL;
	static char *kwlist[] = {"fget", "fset", "fdel", "doc", 0};
	propertyobject *prop = (propertyobject *)self;
	
	if (!PyArg_ParseTupleAndKeywords(args, kwds, "|OOOO:property",
					 kwlist, &get, &set, &del, &doc))
		return -1;

	if (get == Py_None)
		get = NULL;
	if (set == Py_None)
		set = NULL;
	if (del == Py_None)
		del = NULL;

	Py_XINCREF(get);
	Py_XINCREF(set);
	Py_XINCREF(del);
	Py_XINCREF(doc);

	prop->prop_get = get;
	prop->prop_set = set;
	prop->prop_del = del;
	prop->prop_doc = doc;
	prop->getter_doc = 0;

	/* if no docstring given and the getter has one, use that one */
	if ((doc == NULL || doc == Py_None) && get != NULL) {
		PyObject *get_doc = PyObject_GetAttrString(get, "__doc__");
		if (get_doc != NULL) {
			Py_XDECREF(prop->prop_doc);
			prop->prop_doc = get_doc;  /* get_doc already INCREF'd by GetAttr */
			prop->getter_doc = 1;
		} else {
			PyErr_Clear();
		}
	}

	return 0;
}

PyDoc_STRVAR(property_doc,
"property(fget=None, fset=None, fdel=None, doc=None) -> property attribute\n"
"\n"
"fget is a function to be used for getting an attribute value, and likewise\n"
"fset is a function for setting, and fdel a function for del'ing, an\n"
"attribute.  Typical use is to define a managed attribute x:\n"
"class C(object):\n"
"    def getx(self): return self._x\n"
"    def setx(self, value): self._x = value\n"
"    def delx(self): del self._x\n"
"    x = property(getx, setx, delx, \"I'm the 'x' property.\")\n"
"\n"
"Decorators make defining new properties or modifying existing ones easy:\n"
"class C(object):\n"
"    @property\n"
"    def x(self): return self._x\n"
"    @x.setter\n"
"    def x(self, value): self._x = value\n"
"    @x.deleter\n"
"    def x(self): del self._x\n"
);

static int
property_traverse(PyObject *self, visitproc visit, void *arg)
{
	propertyobject *pp = (propertyobject *)self;
	Py_VISIT(pp->prop_get);
	Py_VISIT(pp->prop_set);
	Py_VISIT(pp->prop_del);
	Py_VISIT(pp->prop_doc);
	return 0;
}

PyTypeObject PyProperty_Type = {
	PyVarObject_HEAD_INIT(&PyType_Type, 0)
	"property",				/* tp_name */
	sizeof(propertyobject),			/* tp_basicsize */
	0,					/* tp_itemsize */
	/* methods */
	property_dealloc,	 		/* tp_dealloc */
	0,					/* tp_print */
	0,					/* tp_getattr */
	0,					/* tp_setattr */
	0,					/* tp_compare */
	0,					/* tp_repr */
	0,					/* tp_as_number */
	0,					/* tp_as_sequence */
	0,		       			/* tp_as_mapping */
	0,					/* tp_hash */
	0,					/* tp_call */
	0,					/* tp_str */
	PyObject_GenericGetAttr,		/* tp_getattro */
	0,					/* tp_setattro */
	0,					/* tp_as_buffer */
	Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC |
		Py_TPFLAGS_BASETYPE,		/* tp_flags */
 	property_doc,				/* tp_doc */
	property_traverse,			/* tp_traverse */
 	0,					/* tp_clear */
	0,					/* tp_richcompare */
	0,					/* tp_weaklistoffset */
	0,					/* tp_iter */
	0,					/* tp_iternext */
	property_methods,			/* tp_methods */
	property_members,			/* tp_members */
	0,					/* tp_getset */
	0,					/* tp_base */
	0,					/* tp_dict */
	property_descr_get,			/* tp_descr_get */
	property_descr_set,			/* tp_descr_set */
	0,					/* tp_dictoffset */
	property_init,				/* tp_init */
	PyType_GenericAlloc,			/* tp_alloc */
	PyType_GenericNew,			/* tp_new */
	PyObject_GC_Del,               		/* tp_free */
};