123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171 |
- #ifndef GREENLET_EXCEPTIONS_HPP
- #define GREENLET_EXCEPTIONS_HPP
- #define PY_SSIZE_T_CLEAN
- #include <Python.h>
- #include <stdexcept>
- #include <string>
- #ifdef __clang__
- # pragma clang diagnostic push
- # pragma clang diagnostic ignored "-Wunused-function"
- #endif
- namespace greenlet {
- class PyErrOccurred : public std::runtime_error
- {
- public:
- // CAUTION: In debug builds, may run arbitrary Python code.
- static const PyErrOccurred
- from_current()
- {
- assert(PyErr_Occurred());
- #ifndef NDEBUG
- // This is not exception safe, and
- // not necessarily safe in general (what if it switches?)
- // But we only do this in debug mode, where we are in
- // tight control of what exceptions are getting raised and
- // can prevent those issues.
- // You can't call PyObject_Str with a pending exception.
- PyObject* typ;
- PyObject* val;
- PyObject* tb;
- PyErr_Fetch(&typ, &val, &tb);
- PyObject* typs = PyObject_Str(typ);
- PyObject* vals = PyObject_Str(val ? val : typ);
- const char* typ_msg = PyUnicode_AsUTF8(typs);
- const char* val_msg = PyUnicode_AsUTF8(vals);
- PyErr_Restore(typ, val, tb);
- std::string msg(typ_msg);
- msg += ": ";
- msg += val_msg;
- PyErrOccurred ex(msg);
- Py_XDECREF(typs);
- Py_XDECREF(vals);
- return ex;
- #else
- return PyErrOccurred();
- #endif
- }
- PyErrOccurred() : std::runtime_error("")
- {
- assert(PyErr_Occurred());
- }
- PyErrOccurred(const std::string& msg) : std::runtime_error(msg)
- {
- assert(PyErr_Occurred());
- }
- PyErrOccurred(PyObject* exc_kind, const char* const msg)
- : std::runtime_error(msg)
- {
- PyErr_SetString(exc_kind, msg);
- }
- PyErrOccurred(PyObject* exc_kind, const std::string msg)
- : std::runtime_error(msg)
- {
- // This copies the c_str, so we don't have any lifetime
- // issues to worry about.
- PyErr_SetString(exc_kind, msg.c_str());
- }
- PyErrOccurred(PyObject* exc_kind,
- const std::string msg, //This is the format
- //string; that's not
- //usually safe!
- PyObject* borrowed_obj_one, PyObject* borrowed_obj_two)
- : std::runtime_error(msg)
- {
- //This is designed specifically for the
- //``check_switch_allowed`` function.
- // PyObject_Str and PyObject_Repr are safe to call with
- // NULL pointers; they return the string "<NULL>" in that
- // case.
- // This function always returns null.
- PyErr_Format(exc_kind,
- msg.c_str(),
- borrowed_obj_one, borrowed_obj_two);
- }
- };
- class TypeError : public PyErrOccurred
- {
- public:
- TypeError(const char* const what)
- : PyErrOccurred(PyExc_TypeError, what)
- {
- }
- TypeError(const std::string what)
- : PyErrOccurred(PyExc_TypeError, what)
- {
- }
- };
- class ValueError : public PyErrOccurred
- {
- public:
- ValueError(const char* const what)
- : PyErrOccurred(PyExc_ValueError, what)
- {
- }
- };
- class AttributeError : public PyErrOccurred
- {
- public:
- AttributeError(const char* const what)
- : PyErrOccurred(PyExc_AttributeError, what)
- {
- }
- };
- /**
- * Calls `Py_FatalError` when constructed, so you can't actually
- * throw this. It just makes static analysis easier.
- */
- class PyFatalError : public std::runtime_error
- {
- public:
- PyFatalError(const char* const msg)
- : std::runtime_error(msg)
- {
- Py_FatalError(msg);
- }
- };
- static inline PyObject*
- Require(PyObject* p, const std::string& msg="")
- {
- if (!p) {
- throw PyErrOccurred(msg);
- }
- return p;
- };
- static inline void
- Require(const int retval)
- {
- if (retval < 0) {
- throw PyErrOccurred();
- }
- };
- };
- #ifdef __clang__
- # pragma clang diagnostic pop
- #endif
- #endif
|