]> granicus.if.org Git - python/commitdiff
#7033: add new API function PyErr_NewExceptionWithDoc, for easily giving new exceptio...
authorGeorg Brandl <georg@python.org>
Mon, 28 Dec 2009 08:34:58 +0000 (08:34 +0000)
committerGeorg Brandl <georg@python.org>
Mon, 28 Dec 2009 08:34:58 +0000 (08:34 +0000)
Doc/c-api/exceptions.rst
Doc/data/refcounts.dat
Include/pyerrors.h
Lib/test/test_exceptions.py
Misc/NEWS
Modules/_testcapimodule.c
Python/errors.c

index ba18af161abdaf144e808da19e88d5df5cc40422..474478c4c4b1944580d41266684d93d1463df81f 100644 (file)
@@ -433,6 +433,15 @@ is a separate error indicator for each thread.
    argument can be used to specify a dictionary of class variables and methods.
 
 
+.. cfunction:: PyObject* PyErr_NewExceptionWithDoc(char *name, char *doc, PyObject *base, PyObject *dict)
+
+   Same as :cfunc:`PyErr_NewException`, except that the new exception class can
+   easily be given a docstring: If *doc* is non-*NULL*, it will be used as the
+   docstring for the exception class.
+
+   .. versionadded:: 2.7
+
+
 .. cfunction:: void PyErr_WriteUnraisable(PyObject *obj)
 
    This utility function prints a warning message to ``sys.stderr`` when an
index c8633c097c1458d8328d8aff3caacb41314756d5..58bde9143aec53089553a7767b53a6383c1ef2b3 100644 (file)
@@ -242,6 +242,12 @@ PyErr_NewException:char*:name::
 PyErr_NewException:PyObject*:base:0:
 PyErr_NewException:PyObject*:dict:0:
 
+PyErr_NewExceptionWithDoc:PyObject*::+1:
+PyErr_NewExceptionWithDoc:char*:name::
+PyErr_NewExceptionWithDoc:char*:doc::
+PyErr_NewExceptionWithDoc:PyObject*:base:0:
+PyErr_NewExceptionWithDoc:PyObject*:dict:0:
+
 PyErr_NoMemory:PyObject*::null:
 
 PyErr_NormalizeException:void:::
index fedf91333df7613688f1393422312c9da019c5e6..432004a3e6aa58d1d36834ba71faee0ed88f7a3f 100644 (file)
@@ -220,8 +220,10 @@ PyAPI_FUNC(void) _PyErr_BadInternalCall(char *filename, int lineno);
 #define PyErr_BadInternalCall() _PyErr_BadInternalCall(__FILE__, __LINE__)
 
 /* Function to create a new exception */
-PyAPI_FUNC(PyObject *) PyErr_NewException(char *name, PyObject *base,
-                                         PyObject *dict);
+PyAPI_FUNC(PyObject *) PyErr_NewException(
+       char *name, PyObject *base, PyObject *dict);
+PyAPI_FUNC(PyObject *) PyErr_NewExceptionWithDoc(
+       char *name, char *doc, PyObject *base, PyObject *dict);
 PyAPI_FUNC(void) PyErr_WriteUnraisable(PyObject *);
 
 /* In sigcheck.c or signalmodule.c */
index d5ce80b0b7040ba8362e92a6541fb91d42676e9d..a30f42bcd68bea2be447c505013d00610de10ea7 100644 (file)
@@ -537,6 +537,45 @@ class TestSameStrAndUnicodeMsg(unittest.TestCase):
         self.assertRaises(UnicodeEncodeError, str, e)
         self.assertEqual(unicode(e), u'f\xf6\xf6')
 
+    def test_exception_with_doc(self):
+        import _testcapi
+        doc2 = "This is a test docstring."
+        doc4 = "This is another test docstring."
+
+        self.assertRaises(SystemError, _testcapi.make_exception_with_doc,
+                          "error1")
+
+        # test basic usage of PyErr_NewException
+        error1 = _testcapi.make_exception_with_doc("_testcapi.error1")
+        self.assertIs(type(error1), type)
+        self.assertTrue(issubclass(error1, Exception))
+        self.assertIsNone(error1.__doc__)
+
+        # test with given docstring
+        error2 = _testcapi.make_exception_with_doc("_testcapi.error2", doc2)
+        self.assertEqual(error2.__doc__, doc2)
+
+        # test with explicit base (without docstring)
+        error3 = _testcapi.make_exception_with_doc("_testcapi.error3",
+                                                   base=error2)
+        self.assertTrue(issubclass(error3, error2))
+
+        # test with explicit base tuple
+        class C(object):
+            pass
+        error4 = _testcapi.make_exception_with_doc("_testcapi.error4", doc4,
+                                                   (error3, C))
+        self.assertTrue(issubclass(error4, error3))
+        self.assertTrue(issubclass(error4, C))
+        self.assertEqual(error4.__doc__, doc4)
+
+        # test with explicit dictionary
+        error5 = _testcapi.make_exception_with_doc("_testcapi.error5", "",
+                                                   error4, {'a': 1})
+        self.assertTrue(issubclass(error5, error4))
+        self.assertEqual(error5.a, 1)
+        self.assertEqual(error5.__doc__, "")
+
 
 def test_main():
     run_unittest(ExceptionTests, TestSameStrAndUnicodeMsg)
index 8fbfc50b16bbb9172a1d0aa99c49baf29a56bec3..b23c672988bf18d8a5577db4acfeff8ab63c155e 100644 (file)
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -70,6 +70,11 @@ Library
 - Issue #7457: added a read_pkg_file method to
   distutils.dist.DistributionMetadata.
 
+C-API
+-----
+
+- Issue #7033: function ``PyErr_NewExceptionWithDoc()`` added.
+
 Build
 -----
 
index c8f208739039c430cf42679747a5e1b8901e0acc..f0dd75e0a5a8f0dcb0f6e2707b69b722a27a8a1a 100644 (file)
@@ -1198,6 +1198,26 @@ code_newempty(PyObject *self, PyObject *args)
        return (PyObject *)PyCode_NewEmpty(filename, funcname, firstlineno);
 }
 
+/* Test PyErr_NewExceptionWithDoc (also exercise PyErr_NewException).
+   Run via Lib/test/test_exceptions.py */
+static PyObject *
+make_exception_with_doc(PyObject *self, PyObject *args, PyObject *kwargs)
+{
+       char *name;
+       char *doc = NULL;
+       PyObject *base = NULL;
+       PyObject *dict = NULL;
+
+       static char *kwlist[] = {"name", "doc", "base", "dict", NULL};
+
+       if (!PyArg_ParseTupleAndKeywords(args, kwargs,
+                       "s|sOO:make_exception_with_doc", kwlist,
+                                        &name, &doc, &base, &dict))
+               return NULL;
+
+       return PyErr_NewExceptionWithDoc(name, doc, base, dict);
+}
+
 static PyMethodDef TestMethods[] = {
        {"raise_exception",     raise_exception,                 METH_VARARGS},
        {"test_config",         (PyCFunction)test_config,        METH_NOARGS},
@@ -1248,6 +1268,8 @@ static PyMethodDef TestMethods[] = {
 #endif
        {"traceback_print", traceback_print,             METH_VARARGS},
        {"code_newempty", code_newempty,                 METH_VARARGS},
+       {"make_exception_with_doc", (PyCFunction)make_exception_with_doc,
+        METH_VARARGS | METH_KEYWORDS},
        {NULL, NULL} /* sentinel */
 };
 
index 9f040ad523e74a7de9084d0aa73ca6a5fd5d0475..2d47779ca406a119d196702b2c867a3a3a089ecf 100644 (file)
@@ -604,6 +604,40 @@ PyErr_NewException(char *name, PyObject *base, PyObject *dict)
        return result;
 }
 
+
+/* Create an exception with docstring */
+PyObject *
+PyErr_NewExceptionWithDoc(char *name, char *doc, PyObject *base, PyObject *dict)
+{
+       int result;
+       PyObject *ret = NULL;
+       PyObject *mydict = NULL; /* points to the dict only if we create it */
+       PyObject *docobj;
+
+       if (dict == NULL) {
+               dict = mydict = PyDict_New();
+               if (dict == NULL) {
+                       return NULL;
+               }
+       }
+
+       if (doc != NULL) {
+               docobj = PyString_FromString(doc);
+               if (docobj == NULL)
+                       goto failure;
+               result = PyDict_SetItemString(dict, "__doc__", docobj);
+               Py_DECREF(docobj);
+               if (result < 0)
+                       goto failure;
+       }
+
+       ret = PyErr_NewException(name, base, dict);
+  failure:
+       Py_XDECREF(mydict);
+       return ret;
+}
+
+
 /* Call when an exception has occurred but there is no way for Python
    to handle it.  Examples: exception in __del__ or during GC. */
 void