forked from OSchip/llvm-project
Commit Lawrence D'Anna's patch to change
SetOututFileHandle to work with IOBase. I did make one change after checking with Larry -- I renamed SBDebugger::Flush to FlushDebuggerOutputHandles and added a short docstring to the .i file to make it a little clearer under which context programs may need to use this API. Differential Revision: https://reviews.llvm.org/D39128 <rdar://problem/34870417> llvm-svn: 317182
This commit is contained in:
parent
9e27b70a07
commit
edc2def4a6
|
@ -78,6 +78,8 @@ public:
|
|||
void SetOutputFileHandle(FILE *f, bool transfer_ownership);
|
||||
|
||||
void SetErrorFileHandle(FILE *f, bool transfer_ownership);
|
||||
|
||||
void FlushDebuggerOutputHandles();
|
||||
|
||||
FILE *GetInputFileHandle();
|
||||
|
||||
|
|
|
@ -139,6 +139,8 @@ public:
|
|||
void SetOutputFileHandle(FILE *fh, bool tranfer_ownership);
|
||||
|
||||
void SetErrorFileHandle(FILE *fh, bool tranfer_ownership);
|
||||
|
||||
void FlushDebuggerOutputHandles();
|
||||
|
||||
void SaveInputTerminalState();
|
||||
|
||||
|
|
|
@ -62,6 +62,17 @@ public:
|
|||
m_is_interactive(eLazyBoolCalculate),
|
||||
m_is_real_terminal(eLazyBoolCalculate) {}
|
||||
|
||||
File(File &&rhs);
|
||||
|
||||
File& operator= (File &&rhs);
|
||||
|
||||
void Swap(File &other);
|
||||
|
||||
File(void *cookie,
|
||||
int (*readfn)(void *, char *, int),
|
||||
int (*writefn)(void *, const char *, int),
|
||||
int (*closefn)(void *));
|
||||
|
||||
//------------------------------------------------------------------
|
||||
/// Constructor with path.
|
||||
///
|
||||
|
@ -479,9 +490,6 @@ protected:
|
|||
LazyBool m_is_interactive;
|
||||
LazyBool m_is_real_terminal;
|
||||
LazyBool m_supports_colors;
|
||||
|
||||
private:
|
||||
DISALLOW_COPY_AND_ASSIGN(File);
|
||||
};
|
||||
|
||||
} // namespace lldb_private
|
||||
|
|
|
@ -0,0 +1,228 @@
|
|||
"""
|
||||
Test lldb Python API for setting output and error file handles
|
||||
"""
|
||||
|
||||
from __future__ import print_function
|
||||
|
||||
|
||||
import contextlib
|
||||
import os
|
||||
import io
|
||||
import re
|
||||
import platform
|
||||
import unittest
|
||||
|
||||
import lldb
|
||||
from lldbsuite.test.decorators import *
|
||||
from lldbsuite.test.lldbtest import *
|
||||
from lldbsuite.test import lldbutil
|
||||
|
||||
|
||||
class StringIO(io.TextIOBase):
|
||||
|
||||
def __init__(self, buf=''):
|
||||
self.buf = buf
|
||||
|
||||
def writable(self):
|
||||
return True
|
||||
|
||||
def write(self, s):
|
||||
self.buf += s
|
||||
return len(s)
|
||||
|
||||
|
||||
class BadIO(io.TextIOBase):
|
||||
|
||||
def writable(self):
|
||||
return True
|
||||
|
||||
def write(self, s):
|
||||
raise Exception('OH NOE')
|
||||
|
||||
|
||||
@contextlib.contextmanager
|
||||
def replace_stdout(new):
|
||||
old = sys.stdout
|
||||
sys.stdout = new
|
||||
try:
|
||||
yield
|
||||
finally:
|
||||
sys.stdout = old
|
||||
|
||||
|
||||
def handle_command(debugger, cmd, raise_on_fail=True, collect_result=True):
|
||||
|
||||
ret = lldb.SBCommandReturnObject()
|
||||
|
||||
if collect_result:
|
||||
interpreter = debugger.GetCommandInterpreter()
|
||||
interpreter.HandleCommand(cmd, ret)
|
||||
else:
|
||||
debugger.HandleCommand(cmd)
|
||||
|
||||
if hasattr(debugger, 'FlushDebuggerOutputHandles'):
|
||||
debugger.FlushDebuggerOutputHandles()
|
||||
|
||||
if collect_result and raise_on_fail and not ret.Succeeded():
|
||||
raise Exception
|
||||
|
||||
return ret.GetOutput()
|
||||
|
||||
|
||||
|
||||
class FileHandleTestCase(TestBase):
|
||||
|
||||
mydir = TestBase.compute_mydir(__file__)
|
||||
|
||||
def comment(self, *args):
|
||||
if self.session is not None:
|
||||
print(*args, file=self.session)
|
||||
|
||||
def skip_windows(self):
|
||||
if platform.system() == 'Windows':
|
||||
self.skipTest('windows')
|
||||
|
||||
|
||||
@add_test_categories(['pyapi'])
|
||||
@no_debug_info_test
|
||||
def test_file_out(self):
|
||||
|
||||
debugger = lldb.SBDebugger.Create()
|
||||
try:
|
||||
with open('output', 'w') as f:
|
||||
debugger.SetOutputFileHandle(f, False)
|
||||
handle_command(debugger, 'script print("foobar")')
|
||||
|
||||
with open('output', 'r') as f:
|
||||
self.assertEqual(f.read().strip(), "foobar")
|
||||
|
||||
finally:
|
||||
self.RemoveTempFile('output')
|
||||
lldb.SBDebugger.Destroy(debugger)
|
||||
|
||||
|
||||
@add_test_categories(['pyapi'])
|
||||
@no_debug_info_test
|
||||
def test_file_error(self):
|
||||
|
||||
debugger = lldb.SBDebugger.Create()
|
||||
try:
|
||||
with open('output', 'w') as f:
|
||||
debugger.SetErrorFileHandle(f, False)
|
||||
handle_command(debugger, 'lolwut', raise_on_fail=False, collect_result=False)
|
||||
|
||||
with open('output', 'r') as f:
|
||||
errors = f.read()
|
||||
self.assertTrue(re.search(r'error:.*lolwut', errors))
|
||||
|
||||
finally:
|
||||
self.RemoveTempFile('output')
|
||||
lldb.SBDebugger.Destroy(debugger)
|
||||
|
||||
|
||||
@add_test_categories(['pyapi'])
|
||||
@no_debug_info_test
|
||||
def test_string_out(self):
|
||||
|
||||
self.skip_windows()
|
||||
|
||||
io = StringIO()
|
||||
debugger = lldb.SBDebugger.Create()
|
||||
try:
|
||||
debugger.SetOutputFileHandle(io, False)
|
||||
handle_command(debugger, 'script print("foobar")')
|
||||
|
||||
self.assertEqual(io.buf.strip(), "foobar")
|
||||
|
||||
finally:
|
||||
lldb.SBDebugger.Destroy(debugger)
|
||||
|
||||
|
||||
@add_test_categories(['pyapi'])
|
||||
@no_debug_info_test
|
||||
def test_string_error(self):
|
||||
|
||||
self.skip_windows()
|
||||
|
||||
io = StringIO()
|
||||
debugger = lldb.SBDebugger.Create()
|
||||
try:
|
||||
debugger.SetErrorFileHandle(io, False)
|
||||
handle_command(debugger, 'lolwut', raise_on_fail=False, collect_result=False)
|
||||
|
||||
errors = io.buf
|
||||
self.assertTrue(re.search(r'error:.*lolwut', errors))
|
||||
|
||||
finally:
|
||||
lldb.SBDebugger.Destroy(debugger)
|
||||
|
||||
@add_test_categories(['pyapi'])
|
||||
@no_debug_info_test
|
||||
def test_replace_stdout(self):
|
||||
|
||||
self.skip_windows()
|
||||
|
||||
io = StringIO()
|
||||
debugger = lldb.SBDebugger.Create()
|
||||
try:
|
||||
|
||||
with replace_stdout(io):
|
||||
handle_command(debugger, 'script print("lol, crash")', collect_result=False)
|
||||
|
||||
finally:
|
||||
lldb.SBDebugger.Destroy(debugger)
|
||||
|
||||
|
||||
@add_test_categories(['pyapi'])
|
||||
@no_debug_info_test
|
||||
def test_replace_stdout_with_nonfile(self):
|
||||
|
||||
self.skip_windows()
|
||||
|
||||
io = StringIO()
|
||||
|
||||
with replace_stdout(io):
|
||||
|
||||
class Nothing():
|
||||
pass
|
||||
|
||||
debugger = lldb.SBDebugger.Create()
|
||||
try:
|
||||
with replace_stdout(Nothing):
|
||||
self.assertEqual(sys.stdout, Nothing)
|
||||
handle_command(debugger, 'script print("lol, crash")', collect_result=False)
|
||||
self.assertEqual(sys.stdout, Nothing)
|
||||
finally:
|
||||
lldb.SBDebugger.Destroy(debugger)
|
||||
|
||||
sys.stdout.write("FOO")
|
||||
|
||||
self.assertEqual(io.buf, "FOO")
|
||||
|
||||
|
||||
@add_test_categories(['pyapi'])
|
||||
@no_debug_info_test
|
||||
def test_stream_error(self):
|
||||
|
||||
self.skip_windows()
|
||||
|
||||
messages = list()
|
||||
|
||||
io = BadIO()
|
||||
debugger = lldb.SBDebugger.Create()
|
||||
try:
|
||||
debugger.SetOutputFileHandle(io, False)
|
||||
debugger.SetLoggingCallback(messages.append)
|
||||
handle_command(debugger, 'log enable lldb script')
|
||||
handle_command(debugger, 'script print "foobar"')
|
||||
|
||||
finally:
|
||||
lldb.SBDebugger.Destroy(debugger)
|
||||
|
||||
for message in messages:
|
||||
self.comment("GOT: " + message.strip())
|
||||
|
||||
self.assertTrue(any('OH NOE' in msg for msg in messages))
|
||||
self.assertTrue(any('BadIO' in msg for msg in messages))
|
||||
|
||||
|
|
@ -171,6 +171,14 @@ public:
|
|||
void
|
||||
SetErrorFileHandle (FILE *f, bool transfer_ownership);
|
||||
|
||||
%feature("docstring",
|
||||
"Flush the Debugger's Output/Error file handles.
|
||||
For instance, this is needed by a repl implementation on top of
|
||||
the SB API, where fine grained control of output timing was needed."
|
||||
) FlushDebuggerOutputHandles;
|
||||
void
|
||||
FlushDebuggerOutputHandles ();
|
||||
|
||||
FILE *
|
||||
GetInputFileHandle ();
|
||||
|
||||
|
|
|
@ -270,6 +270,18 @@ void SBDebugger::SetInputFileHandle(FILE *fh, bool transfer_ownership) {
|
|||
m_opaque_sp->SetInputFileHandle(fh, transfer_ownership);
|
||||
}
|
||||
|
||||
void SBDebugger::FlushDebuggerOutputHandles() {
|
||||
Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_API));
|
||||
|
||||
if (log)
|
||||
log->Printf(
|
||||
"SBDebugger(%p)::FlushDebuggerOutputHandles ()",
|
||||
static_cast<void *>(m_opaque_sp.get()));
|
||||
|
||||
if (m_opaque_sp)
|
||||
m_opaque_sp->FlushDebuggerOutputHandles();
|
||||
}
|
||||
|
||||
void SBDebugger::SetOutputFileHandle(FILE *fh, bool transfer_ownership) {
|
||||
Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_API));
|
||||
|
||||
|
|
|
@ -680,6 +680,15 @@ void Debugger::Destroy(DebuggerSP &debugger_sp) {
|
|||
if (!debugger_sp)
|
||||
return;
|
||||
|
||||
/*
|
||||
* FILE* get flushed on process exit. If those FILEs need to call into python
|
||||
* to flush, we can't have them flushing after python is already torn down.
|
||||
* That would result in a segfault. We are still relying on the python script
|
||||
* to tear down the debugger before it exits.
|
||||
*/
|
||||
debugger_sp->m_output_file_sp->Flush();
|
||||
debugger_sp->m_error_file_sp->Flush();
|
||||
|
||||
debugger_sp->Clear();
|
||||
|
||||
if (g_debugger_list_ptr && g_debugger_list_mutex_ptr) {
|
||||
|
@ -896,6 +905,11 @@ void Debugger::SetErrorFileHandle(FILE *fh, bool tranfer_ownership) {
|
|||
err_file.SetStream(stderr, false);
|
||||
}
|
||||
|
||||
void Debugger::FlushDebuggerOutputHandles() {
|
||||
m_output_file_sp->Flush();
|
||||
m_error_file_sp->Flush();
|
||||
}
|
||||
|
||||
void Debugger::SaveInputTerminalState() {
|
||||
if (m_input_file_sp) {
|
||||
File &in_file = m_input_file_sp->GetFile();
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
#include <limits.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <assert.h>
|
||||
|
||||
#ifdef _WIN32
|
||||
#include "lldb/Host/windows/windows.h"
|
||||
|
@ -71,6 +72,129 @@ static const char *GetStreamOpenModeFromOptions(uint32_t options) {
|
|||
int File::kInvalidDescriptor = -1;
|
||||
FILE *File::kInvalidStream = NULL;
|
||||
|
||||
File::File(File &&rhs)
|
||||
: IOObject(eFDTypeFile, false), m_descriptor(kInvalidDescriptor),
|
||||
m_stream(kInvalidStream), m_options(), m_own_stream(false),
|
||||
m_is_interactive(eLazyBoolCalculate),
|
||||
m_is_real_terminal(eLazyBoolCalculate),
|
||||
m_supports_colors(eLazyBoolCalculate)
|
||||
{
|
||||
Swap(rhs);
|
||||
}
|
||||
|
||||
File& File::operator= (File &&rhs)
|
||||
{
|
||||
Close();
|
||||
Swap(rhs);
|
||||
return *this;
|
||||
}
|
||||
|
||||
void File::Swap(File &rhs)
|
||||
{
|
||||
std::swap(m_descriptor, rhs.m_descriptor);
|
||||
std::swap(m_stream, rhs.m_stream);
|
||||
std::swap(m_own_stream, rhs.m_own_stream);
|
||||
std::swap(m_options, rhs.m_options);
|
||||
std::swap(m_is_interactive, rhs.m_is_interactive);
|
||||
std::swap(m_is_real_terminal, rhs.m_is_real_terminal);
|
||||
std::swap(m_supports_colors, rhs.m_supports_colors);
|
||||
}
|
||||
|
||||
#if defined(__linux__)
|
||||
|
||||
struct context {
|
||||
void *cookie;
|
||||
int (*readfn)(void *, char *, int);
|
||||
int (*writefn)(void *, const char *, int);
|
||||
int (*closefn)(void *);
|
||||
};
|
||||
|
||||
static ssize_t
|
||||
write_wrapper(void *c, const char *buf, size_t size)
|
||||
{
|
||||
auto ctx = (struct context *)c;
|
||||
if (size > INT_MAX) {
|
||||
size = INT_MAX;
|
||||
}
|
||||
ssize_t wrote = ctx->writefn(ctx->cookie, buf, (int)size);
|
||||
assert(wrote < 0 || (size_t)wrote <= size);
|
||||
if (wrote < 0) {
|
||||
return -1;
|
||||
} else {
|
||||
return (int)wrote;
|
||||
}
|
||||
}
|
||||
|
||||
static ssize_t
|
||||
read_wrapper(void *c, char *buf, size_t size)
|
||||
{
|
||||
auto ctx = (struct context *)c;
|
||||
if (size > INT_MAX) {
|
||||
size = INT_MAX;
|
||||
}
|
||||
ssize_t read = ctx->writefn(ctx->cookie, buf, (int)size);
|
||||
assert(read < 0 || (size_t)read <= size);
|
||||
if (read < 0) {
|
||||
return -1;
|
||||
} else {
|
||||
return (int)read;
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
close_wrapper(void *c)
|
||||
{
|
||||
auto ctx = (struct context *)c;
|
||||
int ret = ctx->closefn(ctx->cookie);
|
||||
delete ctx;
|
||||
return ret;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
File::File(void *cookie,
|
||||
int (*readfn)(void *, char *, int),
|
||||
int (*writefn)(void *, const char *, int),
|
||||
int (*closefn)(void *))
|
||||
: IOObject(eFDTypeFile, false), m_descriptor(kInvalidDescriptor),
|
||||
m_stream(kInvalidStream), m_options(), m_own_stream(false),
|
||||
m_is_interactive(eLazyBoolCalculate),
|
||||
m_is_real_terminal(eLazyBoolCalculate),
|
||||
m_supports_colors(eLazyBoolCalculate)
|
||||
{
|
||||
#if defined(__APPLE__) || defined(__FreeBSD__) || defined(__NetBSD__)
|
||||
m_stream = funopen(cookie, readfn, writefn, NULL, closefn);
|
||||
#elif defined(__linux__)
|
||||
cookie_io_functions_t io_funcs = {};
|
||||
io_funcs.read = read_wrapper;
|
||||
io_funcs.write = write_wrapper;
|
||||
io_funcs.close = close_wrapper;
|
||||
const char *mode = NULL;
|
||||
if (readfn && writefn) {
|
||||
mode = "r+";
|
||||
} else if (readfn) {
|
||||
mode = "r";
|
||||
} else if (writefn) {
|
||||
mode = "w";
|
||||
}
|
||||
if (mode) {
|
||||
struct context *ctx = new context;
|
||||
ctx->readfn = readfn;
|
||||
ctx->writefn = writefn;
|
||||
ctx->closefn = closefn;
|
||||
ctx->cookie = cookie;
|
||||
m_stream = fopencookie(ctx, mode, io_funcs);
|
||||
if (!m_stream) {
|
||||
delete ctx;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
if (m_stream) {
|
||||
m_own_stream = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
File::File(const char *path, uint32_t options, uint32_t permissions)
|
||||
: IOObject(eFDTypeFile, false), m_descriptor(kInvalidDescriptor),
|
||||
m_stream(kInvalidStream), m_options(), m_own_stream(false),
|
||||
|
|
|
@ -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,10 @@ PythonFile::~PythonFile() {}
|
|||
|
||||
bool PythonFile::Check(PyObject *py_obj) {
|
||||
#if PY_MAJOR_VERSION < 3
|
||||
return PyFile_Check(py_obj);
|
||||
#else
|
||||
if (PyFile_Check(py_obj)) {
|
||||
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`.
|
||||
|
@ -977,11 +980,11 @@ bool PythonFile::Check(PyObject *py_obj) {
|
|||
|
||||
if (1 != PyObject_IsSubclass(object_type.get(), io_base_class.get()))
|
||||
return false;
|
||||
|
||||
if (!object_type.HasAttribute("fileno"))
|
||||
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,168 @@ uint32_t PythonFile::GetOptionsFromMode(llvm::StringRef mode) {
|
|||
.Default(0);
|
||||
}
|
||||
|
||||
static const char *
|
||||
str(PyObject *o, const char *defaultt, PyObject **cleanup)
|
||||
{
|
||||
*cleanup = NULL;
|
||||
if (o == NULL) {
|
||||
return defaultt;
|
||||
}
|
||||
PyObject *string = PyObject_Str(o);
|
||||
*cleanup = string;
|
||||
if (string == NULL) {
|
||||
return defaultt;
|
||||
}
|
||||
if (PyUnicode_Check(string)) {
|
||||
PyObject *bytes = PyUnicode_AsEncodedString(string, "utf-8", "Error ~");
|
||||
if (bytes == NULL) {
|
||||
return defaultt;
|
||||
}
|
||||
Py_XDECREF(string);
|
||||
*cleanup = bytes;
|
||||
return PyBytes_AS_STRING(bytes);
|
||||
} else {
|
||||
return PyBytes_AS_STRING(string);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
log_exception(const char *fmt, PyObject *obj)
|
||||
{
|
||||
Log *log = GetLogIfAllCategoriesSet(LIBLLDB_LOG_SCRIPT);
|
||||
if (!log) {
|
||||
return;
|
||||
}
|
||||
PyObject *pyclass = PyObject_Type(obj);
|
||||
PyObject *pyclassname = NULL;
|
||||
if (pyclass) {
|
||||
pyclassname = PyObject_GetAttrString(pyclass, "__name__");
|
||||
}
|
||||
PyObject *exception = NULL, *v = NULL, *tb = NULL;
|
||||
PyErr_Fetch(&exception, &v, &tb);
|
||||
if (exception) {
|
||||
PyErr_NormalizeException(&exception, &v, &tb);
|
||||
}
|
||||
PyObject *cleanup1 = NULL, *cleanup2 = NULL;
|
||||
log->Printf(fmt,
|
||||
str(pyclassname, "UknownClass", &cleanup1),
|
||||
str(v, "unknown error", &cleanup2));
|
||||
Py_XDECREF(cleanup1);
|
||||
Py_XDECREF(cleanup2);
|
||||
Py_XDECREF(pyclass);
|
||||
Py_XDECREF(pyclassname);
|
||||
Py_XDECREF(exception);
|
||||
Py_XDECREF(v);
|
||||
Py_XDECREF(tb);
|
||||
|
||||
}
|
||||
|
||||
class GIL
|
||||
{
|
||||
private:
|
||||
PyGILState_STATE m_state;
|
||||
public:
|
||||
GIL() {
|
||||
m_state = PyGILState_Ensure();
|
||||
}
|
||||
~GIL() {
|
||||
PyGILState_Release(m_state);
|
||||
}
|
||||
};
|
||||
|
||||
#define callmethod(obj, name, fmt, ...) \
|
||||
PythonObject(PyRefType::Owned, PyObject_CallMethod(obj, (char*)name, (char*)fmt, __VA_ARGS__))
|
||||
|
||||
#define callmethod0(obj, name, fmt) \
|
||||
PythonObject(PyRefType::Owned, PyObject_CallMethod(obj, (char*)name, (char*)fmt))
|
||||
|
||||
static int readfn(void *ctx, char *buffer, int n)
|
||||
{
|
||||
GIL gil;
|
||||
auto *file = (PyObject *) ctx;
|
||||
|
||||
PythonObject pyresult = callmethod(file, "read", "(i)", n);
|
||||
if (!pyresult.IsValid() || PyErr_Occurred()) {
|
||||
log_exception("read from python %s failed: %s", file);
|
||||
return -1;
|
||||
}
|
||||
if (!PyBytes_Check(pyresult)) {
|
||||
PyErr_SetString(PyExc_TypeError, "read didn't return bytes");
|
||||
log_exception("read from python %s failed: %s", file);
|
||||
return -1;
|
||||
}
|
||||
|
||||
int r = 0;
|
||||
char *data = NULL;
|
||||
ssize_t length = 0;
|
||||
r = PyBytes_AsStringAndSize(pyresult, &data, &length);
|
||||
if (r || length < 0 || PyErr_Occurred()) {
|
||||
log_exception("read from python %s failed: %s", file);
|
||||
return -1;
|
||||
}
|
||||
|
||||
memcpy(buffer, data, length);
|
||||
return length;
|
||||
}
|
||||
|
||||
static int writefn(void *ctx, const char *buffer, int n)
|
||||
{
|
||||
GIL gil;
|
||||
auto *file = (PyObject *) ctx;
|
||||
|
||||
PythonObject pyresult = callmethod(file, "write", "(s#)", buffer, n);
|
||||
if (!pyresult.IsValid() || PyErr_Occurred()) {
|
||||
log_exception("write to python %s failed: %s", file);
|
||||
return -1;
|
||||
}
|
||||
|
||||
int result = PyLong_AsLong(pyresult);
|
||||
if (PyErr_Occurred()) {
|
||||
log_exception("read from python %s failed: %s", file);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static int closefn(void *ctx) {
|
||||
GIL gil;
|
||||
auto *file = (PyObject *) ctx;
|
||||
Py_XDECREF(file);
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool PythonFile::GetUnderlyingFile(File &file) const {
|
||||
if (!IsValid())
|
||||
return false;
|
||||
|
||||
if (!PythonFile::Check(m_py_obj))
|
||||
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();
|
||||
}
|
||||
|
||||
bool readable = PyObject_IsTrue(callmethod0(m_py_obj, "readable", "()"));
|
||||
bool writable = PyObject_IsTrue(callmethod0(m_py_obj, "writable", "()"));
|
||||
|
||||
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
|
||||
|
|
|
@ -113,6 +113,8 @@ public:
|
|||
Reset(PyRefType::Borrowed, rhs.m_py_obj);
|
||||
}
|
||||
|
||||
operator PyObject*() const { return m_py_obj; }
|
||||
|
||||
// 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
|
||||
|
|
|
@ -504,7 +504,7 @@ void ScriptInterpreterPython::LeaveSession() {
|
|||
}
|
||||
|
||||
bool ScriptInterpreterPython::SetStdHandle(File &file, const char *py_name,
|
||||
PythonFile &save_file,
|
||||
PythonObject &save_file,
|
||||
const char *mode) {
|
||||
if (file.IsValid()) {
|
||||
// Flush the file before giving it to python to avoid interleaved output.
|
||||
|
@ -512,8 +512,7 @@ bool ScriptInterpreterPython::SetStdHandle(File &file, const char *py_name,
|
|||
|
||||
PythonDictionary &sys_module_dict = GetSysModuleDictionary();
|
||||
|
||||
save_file = sys_module_dict.GetItemForKey(PythonString(py_name))
|
||||
.AsType<PythonFile>();
|
||||
save_file = sys_module_dict.GetItemForKey(PythonString(py_name));
|
||||
|
||||
PythonFile new_file(file, mode);
|
||||
sys_module_dict.SetItemForKey(PythonString(py_name), new_file);
|
||||
|
|
|
@ -541,12 +541,12 @@ protected:
|
|||
|
||||
bool GetEmbeddedInterpreterModuleObjects();
|
||||
|
||||
bool SetStdHandle(File &file, const char *py_name, PythonFile &save_file,
|
||||
bool SetStdHandle(File &file, const char *py_name, PythonObject &save_file,
|
||||
const char *mode);
|
||||
|
||||
PythonFile m_saved_stdin;
|
||||
PythonFile m_saved_stdout;
|
||||
PythonFile m_saved_stderr;
|
||||
PythonObject m_saved_stdin;
|
||||
PythonObject m_saved_stdout;
|
||||
PythonObject m_saved_stderr;
|
||||
PythonObject m_main_module;
|
||||
PythonObject m_lldb_module;
|
||||
PythonDictionary m_session_dict;
|
||||
|
|
Loading…
Reference in New Issue