You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1101 lines
33 KiB
C++

#ifndef GREENLET_REFS_HPP
#define GREENLET_REFS_HPP
#define PY_SSIZE_T_CLEAN
#include <Python.h>
//#include "greenlet_internal.hpp"
#include "greenlet_compiler_compat.hpp"
#include "greenlet_cpython_compat.hpp"
#include "greenlet_exceptions.hpp"
struct _greenlet;
struct _PyMainGreenlet;
typedef struct _greenlet PyGreenlet;
extern PyTypeObject PyGreenlet_Type;
#ifdef GREENLET_USE_STDIO
#include <iostream>
using std::cerr;
using std::endl;
#endif
namespace greenlet
{
class Greenlet;
namespace refs
{
// Type checkers throw a TypeError if the argument is not
// null, and isn't of the required Python type.
// (We can't use most of the defined type checkers
// like PyList_Check, etc, directly, because they are
// implemented as macros.)
typedef void (*TypeChecker)(void*);
G_FP_TMPL_STATIC inline void
NoOpChecker(void*)
{
return;
}
G_FP_TMPL_STATIC inline void
GreenletChecker(void *p)
{
if (!p) {
return;
}
PyTypeObject* typ = Py_TYPE(p);
// fast, common path. (PyObject_TypeCheck is a macro or
// static inline function, and it also does a
// direct comparison of the type pointers, but its fast
// path only handles one type)
if (typ == &PyGreenlet_Type) {
return;
}
if (!PyObject_TypeCheck(p, &PyGreenlet_Type)) {
std::string err("GreenletChecker: Expected any type of greenlet, not ");
err += Py_TYPE(p)->tp_name;
throw TypeError(err);
}
}
G_FP_TMPL_STATIC inline void
MainGreenletExactChecker(void *p);
template <typename T, TypeChecker>
class PyObjectPointer;
template<typename T, TypeChecker>
class OwnedReference;
template<typename T, TypeChecker>
class BorrowedReference;
typedef BorrowedReference<PyObject, NoOpChecker> BorrowedObject;
typedef OwnedReference<PyObject, NoOpChecker> OwnedObject;
class ImmortalObject;
class ImmortalString;
template<typename T, TypeChecker TC>
class _OwnedGreenlet;
typedef _OwnedGreenlet<PyGreenlet, GreenletChecker> OwnedGreenlet;
typedef _OwnedGreenlet<PyGreenlet, MainGreenletExactChecker> OwnedMainGreenlet;
template<typename T, TypeChecker TC>
class _BorrowedGreenlet;
typedef _BorrowedGreenlet<PyGreenlet, GreenletChecker> BorrowedGreenlet;
G_FP_TMPL_STATIC inline void
ContextExactChecker(void *p)
{
if (!p) {
return;
}
if (!PyContext_CheckExact(p)) {
throw TypeError(
"greenlet context must be a contextvars.Context or None"
);
}
}
typedef OwnedReference<PyObject, ContextExactChecker> OwnedContext;
}
}
namespace greenlet {
namespace refs {
// A set of classes to make reference counting rules in python
// code explicit.
//
// Rules of use:
// (1) Functions returning a new reference that the caller of the
// function is expected to dispose of should return a
// ``OwnedObject`` object. This object automatically releases its
// reference when it goes out of scope. It works like a ``std::shared_ptr``
// and can be copied or used as a function parameter (but don't do
// that). Note that constructing a ``OwnedObject`` from a
// PyObject* steals the reference.
// (2) Parameters to functions should be either a
// ``OwnedObject&``, or, more generally, a ``PyObjectPointer&``.
// If the function needs to create its own new reference, it can
// do so by copying to a local ``OwnedObject``.
// (3) Functions returning an existing pointer that is NOT
// incref'd, and which the caller MUST NOT decref,
// should return a ``BorrowedObject``.
//
// For a class with a single pointer member, whose constructor
// does nothing but copy a pointer parameter into the member, and
// which can then be converted back to the pointer type, compilers
// generate code that's the same as just passing the pointer.
// That is, func(BorrowedObject x) called like ``PyObject* p =
// ...; f(p)`` has 0 overhead. Similarly, they "unpack" to the
// pointer type with 0 overhead.
//
// If there are no virtual functions, no complex inheritance (maybe?) and
// no destructor, these can be directly used as parameters in
// Python callbacks like tp_init: the layout is the same as a
// single pointer. Only subclasses with trivial constructors that
// do nothing but set the single pointer member are safe to use
// that way.
// This is the base class for things that can be done with a
// PyObject pointer. It assumes nothing about memory management.
// NOTE: Nothing is virtual, so subclasses shouldn't add new
// storage fields or try to override these methods.
template <typename T=PyObject, TypeChecker TC=NoOpChecker>
class PyObjectPointer
{
public:
typedef T PyType;
protected:
T* p;
public:
explicit PyObjectPointer(T* it=nullptr) : p(it)
{
TC(p);
}
// We don't allow automatic casting to PyObject* at this
// level, because then we could be passed to Py_DECREF/INCREF,
// but we want nothing to do with memory management. If you
// know better, then you can use the get() method, like on a
// std::shared_ptr. Except we name it borrow() to clarify that
// if this is a reference-tracked object, the pointer you get
// back will go away when the object does.
// TODO: This should probably not exist here, but be moved
// down to relevant sub-types.
inline T* borrow() const noexcept
{
return this->p;
}
PyObject* borrow_o() const noexcept
{
return reinterpret_cast<PyObject*>(this->p);
}
inline T* operator->() const noexcept
{
return this->p;
}
bool is_None() const noexcept
{
return this->p == Py_None;
}
inline PyObject* acquire_or_None() const noexcept
{
PyObject* result = this->p ? reinterpret_cast<PyObject*>(this->p) : Py_None;
Py_INCREF(result);
return result;
}
explicit operator bool() const noexcept
{
return p != nullptr;
}
inline Py_ssize_t REFCNT() const noexcept
{
return p ? Py_REFCNT(p) : -42;
}
inline PyTypeObject* TYPE() const noexcept
{
return p ? Py_TYPE(p) : nullptr;
}
inline OwnedObject PyStr() const noexcept;
inline const std::string as_str() const noexcept;
inline OwnedObject PyGetAttr(const ImmortalObject& name) const noexcept;
inline OwnedObject PyRequireAttr(const char* const name) const;
inline OwnedObject PyRequireAttr(const ImmortalString& name) const;
inline OwnedObject PyCall(const BorrowedObject& arg) const;
inline OwnedObject PyCall(PyGreenlet* arg) const ;
inline OwnedObject PyCall(PyObject* arg) const ;
// PyObject_Call(this, args, kwargs);
inline OwnedObject PyCall(const BorrowedObject args,
const BorrowedObject kwargs) const;
inline OwnedObject PyCall(const OwnedObject& args,
const OwnedObject& kwargs) const;
protected:
void _set_raw_pointer(void* t)
{
TC(t);
p = reinterpret_cast<T*>(t);
}
void* _get_raw_pointer() const
{
return p;
}
};
#ifdef GREENLET_USE_STDIO
template<typename T, TypeChecker TC>
std::ostream& operator<<(std::ostream& os, const PyObjectPointer<T, TC>& s)
{
const std::type_info& t = typeid(s);
os << t.name()
<< "(addr=" << s.borrow()
<< ", refcnt=" << s.REFCNT()
<< ", value=" << s.as_str()
<< ")";
return os;
}
#endif
template<typename T, TypeChecker TC>
inline bool operator==(const PyObjectPointer<T, TC>& lhs, const void* const rhs) noexcept
{
return lhs.borrow_o() == rhs;
}
template<typename T, TypeChecker TC, typename X, TypeChecker XC>
inline bool operator==(const PyObjectPointer<T, TC>& lhs, const PyObjectPointer<X, XC>& rhs) noexcept
{
return lhs.borrow_o() == rhs.borrow_o();
}
template<typename T, TypeChecker TC, typename X, TypeChecker XC>
inline bool operator!=(const PyObjectPointer<T, TC>& lhs,
const PyObjectPointer<X, XC>& rhs) noexcept
{
return lhs.borrow_o() != rhs.borrow_o();
}
template<typename T=PyObject, TypeChecker TC=NoOpChecker>
class OwnedReference : public PyObjectPointer<T, TC>
{
private:
friend class OwnedList;
protected:
explicit OwnedReference(T* it) : PyObjectPointer<T, TC>(it)
{
}
public:
// Constructors
static OwnedReference<T, TC> consuming(PyObject* p)
{
return OwnedReference<T, TC>(reinterpret_cast<T*>(p));
}
static OwnedReference<T, TC> owning(T* p)
{
OwnedReference<T, TC> result(p);
Py_XINCREF(result.p);
return result;
}
OwnedReference() : PyObjectPointer<T, TC>(nullptr)
{}
explicit OwnedReference(const PyObjectPointer<>& other)
: PyObjectPointer<T, TC>(nullptr)
{
T* op = other.borrow();
TC(op);
this->p = other.borrow();
Py_XINCREF(this->p);
}
// It would be good to make use of the C++11 distinction
// between move and copy operations, e.g., constructing from a
// pointer should be a move operation.
// In the common case of ``OwnedObject x = Py_SomeFunction()``,
// the call to the copy constructor will be elided completely.
OwnedReference(const OwnedReference<T, TC>& other)
: PyObjectPointer<T, TC>(other.p)
{
Py_XINCREF(this->p);
}
static OwnedReference<PyObject> None()
{
Py_INCREF(Py_None);
return OwnedReference<PyObject>(Py_None);
}
// We can assign from exactly our type without any extra checking
OwnedReference<T, TC>& operator=(const OwnedReference<T, TC>& other)
{
Py_XINCREF(other.p);
const T* tmp = this->p;
this->p = other.p;
Py_XDECREF(tmp);
return *this;
}
OwnedReference<T, TC>& operator=(const BorrowedReference<T, TC> other)
{
return this->operator=(other.borrow());
}
OwnedReference<T, TC>& operator=(T* const other)
{
TC(other);
Py_XINCREF(other);
T* tmp = this->p;
this->p = other;
Py_XDECREF(tmp);
return *this;
}
// We can assign from an arbitrary reference type
// if it passes our check.
template<typename X, TypeChecker XC>
OwnedReference<T, TC>& operator=(const OwnedReference<X, XC>& other)
{
X* op = other.borrow();
TC(op);
return this->operator=(reinterpret_cast<T*>(op));
}
inline void steal(T* other)
{
assert(this->p == nullptr);
TC(other);
this->p = other;
}
T* relinquish_ownership()
{
T* result = this->p;
this->p = nullptr;
return result;
}
T* acquire() const
{
// Return a new reference.
// TODO: This may go away when we have reference objects
// throughout the code.
Py_XINCREF(this->p);
return this->p;
}
// Nothing else declares a destructor, we're the leaf, so we
// should be able to get away without virtual.
~OwnedReference()
{
Py_CLEAR(this->p);
}
void CLEAR()
{
Py_CLEAR(this->p);
assert(this->p == nullptr);
}
};
static inline
void operator<<=(PyObject*& target, OwnedObject& o)
{
target = o.relinquish_ownership();
}
class NewReference : public OwnedObject
{
private:
G_NO_COPIES_OF_CLS(NewReference);
public:
// Consumes the reference. Only use this
// for API return values.
NewReference(PyObject* it) : OwnedObject(it)
{
}
};
class NewDictReference : public NewReference
{
private:
G_NO_COPIES_OF_CLS(NewDictReference);
public:
NewDictReference() : NewReference(PyDict_New())
{
if (!this->p) {
throw PyErrOccurred();
}
}
void SetItem(const char* const key, PyObject* value)
{
Require(PyDict_SetItemString(this->p, key, value));
}
void SetItem(const PyObjectPointer<>& key, PyObject* value)
{
Require(PyDict_SetItem(this->p, key.borrow_o(), value));
}
};
template<typename T=PyGreenlet, TypeChecker TC=GreenletChecker>
class _OwnedGreenlet: public OwnedReference<T, TC>
{
private:
protected:
_OwnedGreenlet(T* it) : OwnedReference<T, TC>(it)
{}
public:
_OwnedGreenlet() : OwnedReference<T, TC>()
{}
_OwnedGreenlet(const _OwnedGreenlet<T, TC>& other) : OwnedReference<T, TC>(other)
{
}
_OwnedGreenlet(OwnedMainGreenlet& other) :
OwnedReference<T, TC>(reinterpret_cast<T*>(other.acquire()))
{
}
_OwnedGreenlet(const BorrowedGreenlet& other);
// Steals a reference.
static _OwnedGreenlet<T, TC> consuming(PyGreenlet* it)
{
return _OwnedGreenlet<T, TC>(reinterpret_cast<T*>(it));
}
inline _OwnedGreenlet<T, TC>& operator=(const OwnedGreenlet& other)
{
return this->operator=(other.borrow());
}
inline _OwnedGreenlet<T, TC>& operator=(const BorrowedGreenlet& other);
_OwnedGreenlet<T, TC>& operator=(const OwnedMainGreenlet& other)
{
PyGreenlet* owned = other.acquire();
Py_XDECREF(this->p);
this->p = reinterpret_cast<T*>(owned);
return *this;
}
_OwnedGreenlet<T, TC>& operator=(T* const other)
{
OwnedReference<T, TC>::operator=(other);
return *this;
}
T* relinquish_ownership()
{
T* result = this->p;
this->p = nullptr;
return result;
}
PyObject* relinquish_ownership_o()
{
return reinterpret_cast<PyObject*>(relinquish_ownership());
}
inline Greenlet* operator->() const noexcept;
inline operator Greenlet*() const noexcept;
};
template <typename T=PyObject, TypeChecker TC=NoOpChecker>
class BorrowedReference : public PyObjectPointer<T, TC>
{
public:
// Allow implicit creation from PyObject* pointers as we
// transition to using these classes. Also allow automatic
// conversion to PyObject* for passing to C API calls and even
// for Py_INCREF/DECREF, because we ourselves do no memory management.
BorrowedReference(T* it) : PyObjectPointer<T, TC>(it)
{}
BorrowedReference(const PyObjectPointer<T>& ref) : PyObjectPointer<T, TC>(ref.borrow())
{}
BorrowedReference() : PyObjectPointer<T, TC>(nullptr)
{}
operator T*() const
{
return this->p;
}
};
typedef BorrowedReference<PyObject> BorrowedObject;
//typedef BorrowedReference<PyGreenlet> BorrowedGreenlet;
template<typename T=PyGreenlet, TypeChecker TC=GreenletChecker>
class _BorrowedGreenlet : public BorrowedReference<T, TC>
{
public:
_BorrowedGreenlet() :
BorrowedReference<T, TC>(nullptr)
{}
_BorrowedGreenlet(T* it) :
BorrowedReference<T, TC>(it)
{}
_BorrowedGreenlet(const BorrowedObject& it);
_BorrowedGreenlet(const OwnedGreenlet& it) :
BorrowedReference<T, TC>(it.borrow())
{}
_BorrowedGreenlet<T, TC>& operator=(const BorrowedObject& other);
// We get one of these for PyGreenlet, but one for PyObject
// is handy as well
operator PyObject*() const
{
return reinterpret_cast<PyObject*>(this->p);
}
inline Greenlet* operator->() const noexcept;
inline operator Greenlet*() const noexcept;
};
typedef _BorrowedGreenlet<PyGreenlet> BorrowedGreenlet;
template<typename T, TypeChecker TC>
_OwnedGreenlet<T, TC>::_OwnedGreenlet(const BorrowedGreenlet& other)
: OwnedReference<T, TC>(reinterpret_cast<T*>(other.borrow()))
{
Py_XINCREF(this->p);
}
class BorrowedMainGreenlet
: public _BorrowedGreenlet<PyGreenlet, MainGreenletExactChecker>
{
public:
BorrowedMainGreenlet(const OwnedMainGreenlet& it) :
_BorrowedGreenlet<PyGreenlet, MainGreenletExactChecker>(it.borrow())
{}
BorrowedMainGreenlet(PyGreenlet* it=nullptr)
: _BorrowedGreenlet<PyGreenlet, MainGreenletExactChecker>(it)
{}
};
template<typename T, TypeChecker TC>
_OwnedGreenlet<T, TC>& _OwnedGreenlet<T, TC>::operator=(const BorrowedGreenlet& other)
{
return this->operator=(other.borrow());
}
class ImmortalObject : public PyObjectPointer<>
{
private:
G_NO_ASSIGNMENT_OF_CLS(ImmortalObject);
public:
explicit ImmortalObject(PyObject* it) : PyObjectPointer<>(it)
{
}
ImmortalObject(const ImmortalObject& other)
: PyObjectPointer<>(other.p)
{
}
/**
* Become the new owner of the object. Does not change the
* reference count.
*/
ImmortalObject& operator=(PyObject* it)
{
assert(this->p == nullptr);
this->p = it;
return *this;
}
static ImmortalObject consuming(PyObject* it)
{
return ImmortalObject(it);
}
inline operator PyObject*() const
{
return this->p;
}
};
class ImmortalString : public ImmortalObject
{
private:
G_NO_COPIES_OF_CLS(ImmortalString);
const char* str;
public:
ImmortalString(const char* const str) :
ImmortalObject(str ? Require(PyUnicode_InternFromString(str)) : nullptr)
{
this->str = str;
}
inline ImmortalString& operator=(const char* const str)
{
if (!this->p) {
this->p = Require(PyUnicode_InternFromString(str));
this->str = str;
}
else {
assert(this->str == str);
}
return *this;
}
inline operator std::string() const
{
return this->str;
}
};
class ImmortalEventName : public ImmortalString
{
private:
G_NO_COPIES_OF_CLS(ImmortalEventName);
public:
ImmortalEventName(const char* const str) : ImmortalString(str)
{}
};
class ImmortalException : public ImmortalObject
{
private:
G_NO_COPIES_OF_CLS(ImmortalException);
public:
ImmortalException(const char* const name, PyObject* base=nullptr) :
ImmortalObject(name
// Python 2.7 isn't const correct
? Require(PyErr_NewException((char*)name, base, nullptr))
: nullptr)
{}
inline bool PyExceptionMatches() const
{
return PyErr_ExceptionMatches(this->p) > 0;
}
};
template<typename T, TypeChecker TC>
inline OwnedObject PyObjectPointer<T, TC>::PyStr() const noexcept
{
if (!this->p) {
return OwnedObject();
}
return OwnedObject::consuming(PyObject_Str(reinterpret_cast<PyObject*>(this->p)));
}
template<typename T, TypeChecker TC>
inline const std::string PyObjectPointer<T, TC>::as_str() const noexcept
{
// NOTE: This is not Python exception safe.
if (this->p) {
// The Python APIs return a cached char* value that's only valid
// as long as the original object stays around, and we're
// about to (probably) toss it. Hence the copy to std::string.
OwnedObject py_str = this->PyStr();
if (!py_str) {
return "(nil)";
}
return PyUnicode_AsUTF8(py_str.borrow());
}
return "(nil)";
}
template<typename T, TypeChecker TC>
inline OwnedObject PyObjectPointer<T, TC>::PyGetAttr(const ImmortalObject& name) const noexcept
{
assert(this->p);
return OwnedObject::consuming(PyObject_GetAttr(reinterpret_cast<PyObject*>(this->p), name));
}
template<typename T, TypeChecker TC>
inline OwnedObject PyObjectPointer<T, TC>::PyRequireAttr(const char* const name) const
{
assert(this->p);
return OwnedObject::consuming(Require(PyObject_GetAttrString(this->p, name), name));
}
template<typename T, TypeChecker TC>
inline OwnedObject PyObjectPointer<T, TC>::PyRequireAttr(const ImmortalString& name) const
{
assert(this->p);
return OwnedObject::consuming(Require(
PyObject_GetAttr(
reinterpret_cast<PyObject*>(this->p),
name
),
name
));
}
template<typename T, TypeChecker TC>
inline OwnedObject PyObjectPointer<T, TC>::PyCall(const BorrowedObject& arg) const
{
return this->PyCall(arg.borrow());
}
template<typename T, TypeChecker TC>
inline OwnedObject PyObjectPointer<T, TC>::PyCall(PyGreenlet* arg) const
{
return this->PyCall(reinterpret_cast<PyObject*>(arg));
}
template<typename T, TypeChecker TC>
inline OwnedObject PyObjectPointer<T, TC>::PyCall(PyObject* arg) const
{
assert(this->p);
return OwnedObject::consuming(PyObject_CallFunctionObjArgs(this->p, arg, NULL));
}
template<typename T, TypeChecker TC>
inline OwnedObject PyObjectPointer<T, TC>::PyCall(const BorrowedObject args,
const BorrowedObject kwargs) const
{
assert(this->p);
return OwnedObject::consuming(PyObject_Call(this->p, args, kwargs));
}
template<typename T, TypeChecker TC>
inline OwnedObject PyObjectPointer<T, TC>::PyCall(const OwnedObject& args,
const OwnedObject& kwargs) const
{
assert(this->p);
return OwnedObject::consuming(PyObject_Call(this->p, args.borrow(), kwargs.borrow()));
}
G_FP_TMPL_STATIC inline void
ListChecker(void * p)
{
if (!p) {
return;
}
if (!PyList_Check(p)) {
throw TypeError("Expected a list");
}
}
class OwnedList : public OwnedReference<PyObject, ListChecker>
{
private:
G_NO_ASSIGNMENT_OF_CLS(OwnedList);
public:
// TODO: Would like to use move.
explicit OwnedList(const OwnedObject& other)
: OwnedReference<PyObject, ListChecker>(other)
{
}
OwnedList& operator=(const OwnedObject& other)
{
if (other && PyList_Check(other.p)) {
// Valid list. Own a new reference to it, discard the
// reference to what we did own.
PyObject* new_ptr = other.p;
Py_INCREF(new_ptr);
Py_XDECREF(this->p);
this->p = new_ptr;
}
else {
// Either the other object was NULL (an error) or it
// wasn't a list. Either way, we're now invalidated.
Py_XDECREF(this->p);
this->p = nullptr;
}
return *this;
}
inline bool empty() const
{
return PyList_GET_SIZE(p) == 0;
}
inline Py_ssize_t size() const
{
return PyList_GET_SIZE(p);
}
inline BorrowedObject at(const Py_ssize_t index) const
{
return PyList_GET_ITEM(p, index);
}
inline void clear()
{
PyList_SetSlice(p, 0, PyList_GET_SIZE(p), NULL);
}
};
// Use this to represent the module object used at module init
// time.
// This could either be a borrowed (Py2) or new (Py3) reference;
// either way, we don't want to do any memory management
// on it here, Python itself will handle that.
// XXX: Actually, that's not quite right. On Python 3, if an
// exception occurs before we return to the interpreter, this will
// leak; but all previous versions also had that problem.
class CreatedModule : public PyObjectPointer<>
{
private:
G_NO_COPIES_OF_CLS(CreatedModule);
public:
CreatedModule(PyModuleDef& mod_def) : PyObjectPointer<>(
Require(PyModule_Create(&mod_def)))
{
}
// PyAddObject(): Add a reference to the object to the module.
// On return, the reference count of the object is unchanged.
//
// The docs warn that PyModule_AddObject only steals the
// reference on success, so if it fails after we've incref'd
// or allocated, we're responsible for the decref.
void PyAddObject(const char* name, const long new_bool)
{
OwnedObject p = OwnedObject::consuming(Require(PyBool_FromLong(new_bool)));
this->PyAddObject(name, p);
}
void PyAddObject(const char* name, const OwnedObject& new_object)
{
// The caller already owns a reference they will decref
// when their variable goes out of scope, we still need to
// incref/decref.
this->PyAddObject(name, new_object.borrow());
}
void PyAddObject(const char* name, const ImmortalObject& new_object)
{
this->PyAddObject(name, new_object.borrow());
}
void PyAddObject(const char* name, PyTypeObject& type)
{
this->PyAddObject(name, reinterpret_cast<PyObject*>(&type));
}
void PyAddObject(const char* name, PyObject* new_object)
{
Py_INCREF(new_object);
try {
Require(PyModule_AddObject(this->p, name, new_object));
}
catch (const PyErrOccurred&) {
Py_DECREF(p);
throw;
}
}
};
class PyErrFetchParam : public PyObjectPointer<>
{
// Not an owned object, because we can't be initialized with
// one, and we only sometimes acquire ownership.
private:
G_NO_COPIES_OF_CLS(PyErrFetchParam);
public:
// To allow declaring these and passing them to
// PyErr_Fetch we implement the empty constructor,
// and the address operator.
PyErrFetchParam() : PyObjectPointer<>(nullptr)
{
}
PyObject** operator&()
{
return &this->p;
}
// This allows us to pass one directly without the &,
// BUT it has higher precedence than the bool operator
// if it's not explicit.
operator PyObject**()
{
return &this->p;
}
// We don't want to be able to pass these to Py_DECREF and
// such so we don't have the implicit PyObject* conversion.
inline PyObject* relinquish_ownership()
{
PyObject* result = this->p;
this->p = nullptr;
return result;
}
~PyErrFetchParam()
{
Py_XDECREF(p);
}
};
class OwnedErrPiece : public OwnedObject
{
private:
public:
// Unlike OwnedObject, this increments the refcount.
OwnedErrPiece(PyObject* p=nullptr) : OwnedObject(p)
{
this->acquire();
}
PyObject** operator&()
{
return &this->p;
}
inline operator PyObject*() const
{
return this->p;
}
operator PyTypeObject*() const
{
return reinterpret_cast<PyTypeObject*>(this->p);
}
};
class PyErrPieces
{
private:
OwnedErrPiece type;
OwnedErrPiece instance;
OwnedErrPiece traceback;
bool restored;
public:
// Takes new references; if we're destroyed before
// restoring the error, we drop the references.
PyErrPieces(PyObject* t, PyObject* v, PyObject* tb) :
type(t),
instance(v),
traceback(tb),
restored(0)
{
this->normalize();
}
PyErrPieces() :
restored(0)
{
// PyErr_Fetch transfers ownership to us, so
// we don't actually need to INCREF; but we *do*
// need to DECREF if we're not restored.
PyErrFetchParam t, v, tb;
PyErr_Fetch(&t, &v, &tb);
type.steal(t.relinquish_ownership());
instance.steal(v.relinquish_ownership());
traceback.steal(tb.relinquish_ownership());
}
void PyErrRestore()
{
// can only do this once
assert(!this->restored);
this->restored = true;
PyErr_Restore(
this->type.relinquish_ownership(),
this->instance.relinquish_ownership(),
this->traceback.relinquish_ownership());
assert(!this->type && !this->instance && !this->traceback);
}
private:
void normalize()
{
// First, check the traceback argument, replacing None,
// with NULL
if (traceback.is_None()) {
traceback = nullptr;
}
if (traceback && !PyTraceBack_Check(traceback.borrow())) {
throw PyErrOccurred(PyExc_TypeError,
"throw() third argument must be a traceback object");
}
if (PyExceptionClass_Check(type)) {
// If we just had a type, we'll now have a type and
// instance.
// The type's refcount will have gone up by one
// because of the instance and the instance will have
// a refcount of one. Either way, we owned, and still
// do own, exactly one reference.
PyErr_NormalizeException(&type, &instance, &traceback);
}
else if (PyExceptionInstance_Check(type)) {
/* Raising an instance --- usually that means an
object that is a subclass of BaseException, but on
Python 2, that can also mean an arbitrary old-style
object. The value should be a dummy. */
if (instance && !instance.is_None()) {
throw PyErrOccurred(
PyExc_TypeError,
"instance exception may not have a separate value");
}
/* Normalize to raise <class>, <instance> */
this->instance = this->type;
this->type = PyExceptionInstance_Class(instance.borrow());
/*
It would be tempting to do this:
Py_ssize_t type_count = Py_REFCNT(Py_TYPE(instance.borrow()));
this->type = PyExceptionInstance_Class(instance.borrow());
assert(this->type.REFCNT() == type_count + 1);
But that doesn't work on Python 2 in the case of
old-style instances: The result of Py_TYPE is going to
be the global shared <type instance> that all
old-style classes have, while the return of Instance_Class()
will be the Python-level class object. The two are unrelated.
*/
}
else {
/* Not something you can raise. throw() fails. */
PyErr_Format(PyExc_TypeError,
"exceptions must be classes, or instances, not %s",
Py_TYPE(type.borrow())->tp_name);
throw PyErrOccurred();
}
}
};
// PyArg_Parse's O argument returns a borrowed reference.
class PyArgParseParam : public BorrowedObject
{
private:
G_NO_COPIES_OF_CLS(PyArgParseParam);
public:
explicit PyArgParseParam(PyObject* p=nullptr) : BorrowedObject(p)
{
}
inline PyObject** operator&()
{
return &this->p;
}
};
};};
#endif