diff options
author | Jason Molenda <jmolenda@apple.com> | 2017-10-17 03:03:44 +0000 |
---|---|---|
committer | Jason Molenda <jmolenda@apple.com> | 2017-10-17 03:03:44 +0000 |
commit | 695a1f6e6cf7f164b71c5ee9f1756374547114c9 (patch) | |
tree | 9621ad0c8eb1af0a097801896bbc34dc7433bdba /lldb/source/Plugins/ScriptInterpreter/Python/PythonDataObjects.cpp | |
parent | 45623bd06dfc763241b82be152d0d01d7bb5cf32 (diff) | |
download | bcm5719-llvm-695a1f6e6cf7f164b71c5ee9f1756374547114c9.tar.gz bcm5719-llvm-695a1f6e6cf7f164b71c5ee9f1756374547114c9.zip |
Committing this for Larry D'Anna:
This patch adds support for passing an arbitrary python stream
(anything inheriting from IOBase) to SetOutputFileHandle or
SetErrorFileHandle.
Differential revision: https://reviews.llvm.org/D38829
<rdar://problem/34870417>
llvm-svn: 315966
Diffstat (limited to 'lldb/source/Plugins/ScriptInterpreter/Python/PythonDataObjects.cpp')
-rw-r--r-- | lldb/source/Plugins/ScriptInterpreter/Python/PythonDataObjects.cpp | 139 |
1 files changed, 128 insertions, 11 deletions
diff --git a/lldb/source/Plugins/ScriptInterpreter/Python/PythonDataObjects.cpp b/lldb/source/Plugins/ScriptInterpreter/Python/PythonDataObjects.cpp index 966bdff3ad9..369f745339c 100644 --- a/lldb/source/Plugins/ScriptInterpreter/Python/PythonDataObjects.cpp +++ b/lldb/source/Plugins/ScriptInterpreter/Python/PythonDataObjects.cpp @@ -17,6 +17,7 @@ #include "PythonDataObjects.h" #include "ScriptInterpreterPython.h" +#include "lldb/Utility/Log.h" #include "lldb/Host/File.h" #include "lldb/Host/FileSystem.h" #include "lldb/Interpreter/ScriptInterpreter.h" @@ -959,8 +960,11 @@ PythonFile::~PythonFile() {} bool PythonFile::Check(PyObject *py_obj) { #if PY_MAJOR_VERSION < 3 - return PyFile_Check(py_obj); -#else + bool is_ordinary_file = PyFile_Check(py_obj); + if (is_ordinary_file) { + return true; + } +#endif // In Python 3, there is no `PyFile_Check`, and in fact PyFile is not even a // first-class object type anymore. `PyFile_FromFd` is just a thin wrapper // over `io.open()`, which returns some object derived from `io.IOBase`. @@ -981,7 +985,6 @@ bool PythonFile::Check(PyObject *py_obj) { return false; return true; -#endif } void PythonFile::Reset(PyRefType type, PyObject *py_obj) { @@ -989,7 +992,7 @@ void PythonFile::Reset(PyRefType type, PyObject *py_obj) { // `py_obj` it still gets decremented if necessary. PythonObject result(type, py_obj); - if (!PythonFile::Check(py_obj)) { + if (py_obj == NULL || !PythonFile::Check(py_obj)) { PythonObject::Reset(); return; } @@ -1034,17 +1037,131 @@ uint32_t PythonFile::GetOptionsFromMode(llvm::StringRef mode) { .Default(0); } +static void +log_exception(const char *fmt, PyObject *obj) +{ + Log *log = GetLogIfAllCategoriesSet(LIBLLDB_LOG_SCRIPT); + if (!log) { + return; + } + const char *classname = "unknown class"; + PyObject *pyclass = PyObject_Type(obj); + if (pyclass) { + PyObject *s = PyObject_GetAttrString(pyclass, "__name__"); + if (s) { + classname = PyString_AsString(s); + Py_XDECREF(s); + } + Py_XDECREF(pyclass); + } + const char *error = "unknown error"; + PyObject *exception, *v, *tb; + PyErr_Fetch(&exception, &v, &tb); + if (exception) { + PyErr_NormalizeException(&exception, &v, &tb); + } + PyObject *string = NULL; + if (v) { + string = PyObject_Str(v); + } + error = PyString_AsString(string); + log->Printf(fmt, classname, error); + Py_XDECREF(exception); + Py_XDECREF(v); + Py_XDECREF(tb); + Py_XDECREF(string); +} + +static int readfn(void *ctx, char *buffer, int n) +{ + auto state = PyGILState_Ensure(); + auto *file = (PyObject *) ctx; + int result = -1; + auto pybuffer = PyBuffer_FromMemory(buffer, n); + PyObject *pyresult = NULL; + if (!pybuffer) { + goto fail; + } + pyresult = PyEval_CallMethod(file, "read", "(i)", n); + if (pyresult == NULL || !PyInt_Check(pyresult)) { + log_exception("read from python %s failed: %s", file); + goto fail; + } + result = _PyInt_AsInt(pyresult); +fail: + Py_XDECREF(pybuffer); + Py_XDECREF(pyresult); + PyGILState_Release(state); + return result; +} + +static int writefn(void *ctx, const char *buffer, int n) +{ + auto state = PyGILState_Ensure(); + auto *file = (PyObject *) ctx; + int result = -1; + auto pyresult = PyEval_CallMethod(file, "write", "(s#)", buffer, n); + if (pyresult == NULL || !PyInt_Check(pyresult)) { + log_exception("write to python %s failed: %s", file); + goto fail; + } + result = _PyInt_AsInt(pyresult); +fail: + Py_XDECREF(pyresult); + PyGILState_Release(state); + return result; +} + + +static int closefn(void *ctx) { + auto *file = (PyObject *) ctx; + auto state = PyGILState_Ensure(); + Py_XDECREF(file); + PyGILState_Release(state); + return 0; +} + + bool PythonFile::GetUnderlyingFile(File &file) const { if (!IsValid()) return false; - + file.Close(); - // We don't own the file descriptor returned by this function, make sure the - // File object knows about that. - file.SetDescriptor(PyObject_AsFileDescriptor(m_py_obj), false); - PythonString py_mode = GetAttributeValue("mode").AsType<PythonString>(); - file.SetOptions(PythonFile::GetOptionsFromMode(py_mode.GetString())); - return file.IsValid(); + + int fd = PyObject_AsFileDescriptor(m_py_obj); + if (fd >= 0) { + // We don't own the file descriptor returned by this function, make sure the + // File object knows about that. + file.SetDescriptor(PyObject_AsFileDescriptor(m_py_obj), false); + PythonString py_mode = GetAttributeValue("mode").AsType<PythonString>(); + file.SetOptions(PythonFile::GetOptionsFromMode(py_mode.GetString())); + return file.IsValid(); + } + + PythonObject io_module(PyRefType::Owned, PyImport_ImportModule("io")); + PythonDictionary io_dict(PyRefType::Borrowed, + PyModule_GetDict(io_module.get())); + PythonObject io_base_class = io_dict.GetItemForKey(PythonString("IOBase")); + + PythonObject object_type(PyRefType::Owned, PyObject_Type(m_py_obj)); + + if (1 != PyObject_IsSubclass(object_type.get(), io_base_class.get())) + return false; + + PyObject *r = PyEval_CallMethod(m_py_obj, "readable", "()"); + bool readable = PyObject_IsTrue(r); + + r = PyEval_CallMethod(m_py_obj, "writable", "()"); + bool writable = PyObject_IsTrue(r); + + Py_XINCREF(m_py_obj); + file = File(m_py_obj, readable ? readfn : NULL, writable ? writefn : NULL, closefn); + if (!file.IsValid()) { + closefn(m_py_obj); + return false; + } else { + return true; + } } #endif |