summaryrefslogtreecommitdiffstats
path: root/lldb/source/Plugins/ScriptInterpreter/Python/PythonDataObjects.cpp
diff options
context:
space:
mode:
authorJason Molenda <jmolenda@apple.com>2017-10-17 03:03:44 +0000
committerJason Molenda <jmolenda@apple.com>2017-10-17 03:03:44 +0000
commit695a1f6e6cf7f164b71c5ee9f1756374547114c9 (patch)
tree9621ad0c8eb1af0a097801896bbc34dc7433bdba /lldb/source/Plugins/ScriptInterpreter/Python/PythonDataObjects.cpp
parent45623bd06dfc763241b82be152d0d01d7bb5cf32 (diff)
downloadbcm5719-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.cpp139
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
OpenPOWER on IntegriCloud