From e97b7bef4fbe71821d59d2f41f311e514fd29e39 Mon Sep 17 00:00:00 2001 From: Adam Turner <9087854+AA-Turner@users.noreply.github.com> Date: Tue, 22 Aug 2023 19:42:51 +0100 Subject: GH-92584: Remove distutils from the newtypes tutorial includes (#108024) --- Doc/extending/newtypes_tutorial.rst | 54 +++++----- Doc/includes/custom.c | 43 -------- Doc/includes/custom2.c | 130 ----------------------- Doc/includes/custom3.c | 171 ------------------------------ Doc/includes/custom4.c | 189 --------------------------------- Doc/includes/newtypes/custom.c | 43 ++++++++ Doc/includes/newtypes/custom2.c | 130 +++++++++++++++++++++++ Doc/includes/newtypes/custom3.c | 171 ++++++++++++++++++++++++++++++ Doc/includes/newtypes/custom4.c | 189 +++++++++++++++++++++++++++++++++ Doc/includes/newtypes/pyproject.toml | 7 ++ Doc/includes/newtypes/setup.py | 8 ++ Doc/includes/newtypes/sublist.c | 67 ++++++++++++ Doc/includes/newtypes/test.py | 192 +++++++++++++++++++++++++++++++++ Doc/includes/setup.py | 9 -- Doc/includes/sublist.c | 67 ------------ Doc/includes/test.py | 199 ----------------------------------- 16 files changed, 834 insertions(+), 835 deletions(-) delete mode 100644 Doc/includes/custom.c delete mode 100644 Doc/includes/custom2.c delete mode 100644 Doc/includes/custom3.c delete mode 100644 Doc/includes/custom4.c create mode 100644 Doc/includes/newtypes/custom.c create mode 100644 Doc/includes/newtypes/custom2.c create mode 100644 Doc/includes/newtypes/custom3.c create mode 100644 Doc/includes/newtypes/custom4.c create mode 100644 Doc/includes/newtypes/pyproject.toml create mode 100644 Doc/includes/newtypes/setup.py create mode 100644 Doc/includes/newtypes/sublist.c create mode 100644 Doc/includes/newtypes/test.py delete mode 100644 Doc/includes/setup.py delete mode 100644 Doc/includes/sublist.c delete mode 100644 Doc/includes/test.py diff --git a/Doc/extending/newtypes_tutorial.rst b/Doc/extending/newtypes_tutorial.rst index 209a4ab..7eba975 100644 --- a/Doc/extending/newtypes_tutorial.rst +++ b/Doc/extending/newtypes_tutorial.rst @@ -45,7 +45,7 @@ extension module :mod:`!custom`: allows defining heap-allocated extension types using the :c:func:`PyType_FromSpec` function, which isn't covered in this tutorial. -.. literalinclude:: ../includes/custom.c +.. literalinclude:: ../includes/newtypes/custom.c Now that's quite a bit to take in at once, but hopefully bits will seem familiar from the previous chapter. This file defines three things: @@ -194,36 +194,32 @@ This adds the type to the module dictionary. This allows us to create >>> mycustom = custom.Custom() That's it! All that remains is to build it; put the above code in a file called -:file:`custom.c` and: +:file:`custom.c`, + +.. literalinclude:: ../includes/newtypes/pyproject.toml + +in a file called :file:`pyproject.toml`, and .. code-block:: python - from distutils.core import setup, Extension - setup(name="custom", version="1.0", - ext_modules=[Extension("custom", ["custom.c"])]) + from setuptools import Extension, setup + setup(ext_modules=[Extension("custom", ["custom.c"])]) in a file called :file:`setup.py`; then typing .. code-block:: shell-session - $ python setup.py build + $ python -m pip install . -at a shell should produce a file :file:`custom.so` in a subdirectory; move to -that directory and fire up Python --- you should be able to ``import custom`` and -play around with Custom objects. +in a shell should produce a file :file:`custom.so` in a subdirectory +and install it; now fire up Python --- you should be able to ``import custom`` +and play around with ``Custom`` objects. That wasn't so hard, was it? Of course, the current Custom type is pretty uninteresting. It has no data and doesn't do anything. It can't even be subclassed. -.. note:: - While this documentation showcases the standard :mod:`!distutils` module - for building C extensions, it is recommended in real-world use cases to - use the newer and better-maintained ``setuptools`` library. Documentation - on how to do this is out of scope for this document and can be found in - the `Python Packaging User's Guide `_. - Adding data and methods to the Basic example ============================================ @@ -232,7 +228,7 @@ Let's extend the basic example to add some data and methods. Let's also make the type usable as a base class. We'll create a new module, :mod:`!custom2` that adds these capabilities: -.. literalinclude:: ../includes/custom2.c +.. literalinclude:: ../includes/newtypes/custom2.c This version of the module has a number of changes. @@ -514,17 +510,21 @@ We rename :c:func:`!PyInit_custom` to :c:func:`!PyInit_custom2`, update the module name in the :c:type:`PyModuleDef` struct, and update the full class name in the :c:type:`PyTypeObject` struct. -Finally, we update our :file:`setup.py` file to build the new module: +Finally, we update our :file:`setup.py` file to include the new module, .. code-block:: python - from distutils.core import setup, Extension - setup(name="custom", version="1.0", - ext_modules=[ - Extension("custom", ["custom.c"]), - Extension("custom2", ["custom2.c"]), - ]) + from setuptools import Extension, setup + setup(ext_modules=[ + Extension("custom", ["custom.c"]), + Extension("custom2", ["custom2.c"]), + ]) + +and then we re-install so that we can ``import custom2``: + +.. code-block:: shell-session + $ python -m pip install . Providing finer control over data attributes ============================================ @@ -535,7 +535,7 @@ version of our module, the instance variables :attr:`!first` and :attr:`!last` could be set to non-string values or even deleted. We want to make sure that these attributes always contain strings. -.. literalinclude:: ../includes/custom3.c +.. literalinclude:: ../includes/newtypes/custom3.c To provide greater control, over the :attr:`!first` and :attr:`!last` attributes, @@ -682,7 +682,7 @@ To allow a :class:`!Custom` instance participating in a reference cycle to be properly detected and collected by the cyclic GC, our :class:`!Custom` type needs to fill two additional slots and to enable a flag that enables these slots: -.. literalinclude:: ../includes/custom4.c +.. literalinclude:: ../includes/newtypes/custom4.c First, the traversal method lets the cyclic GC know about subobjects that could @@ -806,7 +806,7 @@ increases an internal counter: >>> print(s.increment()) 2 -.. literalinclude:: ../includes/sublist.c +.. literalinclude:: ../includes/newtypes/sublist.c As you can see, the source code closely resembles the :class:`!Custom` examples in diff --git a/Doc/includes/custom.c b/Doc/includes/custom.c deleted file mode 100644 index 5253f87..0000000 --- a/Doc/includes/custom.c +++ /dev/null @@ -1,43 +0,0 @@ -#define PY_SSIZE_T_CLEAN -#include - -typedef struct { - PyObject_HEAD - /* Type-specific fields go here. */ -} CustomObject; - -static PyTypeObject CustomType = { - .ob_base = PyVarObject_HEAD_INIT(NULL, 0) - .tp_name = "custom.Custom", - .tp_doc = PyDoc_STR("Custom objects"), - .tp_basicsize = sizeof(CustomObject), - .tp_itemsize = 0, - .tp_flags = Py_TPFLAGS_DEFAULT, - .tp_new = PyType_GenericNew, -}; - -static PyModuleDef custommodule = { - .m_base = PyModuleDef_HEAD_INIT, - .m_name = "custom", - .m_doc = "Example module that creates an extension type.", - .m_size = -1, -}; - -PyMODINIT_FUNC -PyInit_custom(void) -{ - PyObject *m; - if (PyType_Ready(&CustomType) < 0) - return NULL; - - m = PyModule_Create(&custommodule); - if (m == NULL) - return NULL; - - if (PyModule_AddObjectRef(m, "Custom", (PyObject *) &CustomType) < 0) { - Py_DECREF(m); - return NULL; - } - - return m; -} diff --git a/Doc/includes/custom2.c b/Doc/includes/custom2.c deleted file mode 100644 index a0222b1..0000000 --- a/Doc/includes/custom2.c +++ /dev/null @@ -1,130 +0,0 @@ -#define PY_SSIZE_T_CLEAN -#include -#include /* for offsetof() */ - -typedef struct { - PyObject_HEAD - PyObject *first; /* first name */ - PyObject *last; /* last name */ - int number; -} CustomObject; - -static void -Custom_dealloc(CustomObject *self) -{ - Py_XDECREF(self->first); - Py_XDECREF(self->last); - Py_TYPE(self)->tp_free((PyObject *) self); -} - -static PyObject * -Custom_new(PyTypeObject *type, PyObject *args, PyObject *kwds) -{ - CustomObject *self; - self = (CustomObject *) type->tp_alloc(type, 0); - if (self != NULL) { - self->first = PyUnicode_FromString(""); - if (self->first == NULL) { - Py_DECREF(self); - return NULL; - } - self->last = PyUnicode_FromString(""); - if (self->last == NULL) { - Py_DECREF(self); - return NULL; - } - self->number = 0; - } - return (PyObject *) self; -} - -static int -Custom_init(CustomObject *self, PyObject *args, PyObject *kwds) -{ - static char *kwlist[] = {"first", "last", "number", NULL}; - PyObject *first = NULL, *last = NULL; - - if (!PyArg_ParseTupleAndKeywords(args, kwds, "|OOi", kwlist, - &first, &last, - &self->number)) - return -1; - - if (first) { - Py_XSETREF(self->first, Py_NewRef(first)); - } - if (last) { - Py_XSETREF(self->last, Py_NewRef(last)); - } - return 0; -} - -static PyMemberDef Custom_members[] = { - {"first", Py_T_OBJECT_EX, offsetof(CustomObject, first), 0, - "first name"}, - {"last", Py_T_OBJECT_EX, offsetof(CustomObject, last), 0, - "last name"}, - {"number", Py_T_INT, offsetof(CustomObject, number), 0, - "custom number"}, - {NULL} /* Sentinel */ -}; - -static PyObject * -Custom_name(CustomObject *self, PyObject *Py_UNUSED(ignored)) -{ - if (self->first == NULL) { - PyErr_SetString(PyExc_AttributeError, "first"); - return NULL; - } - if (self->last == NULL) { - PyErr_SetString(PyExc_AttributeError, "last"); - return NULL; - } - return PyUnicode_FromFormat("%S %S", self->first, self->last); -} - -static PyMethodDef Custom_methods[] = { - {"name", (PyCFunction) Custom_name, METH_NOARGS, - "Return the name, combining the first and last name" - }, - {NULL} /* Sentinel */ -}; - -static PyTypeObject CustomType = { - .ob_base = PyVarObject_HEAD_INIT(NULL, 0) - .tp_name = "custom2.Custom", - .tp_doc = PyDoc_STR("Custom objects"), - .tp_basicsize = sizeof(CustomObject), - .tp_itemsize = 0, - .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, - .tp_new = Custom_new, - .tp_init = (initproc) Custom_init, - .tp_dealloc = (destructor) Custom_dealloc, - .tp_members = Custom_members, - .tp_methods = Custom_methods, -}; - -static PyModuleDef custommodule = { - .m_base =PyModuleDef_HEAD_INIT, - .m_name = "custom2", - .m_doc = "Example module that creates an extension type.", - .m_size = -1, -}; - -PyMODINIT_FUNC -PyInit_custom2(void) -{ - PyObject *m; - if (PyType_Ready(&CustomType) < 0) - return NULL; - - m = PyModule_Create(&custommodule); - if (m == NULL) - return NULL; - - if (PyModule_AddObjectRef(m, "Custom", (PyObject *) &CustomType) < 0) { - Py_DECREF(m); - return NULL; - } - - return m; -} diff --git a/Doc/includes/custom3.c b/Doc/includes/custom3.c deleted file mode 100644 index 4aeebe0..0000000 --- a/Doc/includes/custom3.c +++ /dev/null @@ -1,171 +0,0 @@ -#define PY_SSIZE_T_CLEAN -#include -#include /* for offsetof() */ - -typedef struct { - PyObject_HEAD - PyObject *first; /* first name */ - PyObject *last; /* last name */ - int number; -} CustomObject; - -static void -Custom_dealloc(CustomObject *self) -{ - Py_XDECREF(self->first); - Py_XDECREF(self->last); - Py_TYPE(self)->tp_free((PyObject *) self); -} - -static PyObject * -Custom_new(PyTypeObject *type, PyObject *args, PyObject *kwds) -{ - CustomObject *self; - self = (CustomObject *) type->tp_alloc(type, 0); - if (self != NULL) { - self->first = PyUnicode_FromString(""); - if (self->first == NULL) { - Py_DECREF(self); - return NULL; - } - self->last = PyUnicode_FromString(""); - if (self->last == NULL) { - Py_DECREF(self); - return NULL; - } - self->number = 0; - } - return (PyObject *) self; -} - -static int -Custom_init(CustomObject *self, PyObject *args, PyObject *kwds) -{ - static char *kwlist[] = {"first", "last", "number", NULL}; - PyObject *first = NULL, *last = NULL; - - if (!PyArg_ParseTupleAndKeywords(args, kwds, "|UUi", kwlist, - &first, &last, - &self->number)) - return -1; - - if (first) { - Py_SETREF(self->first, Py_NewRef(first)); - } - if (last) { - Py_SETREF(self->last, Py_NewRef(last)); - } - return 0; -} - -static PyMemberDef Custom_members[] = { - {"number", Py_T_INT, offsetof(CustomObject, number), 0, - "custom number"}, - {NULL} /* Sentinel */ -}; - -static PyObject * -Custom_getfirst(CustomObject *self, void *closure) -{ - return Py_NewRef(self->first); -} - -static int -Custom_setfirst(CustomObject *self, PyObject *value, void *closure) -{ - if (value == NULL) { - PyErr_SetString(PyExc_TypeError, "Cannot delete the first attribute"); - return -1; - } - if (!PyUnicode_Check(value)) { - PyErr_SetString(PyExc_TypeError, - "The first attribute value must be a string"); - return -1; - } - Py_SETREF(self->first, Py_NewRef(value)); - return 0; -} - -static PyObject * -Custom_getlast(CustomObject *self, void *closure) -{ - return Py_NewRef(self->last); -} - -static int -Custom_setlast(CustomObject *self, PyObject *value, void *closure) -{ - if (value == NULL) { - PyErr_SetString(PyExc_TypeError, "Cannot delete the last attribute"); - return -1; - } - if (!PyUnicode_Check(value)) { - PyErr_SetString(PyExc_TypeError, - "The last attribute value must be a string"); - return -1; - } - Py_SETREF(self->last, Py_NewRef(value)); - return 0; -} - -static PyGetSetDef Custom_getsetters[] = { - {"first", (getter) Custom_getfirst, (setter) Custom_setfirst, - "first name", NULL}, - {"last", (getter) Custom_getlast, (setter) Custom_setlast, - "last name", NULL}, - {NULL} /* Sentinel */ -}; - -static PyObject * -Custom_name(CustomObject *self, PyObject *Py_UNUSED(ignored)) -{ - return PyUnicode_FromFormat("%S %S", self->first, self->last); -} - -static PyMethodDef Custom_methods[] = { - {"name", (PyCFunction) Custom_name, METH_NOARGS, - "Return the name, combining the first and last name" - }, - {NULL} /* Sentinel */ -}; - -static PyTypeObject CustomType = { - .ob_base = PyVarObject_HEAD_INIT(NULL, 0) - .tp_name = "custom3.Custom", - .tp_doc = PyDoc_STR("Custom objects"), - .tp_basicsize = sizeof(CustomObject), - .tp_itemsize = 0, - .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, - .tp_new = Custom_new, - .tp_init = (initproc) Custom_init, - .tp_dealloc = (destructor) Custom_dealloc, - .tp_members = Custom_members, - .tp_methods = Custom_methods, - .tp_getset = Custom_getsetters, -}; - -static PyModuleDef custommodule = { - .m_base = PyModuleDef_HEAD_INIT, - .m_name = "custom3", - .m_doc = "Example module that creates an extension type.", - .m_size = -1, -}; - -PyMODINIT_FUNC -PyInit_custom3(void) -{ - PyObject *m; - if (PyType_Ready(&CustomType) < 0) - return NULL; - - m = PyModule_Create(&custommodule); - if (m == NULL) - return NULL; - - if (PyModule_AddObjectRef(m, "Custom", (PyObject *) &CustomType) < 0) { - Py_DECREF(m); - return NULL; - } - - return m; -} diff --git a/Doc/includes/custom4.c b/Doc/includes/custom4.c deleted file mode 100644 index 3998918..0000000 --- a/Doc/includes/custom4.c +++ /dev/null @@ -1,189 +0,0 @@ -#define PY_SSIZE_T_CLEAN -#include -#include /* for offsetof() */ - -typedef struct { - PyObject_HEAD - PyObject *first; /* first name */ - PyObject *last; /* last name */ - int number; -} CustomObject; - -static int -Custom_traverse(CustomObject *self, visitproc visit, void *arg) -{ - Py_VISIT(self->first); - Py_VISIT(self->last); - return 0; -} - -static int -Custom_clear(CustomObject *self) -{ - Py_CLEAR(self->first); - Py_CLEAR(self->last); - return 0; -} - -static void -Custom_dealloc(CustomObject *self) -{ - PyObject_GC_UnTrack(self); - Custom_clear(self); - Py_TYPE(self)->tp_free((PyObject *) self); -} - -static PyObject * -Custom_new(PyTypeObject *type, PyObject *args, PyObject *kwds) -{ - CustomObject *self; - self = (CustomObject *) type->tp_alloc(type, 0); - if (self != NULL) { - self->first = PyUnicode_FromString(""); - if (self->first == NULL) { - Py_DECREF(self); - return NULL; - } - self->last = PyUnicode_FromString(""); - if (self->last == NULL) { - Py_DECREF(self); - return NULL; - } - self->number = 0; - } - return (PyObject *) self; -} - -static int -Custom_init(CustomObject *self, PyObject *args, PyObject *kwds) -{ - static char *kwlist[] = {"first", "last", "number", NULL}; - PyObject *first = NULL, *last = NULL; - - if (!PyArg_ParseTupleAndKeywords(args, kwds, "|UUi", kwlist, - &first, &last, - &self->number)) - return -1; - - if (first) { - Py_SETREF(self->first, Py_NewRef(first)); - } - if (last) { - Py_SETREF(self->last, Py_NewRef(last)); - } - return 0; -} - -static PyMemberDef Custom_members[] = { - {"number", Py_T_INT, offsetof(CustomObject, number), 0, - "custom number"}, - {NULL} /* Sentinel */ -}; - -static PyObject * -Custom_getfirst(CustomObject *self, void *closure) -{ - return Py_NewRef(self->first); -} - -static int -Custom_setfirst(CustomObject *self, PyObject *value, void *closure) -{ - if (value == NULL) { - PyErr_SetString(PyExc_TypeError, "Cannot delete the first attribute"); - return -1; - } - if (!PyUnicode_Check(value)) { - PyErr_SetString(PyExc_TypeError, - "The first attribute value must be a string"); - return -1; - } - Py_XSETREF(self->first, Py_NewRef(value)); - return 0; -} - -static PyObject * -Custom_getlast(CustomObject *self, void *closure) -{ - return Py_NewRef(self->last); -} - -static int -Custom_setlast(CustomObject *self, PyObject *value, void *closure) -{ - if (value == NULL) { - PyErr_SetString(PyExc_TypeError, "Cannot delete the last attribute"); - return -1; - } - if (!PyUnicode_Check(value)) { - PyErr_SetString(PyExc_TypeError, - "The last attribute value must be a string"); - return -1; - } - Py_XSETREF(self->last, Py_NewRef(value)); - return 0; -} - -static PyGetSetDef Custom_getsetters[] = { - {"first", (getter) Custom_getfirst, (setter) Custom_setfirst, - "first name", NULL}, - {"last", (getter) Custom_getlast, (setter) Custom_setlast, - "last name", NULL}, - {NULL} /* Sentinel */ -}; - -static PyObject * -Custom_name(CustomObject *self, PyObject *Py_UNUSED(ignored)) -{ - return PyUnicode_FromFormat("%S %S", self->first, self->last); -} - -static PyMethodDef Custom_methods[] = { - {"name", (PyCFunction) Custom_name, METH_NOARGS, - "Return the name, combining the first and last name" - }, - {NULL} /* Sentinel */ -}; - -static PyTypeObject CustomType = { - .ob_base = PyVarObject_HEAD_INIT(NULL, 0) - .tp_name = "custom4.Custom", - .tp_doc = PyDoc_STR("Custom objects"), - .tp_basicsize = sizeof(CustomObject), - .tp_itemsize = 0, - .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC, - .tp_new = Custom_new, - .tp_init = (initproc) Custom_init, - .tp_dealloc = (destructor) Custom_dealloc, - .tp_traverse = (traverseproc) Custom_traverse, - .tp_clear = (inquiry) Custom_clear, - .tp_members = Custom_members, - .tp_methods = Custom_methods, - .tp_getset = Custom_getsetters, -}; - -static PyModuleDef custommodule = { - .m_base = PyModuleDef_HEAD_INIT, - .m_name = "custom4", - .m_doc = "Example module that creates an extension type.", - .m_size = -1, -}; - -PyMODINIT_FUNC -PyInit_custom4(void) -{ - PyObject *m; - if (PyType_Ready(&CustomType) < 0) - return NULL; - - m = PyModule_Create(&custommodule); - if (m == NULL) - return NULL; - - if (PyModule_AddObjectRef(m, "Custom", (PyObject *) &CustomType) < 0) { - Py_DECREF(m); - return NULL; - } - - return m; -} diff --git a/Doc/includes/newtypes/custom.c b/Doc/includes/newtypes/custom.c new file mode 100644 index 0000000..5253f87 --- /dev/null +++ b/Doc/includes/newtypes/custom.c @@ -0,0 +1,43 @@ +#define PY_SSIZE_T_CLEAN +#include + +typedef struct { + PyObject_HEAD + /* Type-specific fields go here. */ +} CustomObject; + +static PyTypeObject CustomType = { + .ob_base = PyVarObject_HEAD_INIT(NULL, 0) + .tp_name = "custom.Custom", + .tp_doc = PyDoc_STR("Custom objects"), + .tp_basicsize = sizeof(CustomObject), + .tp_itemsize = 0, + .tp_flags = Py_TPFLAGS_DEFAULT, + .tp_new = PyType_GenericNew, +}; + +static PyModuleDef custommodule = { + .m_base = PyModuleDef_HEAD_INIT, + .m_name = "custom", + .m_doc = "Example module that creates an extension type.", + .m_size = -1, +}; + +PyMODINIT_FUNC +PyInit_custom(void) +{ + PyObject *m; + if (PyType_Ready(&CustomType) < 0) + return NULL; + + m = PyModule_Create(&custommodule); + if (m == NULL) + return NULL; + + if (PyModule_AddObjectRef(m, "Custom", (PyObject *) &CustomType) < 0) { + Py_DECREF(m); + return NULL; + } + + return m; +} diff --git a/Doc/includes/newtypes/custom2.c b/Doc/includes/newtypes/custom2.c new file mode 100644 index 0000000..a0222b1 --- /dev/null +++ b/Doc/includes/newtypes/custom2.c @@ -0,0 +1,130 @@ +#define PY_SSIZE_T_CLEAN +#include +#include /* for offsetof() */ + +typedef struct { + PyObject_HEAD + PyObject *first; /* first name */ + PyObject *last; /* last name */ + int number; +} CustomObject; + +static void +Custom_dealloc(CustomObject *self) +{ + Py_XDECREF(self->first); + Py_XDECREF(self->last); + Py_TYPE(self)->tp_free((PyObject *) self); +} + +static PyObject * +Custom_new(PyTypeObject *type, PyObject *args, PyObject *kwds) +{ + CustomObject *self; + self = (CustomObject *) type->tp_alloc(type, 0); + if (self != NULL) { + self->first = PyUnicode_FromString(""); + if (self->first == NULL) { + Py_DECREF(self); + return NULL; + } + self->last = PyUnicode_FromString(""); + if (self->last == NULL) { + Py_DECREF(self); + return NULL; + } + self->number = 0; + } + return (PyObject *) self; +} + +static int +Custom_init(CustomObject *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"first", "last", "number", NULL}; + PyObject *first = NULL, *last = NULL; + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "|OOi", kwlist, + &first, &last, + &self->number)) + return -1; + + if (first) { + Py_XSETREF(self->first, Py_NewRef(first)); + } + if (last) { + Py_XSETREF(self->last, Py_NewRef(last)); + } + return 0; +} + +static PyMemberDef Custom_members[] = { + {"first", Py_T_OBJECT_EX, offsetof(CustomObject, first), 0, + "first name"}, + {"last", Py_T_OBJECT_EX, offsetof(CustomObject, last), 0, + "last name"}, + {"number", Py_T_INT, offsetof(CustomObject, number), 0, + "custom number"}, + {NULL} /* Sentinel */ +}; + +static PyObject * +Custom_name(CustomObject *self, PyObject *Py_UNUSED(ignored)) +{ + if (self->first == NULL) { + PyErr_SetString(PyExc_AttributeError, "first"); + return NULL; + } + if (self->last == NULL) { + PyErr_SetString(PyExc_AttributeError, "last"); + return NULL; + } + return PyUnicode_FromFormat("%S %S", self->first, self->last); +} + +static PyMethodDef Custom_methods[] = { + {"name", (PyCFunction) Custom_name, METH_NOARGS, + "Return the name, combining the first and last name" + }, + {NULL} /* Sentinel */ +}; + +static PyTypeObject CustomType = { + .ob_base = PyVarObject_HEAD_INIT(NULL, 0) + .tp_name = "custom2.Custom", + .tp_doc = PyDoc_STR("Custom objects"), + .tp_basicsize = sizeof(CustomObject), + .tp_itemsize = 0, + .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, + .tp_new = Custom_new, + .tp_init = (initproc) Custom_init, + .tp_dealloc = (destructor) Custom_dealloc, + .tp_members = Custom_members, + .tp_methods = Custom_methods, +}; + +static PyModuleDef custommodule = { + .m_base =PyModuleDef_HEAD_INIT, + .m_name = "custom2", + .m_doc = "Example module that creates an extension type.", + .m_size = -1, +}; + +PyMODINIT_FUNC +PyInit_custom2(void) +{ + PyObject *m; + if (PyType_Ready(&CustomType) < 0) + return NULL; + + m = PyModule_Create(&custommodule); + if (m == NULL) + return NULL; + + if (PyModule_AddObjectRef(m, "Custom", (PyObject *) &CustomType) < 0) { + Py_DECREF(m); + return NULL; + } + + return m; +} diff --git a/Doc/includes/newtypes/custom3.c b/Doc/includes/newtypes/custom3.c new file mode 100644 index 0000000..4aeebe0 --- /dev/null +++ b/Doc/includes/newtypes/custom3.c @@ -0,0 +1,171 @@ +#define PY_SSIZE_T_CLEAN +#include +#include /* for offsetof() */ + +typedef struct { + PyObject_HEAD + PyObject *first; /* first name */ + PyObject *last; /* last name */ + int number; +} CustomObject; + +static void +Custom_dealloc(CustomObject *self) +{ + Py_XDECREF(self->first); + Py_XDECREF(self->last); + Py_TYPE(self)->tp_free((PyObject *) self); +} + +static PyObject * +Custom_new(PyTypeObject *type, PyObject *args, PyObject *kwds) +{ + CustomObject *self; + self = (CustomObject *) type->tp_alloc(type, 0); + if (self != NULL) { + self->first = PyUnicode_FromString(""); + if (self->first == NULL) { + Py_DECREF(self); + return NULL; + } + self->last = PyUnicode_FromString(""); + if (self->last == NULL) { + Py_DECREF(self); + return NULL; + } + self->number = 0; + } + return (PyObject *) self; +} + +static int +Custom_init(CustomObject *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"first", "last", "number", NULL}; + PyObject *first = NULL, *last = NULL; + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "|UUi", kwlist, + &first, &last, + &self->number)) + return -1; + + if (first) { + Py_SETREF(self->first, Py_NewRef(first)); + } + if (last) { + Py_SETREF(self->last, Py_NewRef(last)); + } + return 0; +} + +static PyMemberDef Custom_members[] = { + {"number", Py_T_INT, offsetof(CustomObject, number), 0, + "custom number"}, + {NULL} /* Sentinel */ +}; + +static PyObject * +Custom_getfirst(CustomObject *self, void *closure) +{ + return Py_NewRef(self->first); +} + +static int +Custom_setfirst(CustomObject *self, PyObject *value, void *closure) +{ + if (value == NULL) { + PyErr_SetString(PyExc_TypeError, "Cannot delete the first attribute"); + return -1; + } + if (!PyUnicode_Check(value)) { + PyErr_SetString(PyExc_TypeError, + "The first attribute value must be a string"); + return -1; + } + Py_SETREF(self->first, Py_NewRef(value)); + return 0; +} + +static PyObject * +Custom_getlast(CustomObject *self, void *closure) +{ + return Py_NewRef(self->last); +} + +static int +Custom_setlast(CustomObject *self, PyObject *value, void *closure) +{ + if (value == NULL) { + PyErr_SetString(PyExc_TypeError, "Cannot delete the last attribute"); + return -1; + } + if (!PyUnicode_Check(value)) { + PyErr_SetString(PyExc_TypeError, + "The last attribute value must be a string"); + return -1; + } + Py_SETREF(self->last, Py_NewRef(value)); + return 0; +} + +static PyGetSetDef Custom_getsetters[] = { + {"first", (getter) Custom_getfirst, (setter) Custom_setfirst, + "first name", NULL}, + {"last", (getter) Custom_getlast, (setter) Custom_setlast, + "last name", NULL}, + {NULL} /* Sentinel */ +}; + +static PyObject * +Custom_name(CustomObject *self, PyObject *Py_UNUSED(ignored)) +{ + return PyUnicode_FromFormat("%S %S", self->first, self->last); +} + +static PyMethodDef Custom_methods[] = { + {"name", (PyCFunction) Custom_name, METH_NOARGS, + "Return the name, combining the first and last name" + }, + {NULL} /* Sentinel */ +}; + +static PyTypeObject CustomType = { + .ob_base = PyVarObject_HEAD_INIT(NULL, 0) + .tp_name = "custom3.Custom", + .tp_doc = PyDoc_STR("Custom objects"), + .tp_basicsize = sizeof(CustomObject), + .tp_itemsize = 0, + .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, + .tp_new = Custom_new, + .tp_init = (initproc) Custom_init, + .tp_dealloc = (destructor) Custom_dealloc, + .tp_members = Custom_members, + .tp_methods = Custom_methods, + .tp_getset = Custom_getsetters, +}; + +static PyModuleDef custommodule = { + .m_base = PyModuleDef_HEAD_INIT, + .m_name = "custom3", + .m_doc = "Example module that creates an extension type.", + .m_size = -1, +}; + +PyMODINIT_FUNC +PyInit_custom3(void) +{ + PyObject *m; + if (PyType_Ready(&CustomType) < 0) + return NULL; + + m = PyModule_Create(&custommodule); + if (m == NULL) + return NULL; + + if (PyModule_AddObjectRef(m, "Custom", (PyObject *) &CustomType) < 0) { + Py_DECREF(m); + return NULL; + } + + return m; +} diff --git a/Doc/includes/newtypes/custom4.c b/Doc/includes/newtypes/custom4.c new file mode 100644 index 0000000..3998918 --- /dev/null +++ b/Doc/includes/newtypes/custom4.c @@ -0,0 +1,189 @@ +#define PY_SSIZE_T_CLEAN +#include +#include /* for offsetof() */ + +typedef struct { + PyObject_HEAD + PyObject *first; /* first name */ + PyObject *last; /* last name */ + int number; +} CustomObject; + +static int +Custom_traverse(CustomObject *self, visitproc visit, void *arg) +{ + Py_VISIT(self->first); + Py_VISIT(self->last); + return 0; +} + +static int +Custom_clear(CustomObject *self) +{ + Py_CLEAR(self->first); + Py_CLEAR(self->last); + return 0; +} + +static void +Custom_dealloc(CustomObject *self) +{ + PyObject_GC_UnTrack(self); + Custom_clear(self); + Py_TYPE(self)->tp_free((PyObject *) self); +} + +static PyObject * +Custom_new(PyTypeObject *type, PyObject *args, PyObject *kwds) +{ + CustomObject *self; + self = (CustomObject *) type->tp_alloc(type, 0); + if (self != NULL) { + self->first = PyUnicode_FromString(""); + if (self->first == NULL) { + Py_DECREF(self); + return NULL; + } + self->last = PyUnicode_FromString(""); + if (self->last == NULL) { + Py_DECREF(self); + return NULL; + } + self->number = 0; + } + return (PyObject *) self; +} + +static int +Custom_init(CustomObject *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"first", "last", "number", NULL}; + PyObject *first = NULL, *last = NULL; + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "|UUi", kwlist, + &first, &last, + &self->number)) + return -1; + + if (first) { + Py_SETREF(self->first, Py_NewRef(first)); + } + if (last) { + Py_SETREF(self->last, Py_NewRef(last)); + } + return 0; +} + +static PyMemberDef Custom_members[] = { + {"number", Py_T_INT, offsetof(CustomObject, number), 0, + "custom number"}, + {NULL} /* Sentinel */ +}; + +static PyObject * +Custom_getfirst(CustomObject *self, void *closure) +{ + return Py_NewRef(self->first); +} + +static int +Custom_setfirst(CustomObject *self, PyObject *value, void *closure) +{ + if (value == NULL) { + PyErr_SetString(PyExc_TypeError, "Cannot delete the first attribute"); + return -1; + } + if (!PyUnicode_Check(value)) { + PyErr_SetString(PyExc_TypeError, + "The first attribute value must be a string"); + return -1; + } + Py_XSETREF(self->first, Py_NewRef(value)); + return 0; +} + +static PyObject * +Custom_getlast(CustomObject *self, void *closure) +{ + return Py_NewRef(self->last); +} + +static int +Custom_setlast(CustomObject *self, PyObject *value, void *closure) +{ + if (value == NULL) { + PyErr_SetString(PyExc_TypeError, "Cannot delete the last attribute"); + return -1; + } + if (!PyUnicode_Check(value)) { + PyErr_SetString(PyExc_TypeError, + "The last attribute value must be a string"); + return -1; + } + Py_XSETREF(self->last, Py_NewRef(value)); + return 0; +} + +static PyGetSetDef Custom_getsetters[] = { + {"first", (getter) Custom_getfirst, (setter) Custom_setfirst, + "first name", NULL}, + {"last", (getter) Custom_getlast, (setter) Custom_setlast, + "last name", NULL}, + {NULL} /* Sentinel */ +}; + +static PyObject * +Custom_name(CustomObject *self, PyObject *Py_UNUSED(ignored)) +{ + return PyUnicode_FromFormat("%S %S", self->first, self->last); +} + +static PyMethodDef Custom_methods[] = { + {"name", (PyCFunction) Custom_name, METH_NOARGS, + "Return the name, combining the first and last name" + }, + {NULL} /* Sentinel */ +}; + +static PyTypeObject CustomType = { + .ob_base = PyVarObject_HEAD_INIT(NULL, 0) + .tp_name = "custom4.Custom", + .tp_doc = PyDoc_STR("Custom objects"), + .tp_basicsize = sizeof(CustomObject), + .tp_itemsize = 0, + .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC, + .tp_new = Custom_new, + .tp_init = (initproc) Custom_init, + .tp_dealloc = (destructor) Custom_dealloc, + .tp_traverse = (traverseproc) Custom_traverse, + .tp_clear = (inquiry) Custom_clear, + .tp_members = Custom_members, + .tp_methods = Custom_methods, + .tp_getset = Custom_getsetters, +}; + +static PyModuleDef custommodule = { + .m_base = PyModuleDef_HEAD_INIT, + .m_name = "custom4", + .m_doc = "Example module that creates an extension type.", + .m_size = -1, +}; + +PyMODINIT_FUNC +PyInit_custom4(void) +{ + PyObject *m; + if (PyType_Ready(&CustomType) < 0) + return NULL; + + m = PyModule_Create(&custommodule); + if (m == NULL) + return NULL; + + if (PyModule_AddObjectRef(m, "Custom", (PyObject *) &CustomType) < 0) { + Py_DECREF(m); + return NULL; + } + + return m; +} diff --git a/Doc/includes/newtypes/pyproject.toml b/Doc/includes/newtypes/pyproject.toml new file mode 100644 index 0000000..ea7937a --- /dev/null +++ b/Doc/includes/newtypes/pyproject.toml @@ -0,0 +1,7 @@ +[build-system] +requires = ["setuptools"] +build-backend = "setuptools.build_meta" + +[project] +name = "custom" +version = "1" diff --git a/Doc/includes/newtypes/setup.py b/Doc/includes/newtypes/setup.py new file mode 100644 index 0000000..67f8367 --- /dev/null +++ b/Doc/includes/newtypes/setup.py @@ -0,0 +1,8 @@ +from setuptools import Extension, setup +setup(ext_modules=[ + Extension("custom", ["custom.c"]), + Extension("custom2", ["custom2.c"]), + Extension("custom3", ["custom3.c"]), + Extension("custom4", ["custom4.c"]), + Extension("sublist", ["sublist.c"]), +]) diff --git a/Doc/includes/newtypes/sublist.c b/Doc/includes/newtypes/sublist.c new file mode 100644 index 0000000..d8aba46 --- /dev/null +++ b/Doc/includes/newtypes/sublist.c @@ -0,0 +1,67 @@ +#define PY_SSIZE_T_CLEAN +#include + +typedef struct { + PyListObject list; + int state; +} SubListObject; + +static PyObject * +SubList_increment(SubListObject *self, PyObject *unused) +{ + self->state++; + return PyLong_FromLong(self->state); +} + +static PyMethodDef SubList_methods[] = { + {"increment", (PyCFunction) SubList_increment, METH_NOARGS, + PyDoc_STR("increment state counter")}, + {NULL}, +}; + +static int +SubList_init(SubListObject *self, PyObject *args, PyObject *kwds) +{ + if (PyList_Type.tp_init((PyObject *) self, args, kwds) < 0) + return -1; + self->state = 0; + return 0; +} + +static PyTypeObject SubListType = { + PyVarObject_HEAD_INIT(NULL, 0) + .tp_name = "sublist.SubList", + .tp_doc = PyDoc_STR("SubList objects"), + .tp_basicsize = sizeof(SubListObject), + .tp_itemsize = 0, + .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, + .tp_init = (initproc) SubList_init, + .tp_methods = SubList_methods, +}; + +static PyModuleDef sublistmodule = { + PyModuleDef_HEAD_INIT, + .m_name = "sublist", + .m_doc = "Example module that creates an extension type.", + .m_size = -1, +}; + +PyMODINIT_FUNC +PyInit_sublist(void) +{ + PyObject *m; + SubListType.tp_base = &PyList_Type; + if (PyType_Ready(&SubListType) < 0) + return NULL; + + m = PyModule_Create(&sublistmodule); + if (m == NULL) + return NULL; + + if (PyModule_AddObjectRef(m, "SubList", (PyObject *) &SubListType) < 0) { + Py_DECREF(m); + return NULL; + } + + return m; +} diff --git a/Doc/includes/newtypes/test.py b/Doc/includes/newtypes/test.py new file mode 100644 index 0000000..55a5cf9 --- /dev/null +++ b/Doc/includes/newtypes/test.py @@ -0,0 +1,192 @@ +"""Test module for the custom examples + +Custom 1: + +>>> import custom +>>> c1 = custom.Custom() +>>> c2 = custom.Custom() +>>> del c1 +>>> del c2 + + +Custom 2 + +>>> import custom2 +>>> c1 = custom2.Custom('jim', 'fulton', 42) +>>> c1.first +'jim' +>>> c1.last +'fulton' +>>> c1.number +42 +>>> c1.name() +'jim fulton' +>>> c1.first = 'will' +>>> c1.name() +'will fulton' +>>> c1.last = 'tell' +>>> c1.name() +'will tell' +>>> del c1.first +>>> c1.name() +Traceback (most recent call last): +... +AttributeError: first +>>> c1.first +Traceback (most recent call last): +... +AttributeError: first +>>> c1.first = 'drew' +>>> c1.first +'drew' +>>> del c1.number +Traceback (most recent call last): +... +TypeError: can't delete numeric/char attribute +>>> c1.number=2 +>>> c1.number +2 +>>> c1.first = 42 +>>> c1.name() +'42 tell' +>>> c2 = custom2.Custom() +>>> c2.name() +' ' +>>> c2.first +'' +>>> c2.last +'' +>>> del c2.first +>>> c2.first +Traceback (most recent call last): +... +AttributeError: first +>>> c2.first +Traceback (most recent call last): +... +AttributeError: first +>>> c2.name() +Traceback (most recent call last): + File "", line 1, in ? +AttributeError: first +>>> c2.number +0 +>>> n3 = custom2.Custom('jim', 'fulton', 'waaa') +Traceback (most recent call last): + File "", line 1, in ? +TypeError: an integer is required (got type str) +>>> del c1 +>>> del c2 + + +Custom 3 + +>>> import custom3 +>>> c1 = custom3.Custom('jim', 'fulton', 42) +>>> c1 = custom3.Custom('jim', 'fulton', 42) +>>> c1.name() +'jim fulton' +>>> del c1.first +Traceback (most recent call last): + File "", line 1, in ? +TypeError: Cannot delete the first attribute +>>> c1.first = 42 +Traceback (most recent call last): + File "", line 1, in ? +TypeError: The first attribute value must be a string +>>> c1.first = 'will' +>>> c1.name() +'will fulton' +>>> c2 = custom3.Custom() +>>> c2 = custom3.Custom() +>>> c2 = custom3.Custom() +>>> n3 = custom3.Custom('jim', 'fulton', 'waaa') +Traceback (most recent call last): + File "", line 1, in ? +TypeError: an integer is required (got type str) +>>> del c1 +>>> del c2 + +Custom 4 + +>>> import custom4 +>>> c1 = custom4.Custom('jim', 'fulton', 42) +>>> c1.first +'jim' +>>> c1.last +'fulton' +>>> c1.number +42 +>>> c1.name() +'jim fulton' +>>> c1.first = 'will' +>>> c1.name() +'will fulton' +>>> c1.last = 'tell' +>>> c1.name() +'will tell' +>>> del c1.first +Traceback (most recent call last): +... +TypeError: Cannot delete the first attribute +>>> c1.name() +'will tell' +>>> c1.first = 'drew' +>>> c1.first +'drew' +>>> del c1.number +Traceback (most recent call last): +... +TypeError: can't delete numeric/char attribute +>>> c1.number=2 +>>> c1.number +2 +>>> c1.first = 42 +Traceback (most recent call last): +... +TypeError: The first attribute value must be a string +>>> c1.name() +'drew tell' +>>> c2 = custom4.Custom() +>>> c2 = custom4.Custom() +>>> c2 = custom4.Custom() +>>> c2 = custom4.Custom() +>>> c2.name() +' ' +>>> c2.first +'' +>>> c2.last +'' +>>> c2.number +0 +>>> n3 = custom4.Custom('jim', 'fulton', 'waaa') +Traceback (most recent call last): +... +TypeError: an integer is required (got type str) + + +Test cyclic gc(?) + +>>> import gc +>>> gc.disable() + +>>> class Subclass(custom4.Custom): pass +... +>>> s = Subclass() +>>> s.cycle = [s] +>>> s.cycle.append(s.cycle) +>>> x = object() +>>> s.x = x +>>> del s +>>> sys.getrefcount(x) +3 +>>> ignore = gc.collect() +>>> sys.getrefcount(x) +2 + +>>> gc.enable() +""" + +if __name__ == "__main__": + import doctest, __main__ + doctest.testmod(__main__) diff --git a/Doc/includes/setup.py b/Doc/includes/setup.py deleted file mode 100644 index a38a39d..0000000 --- a/Doc/includes/setup.py +++ /dev/null @@ -1,9 +0,0 @@ -from distutils.core import setup, Extension -setup(name="noddy", version="1.0", - ext_modules=[ - Extension("noddy", ["noddy.c"]), - Extension("noddy2", ["noddy2.c"]), - Extension("noddy3", ["noddy3.c"]), - Extension("noddy4", ["noddy4.c"]), - Extension("shoddy", ["shoddy.c"]), - ]) diff --git a/Doc/includes/sublist.c b/Doc/includes/sublist.c deleted file mode 100644 index d8aba46..0000000 --- a/Doc/includes/sublist.c +++ /dev/null @@ -1,67 +0,0 @@ -#define PY_SSIZE_T_CLEAN -#include - -typedef struct { - PyListObject list; - int state; -} SubListObject; - -static PyObject * -SubList_increment(SubListObject *self, PyObject *unused) -{ - self->state++; - return PyLong_FromLong(self->state); -} - -static PyMethodDef SubList_methods[] = { - {"increment", (PyCFunction) SubList_increment, METH_NOARGS, - PyDoc_STR("increment state counter")}, - {NULL}, -}; - -static int -SubList_init(SubListObject *self, PyObject *args, PyObject *kwds) -{ - if (PyList_Type.tp_init((PyObject *) self, args, kwds) < 0) - return -1; - self->state = 0; - return 0; -} - -static PyTypeObject SubListType = { - PyVarObject_HEAD_INIT(NULL, 0) - .tp_name = "sublist.SubList", - .tp_doc = PyDoc_STR("SubList objects"), - .tp_basicsize = sizeof(SubListObject), - .tp_itemsize = 0, - .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, - .tp_init = (initproc) SubList_init, - .tp_methods = SubList_methods, -}; - -static PyModuleDef sublistmodule = { - PyModuleDef_HEAD_INIT, - .m_name = "sublist", - .m_doc = "Example module that creates an extension type.", - .m_size = -1, -}; - -PyMODINIT_FUNC -PyInit_sublist(void) -{ - PyObject *m; - SubListType.tp_base = &PyList_Type; - if (PyType_Ready(&SubListType) < 0) - return NULL; - - m = PyModule_Create(&sublistmodule); - if (m == NULL) - return NULL; - - if (PyModule_AddObjectRef(m, "SubList", (PyObject *) &SubListType) < 0) { - Py_DECREF(m); - return NULL; - } - - return m; -} diff --git a/Doc/includes/test.py b/Doc/includes/test.py deleted file mode 100644 index 09ebe3f..0000000 --- a/Doc/includes/test.py +++ /dev/null @@ -1,199 +0,0 @@ -"""Test module for the custom examples - -Custom 1: - ->>> import custom ->>> c1 = custom.Custom() ->>> c2 = custom.Custom() ->>> del c1 ->>> del c2 - - -Custom 2 - ->>> import custom2 ->>> c1 = custom2.Custom('jim', 'fulton', 42) ->>> c1.first -'jim' ->>> c1.last -'fulton' ->>> c1.number -42 ->>> c1.name() -'jim fulton' ->>> c1.first = 'will' ->>> c1.name() -'will fulton' ->>> c1.last = 'tell' ->>> c1.name() -'will tell' ->>> del c1.first ->>> c1.name() -Traceback (most recent call last): -... -AttributeError: first ->>> c1.first -Traceback (most recent call last): -... -AttributeError: first ->>> c1.first = 'drew' ->>> c1.first -'drew' ->>> del c1.number -Traceback (most recent call last): -... -TypeError: can't delete numeric/char attribute ->>> c1.number=2 ->>> c1.number -2 ->>> c1.first = 42 ->>> c1.name() -'42 tell' ->>> c2 = custom2.Custom() ->>> c2.name() -' ' ->>> c2.first -'' ->>> c2.last -'' ->>> del c2.first ->>> c2.first -Traceback (most recent call last): -... -AttributeError: first ->>> c2.first -Traceback (most recent call last): -... -AttributeError: first ->>> c2.name() -Traceback (most recent call last): - File "", line 1, in ? -AttributeError: first ->>> c2.number -0 ->>> n3 = custom2.Custom('jim', 'fulton', 'waaa') -Traceback (most recent call last): - File "", line 1, in ? -TypeError: an integer is required (got type str) ->>> del c1 ->>> del c2 - - -Custom 3 - ->>> import custom3 ->>> c1 = custom3.Custom('jim', 'fulton', 42) ->>> c1 = custom3.Custom('jim', 'fulton', 42) ->>> c1.name() -'jim fulton' ->>> del c1.first -Traceback (most recent call last): - File "", line 1, in ? -TypeError: Cannot delete the first attribute ->>> c1.first = 42 -Traceback (most recent call last): - File "", line 1, in ? -TypeError: The first attribute value must be a string ->>> c1.first = 'will' ->>> c1.name() -'will fulton' ->>> c2 = custom3.Custom() ->>> c2 = custom3.Custom() ->>> c2 = custom3.Custom() ->>> n3 = custom3.Custom('jim', 'fulton', 'waaa') -Traceback (most recent call last): - File "", line 1, in ? -TypeError: an integer is required (got type str) ->>> del c1 ->>> del c2 - -Custom 4 - ->>> import custom4 ->>> c1 = custom4.Custom('jim', 'fulton', 42) ->>> c1.first -'jim' ->>> c1.last -'fulton' ->>> c1.number -42 ->>> c1.name() -'jim fulton' ->>> c1.first = 'will' ->>> c1.name() -'will fulton' ->>> c1.last = 'tell' ->>> c1.name() -'will tell' ->>> del c1.first -Traceback (most recent call last): -... -TypeError: Cannot delete the first attribute ->>> c1.name() -'will tell' ->>> c1.first = 'drew' ->>> c1.first -'drew' ->>> del c1.number -Traceback (most recent call last): -... -TypeError: can't delete numeric/char attribute ->>> c1.number=2 ->>> c1.number -2 ->>> c1.first = 42 -Traceback (most recent call last): -... -TypeError: The first attribute value must be a string ->>> c1.name() -'drew tell' ->>> c2 = custom4.Custom() ->>> c2 = custom4.Custom() ->>> c2 = custom4.Custom() ->>> c2 = custom4.Custom() ->>> c2.name() -' ' ->>> c2.first -'' ->>> c2.last -'' ->>> c2.number -0 ->>> n3 = custom4.Custom('jim', 'fulton', 'waaa') -Traceback (most recent call last): -... -TypeError: an integer is required (got type str) - - -Test cyclic gc(?) - ->>> import gc ->>> gc.disable() - ->>> class Subclass(custom4.Custom): pass -... ->>> s = Subclass() ->>> s.cycle = [s] ->>> s.cycle.append(s.cycle) ->>> x = object() ->>> s.x = x ->>> del s ->>> sys.getrefcount(x) -3 ->>> ignore = gc.collect() ->>> sys.getrefcount(x) -2 - ->>> gc.enable() -""" - -import os -import sys -from distutils.util import get_platform -PLAT_SPEC = "%s-%d.%d" % (get_platform(), *sys.version_info[:2]) -src = os.path.join("build", "lib.%s" % PLAT_SPEC) -sys.path.append(src) - -if __name__ == "__main__": - import doctest, __main__ - doctest.testmod(__main__) -- cgit v0.12