| //===-- PythonDataObjects.h--------------------------------------*- C++ -*-===// | |
| // | |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | |
| // See https://llvm.org/LICENSE.txt for license information. | |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | |
| // | |
| //===----------------------------------------------------------------------===// | |
| // | |
| // !! 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. | |
| // | |
| // * Eliminate 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_SOURCE_PLUGINS_SCRIPTINTERPRETER_PYTHON_PYTHONDATAOBJECTS_H | |
| #define LLDB_SOURCE_PLUGINS_SCRIPTINTERPRETER_PYTHON_PYTHONDATAOBJECTS_H | |
| #include "lldb-python.h" | |
| #include "lldb/Host/File.h" | |
| #include "lldb/Utility/StructuredData.h" | |
| #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 GIL { | |
| public: | |
| GIL() { | |
| m_state = PyGILState_Ensure(); | |
| assert(!PyErr_Occurred()); | |
| } | |
| ~GIL() { PyGILState_Release(m_state); } | |
| protected: | |
| PyGILState_STATE m_state; | |
| }; | |
| enum class PyObjectType { | |
| Unknown, | |
| None, | |
| Boolean, | |
| Integer, | |
| Dictionary, | |
| List, | |
| String, | |
| Bytes, | |
| ByteArray, | |
| Module, | |
| Callable, | |
| Tuple, | |
| File | |
| }; | |
| enum class PyRefType { | |
| Borrowed, // We are not given ownership of the incoming PyObject. | |
| // We cannot safely hold it without calling Py_INCREF. | |
| Owned // We have ownership of the incoming PyObject. We should | |
| // not call Py_INCREF. | |
| }; | |
| // 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 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 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; | |
| public: | |
| NullTerminated(const llvm::Twine &twine) { | |
| llvm::StringRef ref = twine.toNullTerminatedStringRef(storage); | |
| str = ref.begin(); | |
| } | |
| operator const char *() { return str; } | |
| }; | |
| inline llvm::Error nullDeref() { | |
| return llvm::createStringError(llvm::inconvertibleErrorCode(), | |
| "A NULL PyObject* was dereferenced"); | |
| } | |
| inline llvm::Error exception(const char *s = nullptr) { | |
| return llvm::make_error<PythonException>(s); | |
| } | |
| inline llvm::Error keyError() { | |
| return llvm::createStringError(llvm::inconvertibleErrorCode(), | |
| "key not in dict"); | |
| } | |
| inline const char *py2_const_cast(const char *s) { return s; } | |
| enum class PyInitialValue { Invalid, Empty }; | |
| // DOC: https://docs.python.org/3/c-api/arg.html#building-values | |
| template <typename T, typename Enable = void> struct PythonFormat; | |
| template <typename T, char F> struct PassthroughFormat { | |
| static constexpr char format = F; | |
| static constexpr T get(T t) { return t; } | |
| }; | |
| template <> struct PythonFormat<char *> : PassthroughFormat<char *, 's'> {}; | |
| template <> | |
| struct PythonFormat<const char *> : PassthroughFormat<const char *, 's'> {}; | |
| template <> struct PythonFormat<char> : PassthroughFormat<char, 'b'> {}; | |
| template <> | |
| struct PythonFormat<unsigned char> : PassthroughFormat<unsigned char, 'B'> {}; | |
| template <> struct PythonFormat<short> : PassthroughFormat<short, 'h'> {}; | |
| template <> | |
| struct PythonFormat<unsigned short> : PassthroughFormat<unsigned short, 'H'> {}; | |
| template <> struct PythonFormat<int> : PassthroughFormat<int, 'i'> {}; | |
| template <> struct PythonFormat<bool> : PassthroughFormat<bool, 'p'> {}; | |
| template <> | |
| struct PythonFormat<unsigned int> : PassthroughFormat<unsigned int, 'I'> {}; | |
| template <> struct PythonFormat<long> : PassthroughFormat<long, 'l'> {}; | |
| template <> | |
| struct PythonFormat<unsigned long> : PassthroughFormat<unsigned long, 'k'> {}; | |
| template <> | |
| struct PythonFormat<long long> : PassthroughFormat<long long, 'L'> {}; | |
| template <> | |
| struct PythonFormat<unsigned long long> | |
| : PassthroughFormat<unsigned long long, 'K'> {}; | |
| template <> | |
| struct PythonFormat<PyObject *> : PassthroughFormat<PyObject *, 'O'> {}; | |
| 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() = default; | |
| 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 (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(); | |
| void Dump(Stream &strm) const; | |
| PyObject *get() const { return m_py_obj; } | |
| PyObject *release() { | |
| PyObject *result = m_py_obj; | |
| m_py_obj = nullptr; | |
| return result; | |
| } | |
| PythonObject &operator=(PythonObject other) { | |
| Reset(); | |
| m_py_obj = std::exchange(other.m_py_obj, nullptr); | |
| return *this; | |
| } | |
| PyObjectType GetObjectType() const; | |
| PythonString Repr() const; | |
| PythonString Str() const; | |
| static PythonObject ResolveNameWithDictionary(llvm::StringRef name, | |
| const PythonDictionary &dict); | |
| template <typename T> | |
| static T ResolveNameWithDictionary(llvm::StringRef name, | |
| const PythonDictionary &dict) { | |
| return ResolveNameWithDictionary(name, dict).AsType<T>(); | |
| } | |
| PythonObject ResolveName(llvm::StringRef name) const; | |
| template <typename T> T ResolveName(llvm::StringRef name) const { | |
| return ResolveName(name).AsType<T>(); | |
| } | |
| bool HasAttribute(llvm::StringRef attribute) const; | |
| PythonObject GetAttributeValue(llvm::StringRef attribute) const; | |
| bool IsNone() const { return m_py_obj == Py_None; } | |
| bool IsValid() const { return m_py_obj != nullptr; } | |
| bool IsAllocated() const { return IsValid() && !IsNone(); } | |
| explicit operator bool() const { return IsValid() && !IsNone(); } | |
| template <typename T> T AsType() const { | |
| if (!T::Check(m_py_obj)) | |
| return T(); | |
| return T(PyRefType::Borrowed, m_py_obj); | |
| } | |
| StructuredData::ObjectSP CreateStructuredObject() const; | |
| 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<PythonObject> GetType() const { | |
| if (!m_py_obj) | |
| return nullDeref(); | |
| PyObject *obj = PyObject_Type(m_py_obj); | |
| 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() const; | |
| llvm::Expected<unsigned long long> AsUnsignedLongLong() const; | |
| // wraps on overflow, instead of raising an error. | |
| llvm::Expected<unsigned long long> AsModuloUnsignedLongLong() const; | |
| 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 = nullptr; | |
| }; | |
| // 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<unsigned long long> | |
| As<unsigned 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: | |
| TypedPythonObject(PyRefType type, PyObject *py_obj) { | |
| if (!py_obj) | |
| return; | |
| if (T::Check(py_obj)) | |
| PythonObject::operator=(PythonObject(type, py_obj)); | |
| else if (type == PyRefType::Owned) | |
| Py_DECREF(py_obj); | |
| } | |
| TypedPythonObject() = default; | |
| }; | |
| class PythonBytes : public TypedPythonObject<PythonBytes> { | |
| public: | |
| using TypedPythonObject::TypedPythonObject; | |
| explicit PythonBytes(llvm::ArrayRef<uint8_t> bytes); | |
| PythonBytes(const uint8_t *bytes, size_t length); | |
| static bool Check(PyObject *py_obj); | |
| llvm::ArrayRef<uint8_t> GetBytes() const; | |
| size_t GetSize() const; | |
| void SetBytes(llvm::ArrayRef<uint8_t> stringbytes); | |
| StructuredData::StringSP CreateStructuredString() const; | |
| }; | |
| class PythonByteArray : public TypedPythonObject<PythonByteArray> { | |
| public: | |
| using TypedPythonObject::TypedPythonObject; | |
| explicit PythonByteArray(llvm::ArrayRef<uint8_t> bytes); | |
| PythonByteArray(const uint8_t *bytes, size_t length); | |
| PythonByteArray(const PythonBytes &object); | |
| static bool Check(PyObject *py_obj); | |
| llvm::ArrayRef<uint8_t> GetBytes() const; | |
| size_t GetSize() const; | |
| void SetBytes(llvm::ArrayRef<uint8_t> stringbytes); | |
| StructuredData::StringSP CreateStructuredString() const; | |
| }; | |
| class PythonString : public TypedPythonObject<PythonString> { | |
| public: | |
| using TypedPythonObject::TypedPythonObject; | |
| static llvm::Expected<PythonString> FromUTF8(llvm::StringRef string); | |
| PythonString() : TypedPythonObject() {} // MSVC requires this for some reason | |
| explicit PythonString(llvm::StringRef string); // safe, null on error | |
| static bool Check(PyObject *py_obj); | |
| llvm::StringRef GetString() const; // safe, empty string on error | |
| llvm::Expected<llvm::StringRef> AsUTF8() const; | |
| size_t GetSize() const; | |
| void SetString(llvm::StringRef string); // safe, null on error | |
| StructuredData::StringSP CreateStructuredString() const; | |
| }; | |
| class PythonInteger : public TypedPythonObject<PythonInteger> { | |
| public: | |
| using TypedPythonObject::TypedPythonObject; | |
| PythonInteger() : TypedPythonObject() {} // MSVC requires this for some reason | |
| explicit PythonInteger(int64_t value); | |
| static bool Check(PyObject *py_obj); | |
| void SetInteger(int64_t value); | |
| StructuredData::IntegerSP CreateStructuredInteger() const; | |
| StructuredData::UnsignedIntegerSP CreateStructuredUnsignedInteger() const; | |
| StructuredData::SignedIntegerSP CreateStructuredSignedInteger() const; | |
| }; | |
| class PythonBoolean : public TypedPythonObject<PythonBoolean> { | |
| public: | |
| using TypedPythonObject::TypedPythonObject; | |
| explicit PythonBoolean(bool value); | |
| static bool Check(PyObject *py_obj); | |
| bool GetValue() const; | |
| void SetValue(bool value); | |
| StructuredData::BooleanSP CreateStructuredBoolean() const; | |
| }; | |
| class PythonList : public TypedPythonObject<PythonList> { | |
| public: | |
| using TypedPythonObject::TypedPythonObject; | |
| PythonList() : TypedPythonObject() {} // MSVC requires this for some reason | |
| explicit PythonList(PyInitialValue value); | |
| explicit PythonList(int list_size); | |
| static bool Check(PyObject *py_obj); | |
| uint32_t GetSize() const; | |
| PythonObject GetItemAtIndex(uint32_t index) const; | |
| void SetItemAtIndex(uint32_t index, const PythonObject &object); | |
| void AppendItem(const PythonObject &object); | |
| StructuredData::ArraySP CreateStructuredArray() const; | |
| }; | |
| class PythonTuple : public TypedPythonObject<PythonTuple> { | |
| public: | |
| using TypedPythonObject::TypedPythonObject; | |
| explicit PythonTuple(PyInitialValue value); | |
| explicit PythonTuple(int tuple_size); | |
| PythonTuple(std::initializer_list<PythonObject> objects); | |
| PythonTuple(std::initializer_list<PyObject *> objects); | |
| static bool Check(PyObject *py_obj); | |
| uint32_t GetSize() const; | |
| PythonObject GetItemAtIndex(uint32_t index) const; | |
| void SetItemAtIndex(uint32_t index, const PythonObject &object); | |
| StructuredData::ArraySP CreateStructuredArray() const; | |
| }; | |
| class PythonDictionary : public TypedPythonObject<PythonDictionary> { | |
| public: | |
| using TypedPythonObject::TypedPythonObject; | |
| PythonDictionary() : TypedPythonObject() {} // MSVC requires this for some reason | |
| explicit PythonDictionary(PyInitialValue value); | |
| static bool Check(PyObject *py_obj); | |
| bool HasKey(const llvm::Twine &key) const; | |
| uint32_t GetSize() const; | |
| PythonList GetKeys() const; | |
| 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 TypedPythonObject<PythonModule> { | |
| public: | |
| using TypedPythonObject::TypedPythonObject; | |
| static bool Check(PyObject *py_obj); | |
| static PythonModule BuiltinsModule(); | |
| static PythonModule MainModule(); | |
| static PythonModule AddModule(llvm::StringRef module); | |
| // safe, returns invalid on error; | |
| static PythonModule ImportModule(llvm::StringRef name) { | |
| std::string s = std::string(name); | |
| auto mod = Import(s.c_str()); | |
| if (!mod) { | |
| llvm::consumeError(mod.takeError()); | |
| return PythonModule(); | |
| } | |
| return std::move(mod.get()); | |
| } | |
| static llvm::Expected<PythonModule> Import(const llvm::Twine &name); | |
| llvm::Expected<PythonObject> Get(const llvm::Twine &name); | |
| PythonDictionary GetDictionary() const; | |
| }; | |
| class PythonCallable : public TypedPythonObject<PythonCallable> { | |
| public: | |
| using TypedPythonObject::TypedPythonObject; | |
| struct ArgInfo { | |
| /* 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 | |
| }; | |
| static bool Check(PyObject *py_obj); | |
| llvm::Expected<ArgInfo> GetArgInfo() const; | |
| // Always derives ArgInfo via a Python-level inspect.signature() call, | |
| // regardless of whether the callable's argument count/varargs bit could | |
| // have been read directly off its data attributes. GetArgInfo() prefers | |
| // the cheaper attribute-based path and only falls back to this for | |
| // callables it can't introspect that way (e.g. builtins); exposed | |
| // separately so that fallback behavior can be tested directly. | |
| static llvm::Expected<ArgInfo> | |
| GetArgInfoFromInspectSignature(const PythonCallable &callable); | |
| PythonObject operator()(); | |
| PythonObject operator()(std::initializer_list<PyObject *> args); | |
| PythonObject operator()(std::initializer_list<PythonObject> args); | |
| template <typename Arg, typename... Args> | |
| PythonObject operator()(const Arg &arg, Args... args) { | |
| return operator()({arg, args...}); | |
| } | |
| }; | |
| class PythonFile : public TypedPythonObject<PythonFile> { | |
| public: | |
| using TypedPythonObject::TypedPythonObject; | |
| PythonFile() : TypedPythonObject() {} // MSVC requires this for some reason | |
| static bool Check(PyObject *py_obj); | |
| static int TranslateFdToPython(int our_fd); | |
| static int TranslateFdFromPython(int their_fd); | |
| static llvm::Expected<PythonFile> FromFile(File &file, | |
| const char *mode = nullptr); | |
| llvm::Expected<lldb::FileSP> ConvertToFile(bool borrowed = false); | |
| llvm::Expected<lldb::FileSP> | |
| ConvertToFileForcingUseOfScriptingIOMethods(bool borrowed = false); | |
| }; | |
| class PythonException : public llvm::ErrorInfo<PythonException> { | |
| private: | |
| PyObject *m_exception_type, *m_exception, *m_traceback; | |
| PyObject *m_repr_bytes; | |
| public: | |
| static char ID; | |
| const char *toCString() const; | |
| PythonException(const char *caller = nullptr); | |
| void Restore(); | |
| ~PythonException() override; | |
| 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)...); | |
| } | |
| }; | |
| class StructuredPythonObject : public StructuredData::Generic { | |
| public: | |
| StructuredPythonObject() : StructuredData::Generic() {} | |
| // Take ownership of the object we received. | |
| StructuredPythonObject(PythonObject obj) | |
| : StructuredData::Generic(obj.release()) {} | |
| ~StructuredPythonObject() override { | |
| // Hand ownership back to a (temporary) PythonObject instance and let it | |
| // take care of releasing it. | |
| PythonObject(PyRefType::Owned, static_cast<PyObject *>(GetValue())); | |
| } | |
| bool IsValid() const override { return GetValue() && GetValue() != Py_None; } | |
| void Serialize(llvm::json::OStream &s) const override; | |
| private: | |
| StructuredPythonObject(const StructuredPythonObject &) = delete; | |
| const StructuredPythonObject & | |
| operator=(const StructuredPythonObject &) = delete; | |
| }; | |
| PyObject *RunString(const char *str, int start, PyObject *globals, | |
| PyObject *locals); | |
| int RunSimpleString(const char *str); | |
| } // namespace python | |
| } // namespace lldb_private | |
| #endif // LLDB_SOURCE_PLUGINS_SCRIPTINTERPRETER_PYTHON_PYTHONDATAOBJECTS_H |