diff options
Diffstat (limited to 'contrib/llvm-project/lldb/source/Plugins/ScriptInterpreter/Python/PythonDataObjects.h')
-rw-r--r-- | contrib/llvm-project/lldb/source/Plugins/ScriptInterpreter/Python/PythonDataObjects.h | 622 |
1 files changed, 462 insertions, 160 deletions
diff --git a/contrib/llvm-project/lldb/source/Plugins/ScriptInterpreter/Python/PythonDataObjects.h b/contrib/llvm-project/lldb/source/Plugins/ScriptInterpreter/Python/PythonDataObjects.h index 049ce90bb1b1..373d3212697d 100644 --- a/contrib/llvm-project/lldb/source/Plugins/ScriptInterpreter/Python/PythonDataObjects.h +++ b/contrib/llvm-project/lldb/source/Plugins/ScriptInterpreter/Python/PythonDataObjects.h @@ -6,6 +6,45 @@ // //===----------------------------------------------------------------------===// +// +// !! FIXME FIXME FIXME !! +// +// Python APIs nearly all can return an exception. They do this +// by returning NULL, or -1, or some such value and setting +// the exception state with PyErr_Set*(). Exceptions must be +// handled before further python API functions are called. Failure +// to do so will result in asserts on debug builds of python. +// It will also sometimes, but not usually result in crashes of +// release builds. +// +// Nearly all the code in this header does not handle python exceptions +// correctly. It should all be converted to return Expected<> or +// Error types to capture the exception. +// +// Everything in this file except functions that return Error or +// Expected<> is considered deprecated and should not be +// used in new code. If you need to use it, fix it first. +// +// +// TODOs for this file +// +// * Make all methods safe for exceptions. +// +// * Eliminate method signatures that must translate exceptions into +// empty objects or NULLs. Almost everything here should return +// Expected<>. It should be acceptable for certain operations that +// can never fail to assert instead, such as the creation of +// PythonString from a string literal. +// +// * Elimintate Reset(), and make all non-default constructors private. +// Python objects should be created with Retain<> or Take<>, and they +// should be assigned with operator= +// +// * Eliminate default constructors, make python objects always +// nonnull, and use optionals where necessary. +// + + #ifndef LLDB_PLUGINS_SCRIPTINTERPRETER_PYTHON_PYTHONDATAOBJECTS_H #define LLDB_PLUGINS_SCRIPTINTERPRETER_PYTHON_PYTHONDATAOBJECTS_H @@ -20,12 +59,15 @@ #include "llvm/ADT/ArrayRef.h" namespace lldb_private { +namespace python { +class PythonObject; class PythonBytes; class PythonString; class PythonList; class PythonDictionary; class PythonInteger; +class PythonException; class StructuredPythonObject : public StructuredData::Generic { public: @@ -43,7 +85,7 @@ public: bool IsValid() const override { return GetValue() && GetValue() != Py_None; } - void Dump(Stream &s, bool pretty_print = true) const override; + void Serialize(llvm::json::OStream &s) const override; private: DISALLOW_COPY_AND_ASSIGN(StructuredPythonObject); @@ -72,61 +114,137 @@ enum class PyRefType { // not call Py_INCREF. }; -enum class PyInitialValue { Invalid, Empty }; -class PythonObject { -public: - PythonObject() : m_py_obj(nullptr) {} +// Take a reference that you already own, and turn it into +// a PythonObject. +// +// Most python API methods will return a +1 reference +// if they succeed or NULL if and only if +// they set an exception. Use this to collect such return +// values, after checking for NULL. +// +// If T is not just PythonObject, then obj must be already be +// checked to be of the correct type. +template <typename T> T Take(PyObject *obj) { + assert(obj); + assert(!PyErr_Occurred()); + T thing(PyRefType::Owned, obj); + assert(thing.IsValid()); + return std::move(thing); +} + +// Retain a reference you have borrowed, and turn it into +// a PythonObject. +// +// A minority of python APIs return a borrowed reference +// instead of a +1. They will also return NULL if and only +// if they set an exception. Use this to collect such return +// values, after checking for NULL. +// +// If T is not just PythonObject, then obj must be already be +// checked to be of the correct type. +template <typename T> T Retain(PyObject *obj) { + assert(obj); + assert(!PyErr_Occurred()); + T thing(PyRefType::Borrowed, obj); + assert(thing.IsValid()); + return std::move(thing); +} + +// This class can be used like a utility function to convert from +// a llvm-friendly Twine into a null-terminated const char *, +// which is the form python C APIs want their strings in. +// +// Example: +// const llvm::Twine &some_twine; +// PyFoo_Bar(x, y, z, NullTerminated(some_twine)); +// +// Why a class instead of a function? If the twine isn't already null +// terminated, it will need a temporary buffer to copy the string +// into. We need that buffer to stick around for the lifetime of the +// statement. +class NullTerminated { + const char *str; + llvm::SmallString<32> storage; - PythonObject(PyRefType type, PyObject *py_obj) : m_py_obj(nullptr) { - Reset(type, py_obj); +public: + NullTerminated(const llvm::Twine &twine) { + llvm::StringRef ref = twine.toNullTerminatedStringRef(storage); + str = ref.begin(); } + operator const char *() { return str; } +}; - PythonObject(const PythonObject &rhs) : m_py_obj(nullptr) { Reset(rhs); } +inline llvm::Error nullDeref() { + return llvm::createStringError(llvm::inconvertibleErrorCode(), + "A NULL PyObject* was dereferenced"); +} - virtual ~PythonObject() { Reset(); } +inline llvm::Error exception(const char *s = nullptr) { + return llvm::make_error<PythonException>(s); +} - void Reset() { - // Avoid calling the virtual method since it's not necessary - // to actually validate the type of the PyObject if we're - // just setting to null. - if (Py_IsInitialized()) - Py_XDECREF(m_py_obj); - m_py_obj = nullptr; - } +inline llvm::Error keyError() { + return llvm::createStringError(llvm::inconvertibleErrorCode(), + "key not in dict"); +} - void Reset(const PythonObject &rhs) { - // Avoid calling the virtual method if it's not necessary - // to actually validate the type of the PyObject. - if (!rhs.IsValid()) - Reset(); - else - Reset(PyRefType::Borrowed, rhs.m_py_obj); - } +enum class PyInitialValue { Invalid, Empty }; - // PythonObject is implicitly convertible to PyObject *, which will call the - // wrong overload. We want to explicitly disallow this, since a PyObject - // *always* owns its reference. Therefore the overload which takes a - // PyRefType doesn't make sense, and the copy constructor should be used. - void Reset(PyRefType type, const PythonObject &ref) = delete; +template <typename T, typename Enable = void> struct PythonFormat; - virtual void Reset(PyRefType type, PyObject *py_obj) { - if (py_obj == m_py_obj) - return; +template <> struct PythonFormat<unsigned long long> { + static constexpr char format = 'K'; + static auto get(unsigned long long value) { return value; } +}; - if (Py_IsInitialized()) - Py_XDECREF(m_py_obj); +template <> struct PythonFormat<long long> { + static constexpr char format = 'L'; + static auto get(long long value) { return value; } +}; - m_py_obj = py_obj; +template <> struct PythonFormat<PyObject *> { + static constexpr char format = 'O'; + static auto get(PyObject *value) { return value; } +}; + +template <typename T> +struct PythonFormat< + T, typename std::enable_if<std::is_base_of<PythonObject, T>::value>::type> { + static constexpr char format = 'O'; + static auto get(const T &value) { return value.get(); } +}; + +class PythonObject { +public: + PythonObject() : m_py_obj(nullptr) {} + PythonObject(PyRefType type, PyObject *py_obj) { + m_py_obj = py_obj; // If this is a borrowed reference, we need to convert it to // an owned reference by incrementing it. If it is an owned // reference (for example the caller allocated it with PyDict_New() // then we must *not* increment it. - if (Py_IsInitialized() && type == PyRefType::Borrowed) + if (m_py_obj && Py_IsInitialized() && type == PyRefType::Borrowed) Py_XINCREF(m_py_obj); } + PythonObject(const PythonObject &rhs) + : PythonObject(PyRefType::Borrowed, rhs.m_py_obj) {} + + PythonObject(PythonObject &&rhs) { + m_py_obj = rhs.m_py_obj; + rhs.m_py_obj = nullptr; + } + + ~PythonObject() { Reset(); } + + void Reset() { + if (m_py_obj && Py_IsInitialized()) + Py_DECREF(m_py_obj); + m_py_obj = nullptr; + } + void Dump() const { if (m_py_obj) _PyObject_Dump(m_py_obj); @@ -144,8 +262,9 @@ public: return result; } - PythonObject &operator=(const PythonObject &other) { - Reset(PyRefType::Borrowed, other.get()); + PythonObject &operator=(PythonObject other) { + Reset(); + m_py_obj = std::exchange(other.m_py_obj, nullptr); return *this; } @@ -174,11 +293,13 @@ public: PythonObject GetAttributeValue(llvm::StringRef attribute) const; - bool IsValid() const; + bool IsNone() const { return m_py_obj == Py_None; } + + bool IsValid() const { return m_py_obj != nullptr; } - bool IsAllocated() const; + bool IsAllocated() const { return IsValid() && !IsNone(); } - bool IsNone() const; + explicit operator bool() const { return IsValid() && !IsNone(); } template <typename T> T AsType() const { if (!T::Check(m_py_obj)) @@ -189,24 +310,125 @@ public: StructuredData::ObjectSP CreateStructuredObject() const; protected: + +#if PY_MAJOR_VERSION < 3 + // The python 2 API declares some arguments as char* that should + // be const char *, but it doesn't actually modify them. + static char *py2_const_cast(const char *s) { return const_cast<char *>(s); } +#else + static const char *py2_const_cast(const char *s) { return s; } +#endif + +public: + template <typename... T> + llvm::Expected<PythonObject> CallMethod(const char *name, + const T &... t) const { + const char format[] = {'(', PythonFormat<T>::format..., ')', 0}; + PyObject *obj = + PyObject_CallMethod(m_py_obj, py2_const_cast(name), + py2_const_cast(format), PythonFormat<T>::get(t)...); + if (!obj) + return exception(); + return python::Take<PythonObject>(obj); + } + + template <typename... T> + llvm::Expected<PythonObject> Call(const T &... t) const { + const char format[] = {'(', PythonFormat<T>::format..., ')', 0}; + PyObject *obj = PyObject_CallFunction(m_py_obj, py2_const_cast(format), + PythonFormat<T>::get(t)...); + if (!obj) + return exception(); + return python::Take<PythonObject>(obj); + } + + llvm::Expected<PythonObject> GetAttribute(const llvm::Twine &name) const { + if (!m_py_obj) + return nullDeref(); + PyObject *obj = PyObject_GetAttrString(m_py_obj, NullTerminated(name)); + if (!obj) + return exception(); + return python::Take<PythonObject>(obj); + } + + llvm::Expected<bool> IsTrue() { + if (!m_py_obj) + return nullDeref(); + int r = PyObject_IsTrue(m_py_obj); + if (r < 0) + return exception(); + return !!r; + } + + llvm::Expected<long long> AsLongLong() { + if (!m_py_obj) + return nullDeref(); + assert(!PyErr_Occurred()); + long long r = PyLong_AsLongLong(m_py_obj); + if (PyErr_Occurred()) + return exception(); + return r; + } + + llvm::Expected<bool> IsInstance(const PythonObject &cls) { + if (!m_py_obj || !cls.IsValid()) + return nullDeref(); + int r = PyObject_IsInstance(m_py_obj, cls.get()); + if (r < 0) + return exception(); + return !!r; + } + +protected: PyObject *m_py_obj; }; -class PythonBytes : public PythonObject { + +// This is why C++ needs monads. +template <typename T> llvm::Expected<T> As(llvm::Expected<PythonObject> &&obj) { + if (!obj) + return obj.takeError(); + if (!T::Check(obj.get().get())) + return llvm::createStringError(llvm::inconvertibleErrorCode(), + "type error"); + return T(PyRefType::Borrowed, std::move(obj.get().get())); +} + +template <> llvm::Expected<bool> As<bool>(llvm::Expected<PythonObject> &&obj); + +template <> +llvm::Expected<long long> As<long long>(llvm::Expected<PythonObject> &&obj); + +template <> +llvm::Expected<std::string> As<std::string>(llvm::Expected<PythonObject> &&obj); + + +template <class T> class TypedPythonObject : public PythonObject { public: - PythonBytes(); - explicit PythonBytes(llvm::ArrayRef<uint8_t> bytes); - PythonBytes(const uint8_t *bytes, size_t length); - PythonBytes(PyRefType type, PyObject *o); + // override to perform implicit type conversions on Reset + // This can be eliminated once we drop python 2 support. + static void Convert(PyRefType &type, PyObject *&py_obj) {} - ~PythonBytes() override; + TypedPythonObject(PyRefType type, PyObject *py_obj) { + if (!py_obj) + return; + T::Convert(type, py_obj); + if (T::Check(py_obj)) + PythonObject::operator=(PythonObject(type, py_obj)); + else if (type == PyRefType::Owned) + Py_DECREF(py_obj); + } - static bool Check(PyObject *py_obj); + TypedPythonObject() {} +}; - // Bring in the no-argument base class version - using PythonObject::Reset; +class PythonBytes : public TypedPythonObject<PythonBytes> { +public: + using TypedPythonObject::TypedPythonObject; + explicit PythonBytes(llvm::ArrayRef<uint8_t> bytes); + PythonBytes(const uint8_t *bytes, size_t length); - void Reset(PyRefType type, PyObject *py_obj) override; + static bool Check(PyObject *py_obj); llvm::ArrayRef<uint8_t> GetBytes() const; @@ -217,23 +439,15 @@ public: StructuredData::StringSP CreateStructuredString() const; }; -class PythonByteArray : public PythonObject { +class PythonByteArray : public TypedPythonObject<PythonByteArray> { public: - PythonByteArray(); + using TypedPythonObject::TypedPythonObject; explicit PythonByteArray(llvm::ArrayRef<uint8_t> bytes); PythonByteArray(const uint8_t *bytes, size_t length); - PythonByteArray(PyRefType type, PyObject *o); PythonByteArray(const PythonBytes &object); - ~PythonByteArray() override; - static bool Check(PyObject *py_obj); - // Bring in the no-argument base class version - using PythonObject::Reset; - - void Reset(PyRefType type, PyObject *py_obj) override; - llvm::ArrayRef<uint8_t> GetBytes() const; size_t GetSize() const; @@ -243,45 +457,39 @@ public: StructuredData::StringSP CreateStructuredString() const; }; -class PythonString : public PythonObject { +class PythonString : public TypedPythonObject<PythonString> { public: - PythonString(); - explicit PythonString(llvm::StringRef string); - explicit PythonString(const char *string); - PythonString(PyRefType type, PyObject *o); + using TypedPythonObject::TypedPythonObject; + static llvm::Expected<PythonString> FromUTF8(llvm::StringRef string); - ~PythonString() override; + PythonString() : TypedPythonObject() {} // MSVC requires this for some reason - static bool Check(PyObject *py_obj); + explicit PythonString(llvm::StringRef string); // safe, null on error - // Bring in the no-argument base class version - using PythonObject::Reset; + static bool Check(PyObject *py_obj); + static void Convert(PyRefType &type, PyObject *&py_obj); - void Reset(PyRefType type, PyObject *py_obj) override; + llvm::StringRef GetString() const; // safe, empty string on error - llvm::StringRef GetString() const; + llvm::Expected<llvm::StringRef> AsUTF8() const; size_t GetSize() const; - void SetString(llvm::StringRef string); + void SetString(llvm::StringRef string); // safe, null on error StructuredData::StringSP CreateStructuredString() const; }; -class PythonInteger : public PythonObject { +class PythonInteger : public TypedPythonObject<PythonInteger> { public: - PythonInteger(); - explicit PythonInteger(int64_t value); - PythonInteger(PyRefType type, PyObject *o); - - ~PythonInteger() override; + using TypedPythonObject::TypedPythonObject; - static bool Check(PyObject *py_obj); + PythonInteger() : TypedPythonObject() {} // MSVC requires this for some reason - // Bring in the no-argument base class version - using PythonObject::Reset; + explicit PythonInteger(int64_t value); - void Reset(PyRefType type, PyObject *py_obj) override; + static bool Check(PyObject *py_obj); + static void Convert(PyRefType &type, PyObject *&py_obj); int64_t GetInteger() const; @@ -290,21 +498,14 @@ public: StructuredData::IntegerSP CreateStructuredInteger() const; }; -class PythonBoolean : public PythonObject { +class PythonBoolean : public TypedPythonObject<PythonBoolean> { public: - PythonBoolean() = default; - explicit PythonBoolean(bool value); - PythonBoolean(PyRefType type, PyObject *o); + using TypedPythonObject::TypedPythonObject; - ~PythonBoolean() override = default; + explicit PythonBoolean(bool value); static bool Check(PyObject *py_obj); - // Bring in the no-argument base class version - using PythonObject::Reset; - - void Reset(PyRefType type, PyObject *py_obj) override; - bool GetValue() const; void SetValue(bool value); @@ -312,22 +513,17 @@ public: StructuredData::BooleanSP CreateStructuredBoolean() const; }; -class PythonList : public PythonObject { +class PythonList : public TypedPythonObject<PythonList> { public: - PythonList() {} + using TypedPythonObject::TypedPythonObject; + + PythonList() : TypedPythonObject() {} // MSVC requires this for some reason + explicit PythonList(PyInitialValue value); explicit PythonList(int list_size); - PythonList(PyRefType type, PyObject *o); - - ~PythonList() override; static bool Check(PyObject *py_obj); - // Bring in the no-argument base class version - using PythonObject::Reset; - - void Reset(PyRefType type, PyObject *py_obj) override; - uint32_t GetSize() const; PythonObject GetItemAtIndex(uint32_t index) const; @@ -339,24 +535,17 @@ public: StructuredData::ArraySP CreateStructuredArray() const; }; -class PythonTuple : public PythonObject { +class PythonTuple : public TypedPythonObject<PythonTuple> { public: - PythonTuple() {} + using TypedPythonObject::TypedPythonObject; + explicit PythonTuple(PyInitialValue value); explicit PythonTuple(int tuple_size); - PythonTuple(PyRefType type, PyObject *o); PythonTuple(std::initializer_list<PythonObject> objects); PythonTuple(std::initializer_list<PyObject *> objects); - ~PythonTuple() override; - static bool Check(PyObject *py_obj); - // Bring in the no-argument base class version - using PythonObject::Reset; - - void Reset(PyRefType type, PyObject *py_obj) override; - uint32_t GetSize() const; PythonObject GetItemAtIndex(uint32_t index) const; @@ -366,37 +555,35 @@ public: StructuredData::ArraySP CreateStructuredArray() const; }; -class PythonDictionary : public PythonObject { +class PythonDictionary : public TypedPythonObject<PythonDictionary> { public: - PythonDictionary() {} - explicit PythonDictionary(PyInitialValue value); - PythonDictionary(PyRefType type, PyObject *o); + using TypedPythonObject::TypedPythonObject; - ~PythonDictionary() override; - - static bool Check(PyObject *py_obj); + PythonDictionary() : TypedPythonObject() {} // MSVC requires this for some reason - // Bring in the no-argument base class version - using PythonObject::Reset; + explicit PythonDictionary(PyInitialValue value); - void Reset(PyRefType type, PyObject *py_obj) override; + static bool Check(PyObject *py_obj); uint32_t GetSize() const; PythonList GetKeys() const; - PythonObject GetItemForKey(const PythonObject &key) const; - void SetItemForKey(const PythonObject &key, const PythonObject &value); + PythonObject GetItemForKey(const PythonObject &key) const; // DEPRECATED + void SetItemForKey(const PythonObject &key, + const PythonObject &value); // DEPRECATED + + llvm::Expected<PythonObject> GetItem(const PythonObject &key) const; + llvm::Expected<PythonObject> GetItem(const llvm::Twine &key) const; + llvm::Error SetItem(const PythonObject &key, const PythonObject &value) const; + llvm::Error SetItem(const llvm::Twine &key, const PythonObject &value) const; StructuredData::DictionarySP CreateStructuredDictionary() const; }; -class PythonModule : public PythonObject { +class PythonModule : public TypedPythonObject<PythonModule> { public: - PythonModule(); - PythonModule(PyRefType type, PyObject *o); - - ~PythonModule() override; + using TypedPythonObject::TypedPythonObject; static bool Check(PyObject *py_obj); @@ -406,38 +593,57 @@ public: static PythonModule AddModule(llvm::StringRef module); - static PythonModule ImportModule(llvm::StringRef module); + // safe, returns invalid on error; + static PythonModule ImportModule(llvm::StringRef name) { + std::string s = name; + auto mod = Import(s.c_str()); + if (!mod) { + llvm::consumeError(mod.takeError()); + return PythonModule(); + } + return std::move(mod.get()); + } - // Bring in the no-argument base class version - using PythonObject::Reset; + static llvm::Expected<PythonModule> Import(const llvm::Twine &name); - void Reset(PyRefType type, PyObject *py_obj) override; + llvm::Expected<PythonObject> Get(const llvm::Twine &name); PythonDictionary GetDictionary() const; }; -class PythonCallable : public PythonObject { +class PythonCallable : public TypedPythonObject<PythonCallable> { public: + using TypedPythonObject::TypedPythonObject; + struct ArgInfo { - size_t count; - bool is_bound_method : 1; - bool has_varargs : 1; - bool has_kwargs : 1; + /* the largest number of positional arguments this callable + * can accept, or UNBOUNDED, ie UINT_MAX if it's a varargs + * function and can accept an arbitrary number */ + unsigned max_positional_args; + static constexpr unsigned UNBOUNDED = UINT_MAX; // FIXME c++17 inline + /* the number of positional arguments, including optional ones, + * and excluding varargs. If this is a bound method, then the + * count will still include a +1 for self. + * + * FIXME. That's crazy. This should be replaced with + * an accurate min and max for positional args. + */ + int count; + /* does the callable have positional varargs? */ + bool has_varargs : 1; // FIXME delete this }; - PythonCallable(); - PythonCallable(PyRefType type, PyObject *o); - - ~PythonCallable() override; - static bool Check(PyObject *py_obj); - // Bring in the no-argument base class version - using PythonObject::Reset; + llvm::Expected<ArgInfo> GetArgInfo() const; - void Reset(PyRefType type, PyObject *py_obj) override; + llvm::Expected<ArgInfo> GetInitArgInfo() const; - ArgInfo GetNumArguments() const; + ArgInfo GetNumArguments() const; // DEPRECATED + + // If the callable is a Py_Class, then find the number of arguments + // of the __init__ method. + ArgInfo GetNumInitArguments() const; // DEPRECATED PythonObject operator()(); @@ -451,27 +657,123 @@ public: } }; -class PythonFile : public PythonObject { +class PythonFile : public TypedPythonObject<PythonFile> { public: - PythonFile(); - PythonFile(File &file, const char *mode); - PythonFile(const char *path, const char *mode); - PythonFile(PyRefType type, PyObject *o); + using TypedPythonObject::TypedPythonObject; - ~PythonFile() override; + PythonFile() : TypedPythonObject() {} // MSVC requires this for some reason static bool Check(PyObject *py_obj); - using PythonObject::Reset; + static llvm::Expected<PythonFile> FromFile(File &file, + const char *mode = nullptr); - void Reset(PyRefType type, PyObject *py_obj) override; - void Reset(File &file, const char *mode); + llvm::Expected<lldb::FileSP> ConvertToFile(bool borrowed = false); + llvm::Expected<lldb::FileSP> + ConvertToFileForcingUseOfScriptingIOMethods(bool borrowed = false); +}; - static uint32_t GetOptionsFromMode(llvm::StringRef mode); +class PythonException : public llvm::ErrorInfo<PythonException> { +private: + PyObject *m_exception_type, *m_exception, *m_traceback; + PyObject *m_repr_bytes; - bool GetUnderlyingFile(File &file) const; +public: + static char ID; + const char *toCString() const; + PythonException(const char *caller = nullptr); + void Restore(); + ~PythonException(); + void log(llvm::raw_ostream &OS) const override; + std::error_code convertToErrorCode() const override; + bool Matches(PyObject *exc) const; + std::string ReadBacktrace() const; +}; + +// This extracts the underlying T out of an Expected<T> and returns it. +// If the Expected is an Error instead of a T, that error will be converted +// into a python exception, and this will return a default-constructed T. +// +// This is appropriate for use right at the boundary of python calling into +// C++, such as in a SWIG typemap. In such a context you should simply +// check if the returned T is valid, and if it is, return a NULL back +// to python. This will result in the Error being raised as an exception +// from python code's point of view. +// +// For example: +// ``` +// Expected<Foo *> efoop = some_cpp_function(); +// Foo *foop = unwrapOrSetPythonException(efoop); +// if (!foop) +// return NULL; +// do_something(*foop); +// +// If the Error returned was itself created because a python exception was +// raised when C++ code called into python, then the original exception +// will be restored. Otherwise a simple string exception will be raised. +template <typename T> T unwrapOrSetPythonException(llvm::Expected<T> expected) { + if (expected) + return expected.get(); + llvm::handleAllErrors( + expected.takeError(), [](PythonException &E) { E.Restore(); }, + [](const llvm::ErrorInfoBase &E) { + PyErr_SetString(PyExc_Exception, E.message().c_str()); + }); + return T(); +} + +// This is only here to help incrementally migrate old, exception-unsafe +// code. +template <typename T> T unwrapIgnoringErrors(llvm::Expected<T> expected) { + if (expected) + return std::move(expected.get()); + llvm::consumeError(expected.takeError()); + return T(); +} + +llvm::Expected<PythonObject> runStringOneLine(const llvm::Twine &string, + const PythonDictionary &globals, + const PythonDictionary &locals); + +llvm::Expected<PythonObject> runStringMultiLine(const llvm::Twine &string, + const PythonDictionary &globals, + const PythonDictionary &locals); + +// Sometimes the best way to interact with a python interpreter is +// to run some python code. You construct a PythonScript with +// script string. The script assigns some function to `_function_` +// and you get a C++ callable object that calls the python function. +// +// Example: +// +// const char script[] = R"( +// def main(x, y): +// .... +// )"; +// +// Expected<PythonObject> cpp_foo_wrapper(PythonObject x, PythonObject y) { +// // no need to synchronize access to this global, we already have the GIL +// static PythonScript foo(script) +// return foo(x, y); +// } +class PythonScript { + const char *script; + PythonCallable function; + + llvm::Error Init(); + +public: + PythonScript(const char *script) : script(script), function() {} + + template <typename... Args> + llvm::Expected<PythonObject> operator()(Args &&... args) { + if (llvm::Error error = Init()) + return std::move(error); + return function.Call(std::forward<Args>(args)...); + } }; +} // namespace python } // namespace lldb_private #endif |