관리-도구
편집 파일: greenlet_refs.hpp
#ifndef GREENLET_REFS_HPP #define GREENLET_REFS_HPP #define PY_SSIZE_T_CLEAN #include <Python.h> #include <string> //#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*); void NoOpChecker(void*) { return; } 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); } } 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; 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``. // XXX: The following two paragraphs do not hold for all platforms. // Notably, 32-bit PPC Linux passes structs by reference, not by // value, so this actually doesn't work. (Although that's the only // platform that doesn't work on.) DO NOT ATTEMPT IT. The // unfortunate consequence of that is that the slots which we // *know* are already type safe will wind up calling the type // checker function (when we had the slots accepting // BorrowedGreenlet, this was bypassed), so this slows us down. // TODO: Optimize this again. // 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: 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. T* borrow() const noexcept { return this->p; } PyObject* borrow_o() const noexcept { return reinterpret_cast<PyObject*>(this->p); } T* operator->() const noexcept { return this->p; } bool is_None() const noexcept { return this->p == Py_None; } 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 this->p != nullptr; } bool operator!() const noexcept { return this->p == nullptr; } Py_ssize_t REFCNT() const noexcept { return p ? Py_REFCNT(p) : -42; } 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 PyObject* const rhs) noexcept { return static_cast<const void*>(lhs.borrow_o()) == static_cast<const void*>(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); } Greenlet* operator->() const noexcept; 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())); } 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