Skip to content

Commit 1e28a27

Browse files
committed
Merged revisions 77088 via svnmerge from
svn+ssh://pythondev@svn.python.org/python/trunk ........ r77088 | georg.brandl | 2009-12-28 09:34:58 +0100 (Mo, 28 Dez 2009) | 1 line #7033: add new API function PyErr_NewExceptionWithDoc, for easily giving new exceptions a docstring. ........
1 parent 127d470 commit 1e28a27

7 files changed

Lines changed: 118 additions & 2 deletions

File tree

Doc/c-api/exceptions.rst

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -404,6 +404,15 @@ in various ways. There is a separate error indicator for each thread.
404404
argument can be used to specify a dictionary of class variables and methods.
405405

406406

407+
.. cfunction:: PyObject* PyErr_NewExceptionWithDoc(char *name, char *doc, PyObject *base, PyObject *dict)
408+
409+
Same as :cfunc:`PyErr_NewException`, except that the new exception class can
410+
easily be given a docstring: If *doc* is non-*NULL*, it will be used as the
411+
docstring for the exception class.
412+
413+
.. versionadded:: 3.2
414+
415+
407416
.. cfunction:: void PyErr_WriteUnraisable(PyObject *obj)
408417

409418
This utility function prints a warning message to ``sys.stderr`` when an

Doc/data/refcounts.dat

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -281,6 +281,12 @@ PyErr_NewException:char*:name::
281281
PyErr_NewException:PyObject*:base:0:
282282
PyErr_NewException:PyObject*:dict:0:
283283

284+
PyErr_NewExceptionWithDoc:PyObject*::+1:
285+
PyErr_NewExceptionWithDoc:char*:name::
286+
PyErr_NewExceptionWithDoc:char*:doc::
287+
PyErr_NewExceptionWithDoc:PyObject*:base:0:
288+
PyErr_NewExceptionWithDoc:PyObject*:dict:0:
289+
284290
PyErr_NoMemory:PyObject*::null:
285291

286292
PyErr_NormalizeException:void:::

Include/pyerrors.h

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -210,8 +210,10 @@ PyAPI_FUNC(void) _PyErr_BadInternalCall(const char *filename, int lineno);
210210
#define PyErr_BadInternalCall() _PyErr_BadInternalCall(__FILE__, __LINE__)
211211

212212
/* Function to create a new exception */
213-
PyAPI_FUNC(PyObject *) PyErr_NewException(const char *name, PyObject *base,
214-
PyObject *dict);
213+
PyAPI_FUNC(PyObject *) PyErr_NewException(
214+
const char *name, PyObject *base, PyObject *dict);
215+
PyAPI_FUNC(PyObject *) PyErr_NewExceptionWithDoc(
216+
const char *name, const char *doc, PyObject *base, PyObject *dict);
215217
PyAPI_FUNC(void) PyErr_WriteUnraisable(PyObject *);
216218

217219
/* In sigcheck.c or signalmodule.c */

Lib/test/test_exceptions.py

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -618,6 +618,46 @@ def raiseMemError():
618618
tb2 = raiseMemError()
619619
self.assertEqual(tb1, tb2)
620620

621+
def test_exception_with_doc(self):
622+
import _testcapi
623+
doc2 = "This is a test docstring."
624+
doc4 = "This is another test docstring."
625+
626+
self.assertRaises(SystemError, _testcapi.make_exception_with_doc,
627+
"error1")
628+
629+
# test basic usage of PyErr_NewException
630+
error1 = _testcapi.make_exception_with_doc("_testcapi.error1")
631+
self.assertIs(type(error1), type)
632+
self.assertTrue(issubclass(error1, Exception))
633+
self.assertIsNone(error1.__doc__)
634+
635+
# test with given docstring
636+
error2 = _testcapi.make_exception_with_doc("_testcapi.error2", doc2)
637+
self.assertEqual(error2.__doc__, doc2)
638+
639+
# test with explicit base (without docstring)
640+
error3 = _testcapi.make_exception_with_doc("_testcapi.error3",
641+
base=error2)
642+
self.assertTrue(issubclass(error3, error2))
643+
644+
# test with explicit base tuple
645+
class C(object):
646+
pass
647+
error4 = _testcapi.make_exception_with_doc("_testcapi.error4", doc4,
648+
(error3, C))
649+
self.assertTrue(issubclass(error4, error3))
650+
self.assertTrue(issubclass(error4, C))
651+
self.assertEqual(error4.__doc__, doc4)
652+
653+
# test with explicit dictionary
654+
error5 = _testcapi.make_exception_with_doc("_testcapi.error5", "",
655+
error4, {'a': 1})
656+
self.assertTrue(issubclass(error5, error4))
657+
self.assertEqual(error5.a, 1)
658+
self.assertEqual(error5.__doc__, "")
659+
660+
621661
def test_main():
622662
run_unittest(ExceptionTests)
623663

Misc/NEWS

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,8 @@ Core and Builtins
136136
C-API
137137
-----
138138

139+
- Issue #7033: function ``PyErr_NewExceptionWithDoc()`` added.
140+
139141
- Issue #7414: 'C' code wasn't being skipped properly (for keyword arguments)
140142
in PyArg_ParseTupleAndKeywords.
141143

Modules/_testcapimodule.c

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1716,6 +1716,26 @@ code_newempty(PyObject *self, PyObject *args)
17161716
return (PyObject *)PyCode_NewEmpty(filename, funcname, firstlineno);
17171717
}
17181718

1719+
/* Test PyErr_NewExceptionWithDoc (also exercise PyErr_NewException).
1720+
Run via Lib/test/test_exceptions.py */
1721+
static PyObject *
1722+
make_exception_with_doc(PyObject *self, PyObject *args, PyObject *kwargs)
1723+
{
1724+
const char *name;
1725+
const char *doc = NULL;
1726+
PyObject *base = NULL;
1727+
PyObject *dict = NULL;
1728+
1729+
static char *kwlist[] = {"name", "doc", "base", "dict", NULL};
1730+
1731+
if (!PyArg_ParseTupleAndKeywords(args, kwargs,
1732+
"s|sOO:make_exception_with_doc", kwlist,
1733+
&name, &doc, &base, &dict))
1734+
return NULL;
1735+
1736+
return PyErr_NewExceptionWithDoc(name, doc, base, dict);
1737+
}
1738+
17191739
static PyMethodDef TestMethods[] = {
17201740
{"raise_exception", raise_exception, METH_VARARGS},
17211741
{"raise_memoryerror", (PyCFunction)raise_memoryerror, METH_NOARGS},
@@ -1774,6 +1794,8 @@ static PyMethodDef TestMethods[] = {
17741794
{"exception_print", exception_print, METH_VARARGS},
17751795
{"argparsing", argparsing, METH_VARARGS},
17761796
{"code_newempty", code_newempty, METH_VARARGS},
1797+
{"make_exception_with_doc", (PyCFunction)make_exception_with_doc,
1798+
METH_VARARGS | METH_KEYWORDS},
17771799
{NULL, NULL} /* sentinel */
17781800
};
17791801

Python/errors.c

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -693,6 +693,41 @@ PyErr_NewException(const char *name, PyObject *base, PyObject *dict)
693693
return result;
694694
}
695695

696+
697+
/* Create an exception with docstring */
698+
PyObject *
699+
PyErr_NewExceptionWithDoc(const char *name, const char *doc,
700+
PyObject *base, PyObject *dict)
701+
{
702+
int result;
703+
PyObject *ret = NULL;
704+
PyObject *mydict = NULL; /* points to the dict only if we create it */
705+
PyObject *docobj;
706+
707+
if (dict == NULL) {
708+
dict = mydict = PyDict_New();
709+
if (dict == NULL) {
710+
return NULL;
711+
}
712+
}
713+
714+
if (doc != NULL) {
715+
docobj = PyUnicode_FromString(doc);
716+
if (docobj == NULL)
717+
goto failure;
718+
result = PyDict_SetItemString(dict, "__doc__", docobj);
719+
Py_DECREF(docobj);
720+
if (result < 0)
721+
goto failure;
722+
}
723+
724+
ret = PyErr_NewException(name, base, dict);
725+
failure:
726+
Py_XDECREF(mydict);
727+
return ret;
728+
}
729+
730+
696731
/* Call when an exception has occurred but there is no way for Python
697732
to handle it. Examples: exception in __del__ or during GC. */
698733
void

0 commit comments

Comments
 (0)