aboutsummaryrefslogtreecommitdiffstats
path: root/3rdparty
diff options
context:
space:
mode:
authorMiodrag Milanovic <mmicko@gmail.com>2018-06-28 17:56:44 +0200
committerMiodrag Milanovic <mmicko@gmail.com>2018-06-28 17:57:26 +0200
commit1676b285ae726eb858d4d7ed089496133ce3de4b (patch)
treeb7c2a9a59c2a9a0377d266567e545f5a1c23ae58 /3rdparty
parent2037fff7426e252a8692b971f88689c1dd8c03e8 (diff)
downloadnextpnr-1676b285ae726eb858d4d7ed089496133ce3de4b.tar.gz
nextpnr-1676b285ae726eb858d4d7ed089496133ce3de4b.tar.bz2
nextpnr-1676b285ae726eb858d4d7ed089496133ce3de4b.zip
adapted python-console for easier use
Diffstat (limited to '3rdparty')
-rw-r--r--3rdparty/python-console/Utils.h2
-rw-r--r--3rdparty/python-console/modified/pyconsole.cc294
-rw-r--r--3rdparty/python-console/modified/pyconsole.h77
-rw-r--r--3rdparty/python-console/modified/pyinterpreter.cc155
-rw-r--r--3rdparty/python-console/modified/pyinterpreter.h36
-rw-r--r--3rdparty/python-console/modified/pyredirector.cc112
-rw-r--r--3rdparty/python-console/modified/pyredirector.h32
7 files changed, 707 insertions, 1 deletions
diff --git a/3rdparty/python-console/Utils.h b/3rdparty/python-console/Utils.h
index ebd4234f..bbd11a1a 100644
--- a/3rdparty/python-console/Utils.h
+++ b/3rdparty/python-console/Utils.h
@@ -23,7 +23,7 @@ std::string LongestCommonPrefix( InputIterator begin, InputIterator end )
const std::string& str = *it;
for (int j = 0; j <= endIndex; ++j)
{
- if (j >= str.size() || str[j] != str0[j])
+ if (j >= (int)str.size() || str[j] != str0[j])
endIndex = j - 1;
}
}
diff --git a/3rdparty/python-console/modified/pyconsole.cc b/3rdparty/python-console/modified/pyconsole.cc
new file mode 100644
index 00000000..35f3e930
--- /dev/null
+++ b/3rdparty/python-console/modified/pyconsole.cc
@@ -0,0 +1,294 @@
+#include "pyconsole.h"
+#include "pyinterpreter.h"
+#include "ColumnFormatter.h"
+
+#include <iostream>
+#include <QKeyEvent>
+#include <QFont>
+
+#include "Utils.h"
+
+const QString PythonConsole::PROMPT = ">>> ";
+const QString PythonConsole::MULTILINE_PROMPT = "... ";
+const QColor PythonConsole::NORMAL_COLOR = QColor::fromRgbF( 0, 0, 0 );
+const QColor PythonConsole::ERROR_COLOR = QColor::fromRgbF( 1.0, 0, 0 );
+const QColor PythonConsole::OUTPUT_COLOR = QColor::fromRgbF( 0, 0, 1.0 );
+
+PythonConsole::PythonConsole( QWidget* parent ):
+ QTextEdit( parent )
+{
+ QFont font("unexistent");
+ font.setStyleHint(QFont::Monospace);
+ setFont(font);
+ m_parseHelper.subscribe( this );
+}
+
+void PythonConsole::keyPressEvent( QKeyEvent* e )
+{
+ switch ( e->key() )
+ {
+ case Qt::Key_Return:
+ handleReturnKeyPress( );
+ return;
+
+ case Qt::Key_Tab:
+ autocomplete( );
+ return;
+
+ case Qt::Key_Backspace:
+ if ( ! canBackspace( ) )
+ return;
+ break;
+
+ case Qt::Key_Up:
+ previousHistory( );
+ return;
+
+ case Qt::Key_Down:
+ nextHistory( );
+ return;
+
+ case Qt::Key_Left:
+ if ( ! canGoLeft( ) )
+ return;
+ }
+
+ QTextEdit::keyPressEvent( e );
+}
+
+void PythonConsole::handleReturnKeyPress( )
+{
+ if ( ! cursorIsOnInputLine( ) )
+ {
+ return;
+ }
+
+ QString line = getLine( );
+
+ m_parseHelper.process( line.toStdString( ) );
+ if ( m_parseHelper.buffered( ) )
+ {
+ append("");
+ displayPrompt( );
+ }
+ if ( line.size( ) )
+ {
+ m_historyBuffer.push_back( line.toStdString( ) );
+ m_historyIt = m_historyBuffer.end();
+ }
+ moveCursorToEnd( );
+}
+
+void PythonConsole::parseEvent( const ParseMessage& message )
+{
+ // handle invalid user input
+ if ( message.errorCode )
+ {
+ setTextColor( ERROR_COLOR );
+ append(message.message.c_str());
+
+ setTextColor( NORMAL_COLOR );
+ append("");
+ displayPrompt( );
+ return;
+ }
+
+ // interpret valid user input
+ int errorCode;
+ std::string res;
+ if ( message.message.size() )
+ res = pyinterpreter_execute( message.message, &errorCode );
+ if ( errorCode )
+ {
+ setTextColor( ERROR_COLOR );
+ }
+ else
+ {
+ setTextColor( OUTPUT_COLOR );
+ }
+
+ if ( res.size( ) )
+ {
+ append(res.c_str());
+ }
+
+ setTextColor( NORMAL_COLOR );
+
+ // set up the next line on the console
+ append("");
+ displayPrompt( );
+}
+
+QString PythonConsole::getLine( )
+{
+ QTextCursor cursor = textCursor();
+ cursor.movePosition( QTextCursor::StartOfLine );
+ cursor.movePosition( QTextCursor::Right, QTextCursor::MoveAnchor, PythonConsole::PROMPT.size( ) );
+ cursor.movePosition( QTextCursor::EndOfLine, QTextCursor::KeepAnchor );
+ QString line = cursor.selectedText( );
+ cursor.clearSelection( );
+ return line;
+}
+
+bool PythonConsole::cursorIsOnInputLine( )
+{
+ int cursorBlock = textCursor( ).blockNumber( );
+ QTextCursor bottomCursor = textCursor( );
+ bottomCursor.movePosition( QTextCursor::End );
+ int bottomBlock = bottomCursor.blockNumber( );
+ return ( cursorBlock == bottomBlock );
+}
+
+bool PythonConsole::inputLineIsEmpty( )
+{
+ QTextCursor bottomCursor = textCursor( );
+ bottomCursor.movePosition( QTextCursor::End );
+ int col = bottomCursor.columnNumber( );
+ return ( col == PythonConsole::PROMPT.size( ) );
+}
+
+bool PythonConsole::canBackspace( )
+{
+ if ( ! cursorIsOnInputLine( ) )
+ {
+ return false;
+ }
+
+ if ( inputLineIsEmpty( ) )
+ {
+ return false;
+ }
+
+ return true;
+}
+
+bool PythonConsole::canGoLeft( )
+{
+ if ( cursorIsOnInputLine( ) )
+ {
+ QTextCursor bottomCursor = textCursor( );
+ int col = bottomCursor.columnNumber( );
+ return (col > PythonConsole::PROMPT.size( ));
+ }
+ return true;
+}
+
+void PythonConsole::displayPrompt( )
+{
+ QTextCursor cursor = textCursor();
+ cursor.movePosition( QTextCursor::End );
+ if ( m_parseHelper.buffered( ) )
+ {
+ cursor.insertText( PythonConsole::MULTILINE_PROMPT );
+ }
+ else
+ {
+ cursor.insertText( PythonConsole::PROMPT );
+ }
+ cursor.movePosition( QTextCursor::EndOfLine );
+}
+
+void PythonConsole::displayString(QString text)
+{
+ QTextCursor cursor = textCursor();
+ cursor.movePosition( QTextCursor::End );
+ cursor.insertText( text );
+ cursor.movePosition( QTextCursor::EndOfLine );
+}
+
+void PythonConsole::autocomplete( )
+{
+ if ( ! cursorIsOnInputLine( ) )
+ return;
+
+ QString line = getLine( );
+ const std::list<std::string>& suggestions =
+ pyinterpreter_suggest( line.toStdString( ) );
+ if (suggestions.size() == 1)
+ {
+ line = suggestions.back().c_str();
+ }
+ else
+ {
+ // try to complete to longest common prefix
+ std::string prefix =
+ LongestCommonPrefix(suggestions.begin(), suggestions.end());
+ if (prefix.size() > (size_t)line.size())
+ {
+ line = prefix.c_str();
+ }
+ else
+ {
+ ColumnFormatter fmt;
+ fmt.setItems(suggestions.begin(), suggestions.end());
+ fmt.format(width() / 10);
+ setTextColor( OUTPUT_COLOR );
+ const std::list<std::string>& formatted = fmt.formattedOutput();
+ for (std::list<std::string>::const_iterator it = formatted.begin();
+ it != formatted.end(); ++it)
+ {
+ append(it->c_str());
+ }
+ setTextColor( NORMAL_COLOR );
+ }
+ }
+
+ // set up the next line on the console
+ append("");
+ displayPrompt( );
+ moveCursorToEnd( );
+ QTextCursor cursor = textCursor();
+ cursor.insertText( line );
+ moveCursorToEnd( );
+}
+
+void PythonConsole::previousHistory( )
+{
+ if ( ! cursorIsOnInputLine( ) )
+ return;
+
+ if ( ! m_historyBuffer.size( ) )
+ return;
+
+ QTextCursor cursor = textCursor();
+ cursor.movePosition( QTextCursor::StartOfLine );
+ cursor.movePosition( QTextCursor::Right, QTextCursor::MoveAnchor, PythonConsole::PROMPT.size( ) );
+ cursor.movePosition( QTextCursor::EndOfLine, QTextCursor::KeepAnchor );
+ cursor.removeSelectedText( );
+ if ( m_historyIt != m_historyBuffer.begin( ) )
+ {
+ --m_historyIt;
+ }
+ cursor.insertText( m_historyIt->c_str() );
+}
+
+void PythonConsole::nextHistory( )
+{
+ if ( ! cursorIsOnInputLine( ) )
+ return;
+
+ if ( ! m_historyBuffer.size( ) )
+ return;
+ if ( m_historyIt == m_historyBuffer.end( ) )
+ {
+ return;
+ }
+ QTextCursor cursor = textCursor();
+ cursor.movePosition( QTextCursor::StartOfLine );
+ cursor.movePosition( QTextCursor::Right, QTextCursor::MoveAnchor, PythonConsole::PROMPT.size( ) );
+ cursor.movePosition( QTextCursor::EndOfLine, QTextCursor::KeepAnchor );
+ cursor.removeSelectedText( );
+ ++m_historyIt;
+ if ( m_historyIt == m_historyBuffer.end( ) )
+ {
+ return;
+ }
+ cursor.insertText( m_historyIt->c_str() );
+}
+
+void PythonConsole::moveCursorToEnd( )
+{
+ QTextCursor cursor = textCursor();
+ cursor.movePosition( QTextCursor::End );
+ setTextCursor( cursor );
+}
diff --git a/3rdparty/python-console/modified/pyconsole.h b/3rdparty/python-console/modified/pyconsole.h
new file mode 100644
index 00000000..eb2c98a4
--- /dev/null
+++ b/3rdparty/python-console/modified/pyconsole.h
@@ -0,0 +1,77 @@
+/**
+python-console
+Copyright (C) 2018 Alex Tsui
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the "Software"), to deal in
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+of the Software, and to permit persons to whom the Software is furnished to do
+so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+*/
+
+#ifndef PYCONSOLE_H
+#define PYCONSOLE_H
+#include <QColor>
+#include <QTextEdit>
+#include "ParseHelper.h"
+#include "ParseListener.h"
+
+class QWidget;
+class QKeyEvent;
+
+class PythonConsole : public QTextEdit, ParseListener
+{
+ Q_OBJECT
+
+ public:
+ PythonConsole(QWidget *parent = 0);
+
+ void displayPrompt();
+ void displayString(QString text);
+
+ protected:
+ // override QTextEdit
+ virtual void keyPressEvent(QKeyEvent *e);
+
+ virtual void handleReturnKeyPress();
+
+ /**
+ Handle a compilable chunk of Python user input.
+ */
+ virtual void parseEvent(const ParseMessage &message);
+
+ QString getLine();
+ bool cursorIsOnInputLine();
+ bool inputLineIsEmpty();
+ bool canBackspace();
+ bool canGoLeft();
+ void autocomplete();
+ void previousHistory();
+ void nextHistory();
+ void moveCursorToEnd();
+
+ static const QString PROMPT;
+ static const QString MULTILINE_PROMPT;
+
+ static const QColor NORMAL_COLOR;
+ static const QColor ERROR_COLOR;
+ static const QColor OUTPUT_COLOR;
+
+ ParseHelper m_parseHelper;
+ std::list<std::string> m_historyBuffer;
+ std::list<std::string>::const_iterator m_historyIt;
+};
+
+#endif // PYCONSOLE_H
diff --git a/3rdparty/python-console/modified/pyinterpreter.cc b/3rdparty/python-console/modified/pyinterpreter.cc
new file mode 100644
index 00000000..ee8c4aa8
--- /dev/null
+++ b/3rdparty/python-console/modified/pyinterpreter.cc
@@ -0,0 +1,155 @@
+/**
+python-console
+Copyright (C) 2014 Alex Tsui
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the "Software"), to deal in
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+of the Software, and to permit persons to whom the Software is furnished to do
+so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+*/
+
+#include "pyinterpreter.h"
+#include <Python.h>
+#include <iostream>
+#include <map>
+#include <memory>
+#include "pyredirector.h"
+
+static PyThreadState *MainThreadState = NULL;
+
+static PyThreadState *m_threadState = NULL;
+static PyObject *glb = NULL;
+static PyObject *loc = NULL;
+
+static std::list<std::string> m_suggestions;
+
+template <typename... Args> std::string string_format(const std::string &format, Args... args)
+{
+ size_t size = std::snprintf(nullptr, 0, format.c_str(), args...) + 1; // Extra space for '\0'
+ std::unique_ptr<char[]> buf(new char[size]);
+ std::snprintf(buf.get(), size, format.c_str(), args...);
+ return std::string(buf.get(), buf.get() + size - 1); // We don't want the '\0' inside
+}
+
+std::string pyinterpreter_execute(const std::string &command, int *errorCode)
+{
+ PyEval_AcquireThread(m_threadState);
+ *errorCode = 0;
+
+ PyObject *py_result;
+ PyObject *dum;
+ std::string res;
+ py_result = Py_CompileString(command.c_str(), "<stdin>", Py_single_input);
+ if (py_result == 0) {
+ if (PyErr_Occurred()) {
+ *errorCode = 1;
+ PyErr_Print();
+ res = redirector_take_output(m_threadState);
+ }
+
+ PyEval_ReleaseThread(m_threadState);
+ return res;
+ }
+ dum = PyEval_EvalCode(py_result, glb, loc);
+ Py_XDECREF(dum);
+ Py_XDECREF(py_result);
+ if (PyErr_Occurred()) {
+ *errorCode = 1;
+ PyErr_Print();
+ }
+
+ res = redirector_take_output(m_threadState);
+
+ PyEval_ReleaseThread(m_threadState);
+ return res;
+}
+
+const std::list<std::string> &pyinterpreter_suggest(const std::string &hint)
+{
+ PyEval_AcquireThread(m_threadState);
+ m_suggestions.clear();
+ int i = 0;
+ std::string command = string_format("sys.completer.complete('%s', %d)\n", hint.c_str(), i);
+ std::string res;
+ do {
+ PyObject *py_result;
+ PyObject *dum;
+ py_result = Py_CompileString(command.c_str(), "<stdin>", Py_single_input);
+ dum = PyEval_EvalCode(py_result, glb, loc);
+ Py_XDECREF(dum);
+ Py_XDECREF(py_result);
+ res = redirector_take_output(m_threadState);
+
+ ++i;
+ command = string_format("sys.completer.complete('%s', %d)\n", hint.c_str(), i);
+ if (res.size()) {
+ // throw away the newline
+ res = res.substr(1, res.size() - 3);
+ m_suggestions.push_back(res);
+ }
+ } while (res.size());
+
+ PyEval_ReleaseThread(m_threadState);
+ return m_suggestions;
+}
+
+void pyinterpreter_preinit()
+{
+ m_suggestions.clear();
+ inittab_redirector();
+}
+
+void pyinterpreter_initialize()
+{
+ PyEval_InitThreads();
+ MainThreadState = PyEval_SaveThread();
+ PyEval_AcquireThread(MainThreadState);
+ m_threadState = Py_NewInterpreter();
+
+ PyObject *module = PyImport_ImportModule("__main__");
+ loc = glb = PyModule_GetDict(module);
+
+ PyRun_SimpleString("import sys\n"
+ "import redirector\n"
+ "sys.path.insert(0, \".\")\n" // add current path
+ "sys.stdout = redirector.redirector()\n"
+ "sys.stderr = sys.stdout\n"
+ "import rlcompleter\n"
+ "sys.completer = rlcompleter.Completer()\n");
+
+ PyEval_ReleaseThread(m_threadState);
+}
+
+void pyinterpreter_finalize()
+{
+ m_suggestions.clear();
+
+ PyEval_AcquireThread(m_threadState);
+ Py_EndInterpreter(m_threadState);
+ PyEval_ReleaseLock();
+
+ PyEval_RestoreThread(MainThreadState);
+}
+
+void pyinterpreter_aquire()
+{
+ PyEval_AcquireThread(m_threadState);
+}
+
+void pyinterpreter_release()
+{
+ PyEval_ReleaseThread(m_threadState);
+}
diff --git a/3rdparty/python-console/modified/pyinterpreter.h b/3rdparty/python-console/modified/pyinterpreter.h
new file mode 100644
index 00000000..1a85c1fb
--- /dev/null
+++ b/3rdparty/python-console/modified/pyinterpreter.h
@@ -0,0 +1,36 @@
+/**
+python-console
+Copyright (C) 2014 Alex Tsui
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the "Software"), to deal in
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+of the Software, and to permit persons to whom the Software is furnished to do
+so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+*/
+
+#ifndef PYINTERPRETER_H
+#define PYINTERPRETER_H
+#include <list>
+#include <string>
+
+std::string pyinterpreter_execute(const std::string &command, int *errorCode);
+const std::list<std::string> &pyinterpreter_suggest(const std::string &hint);
+void pyinterpreter_preinit();
+void pyinterpreter_initialize();
+void pyinterpreter_finalize();
+void pyinterpreter_aquire();
+void pyinterpreter_release();
+#endif // PYINTERPRETER_H
diff --git a/3rdparty/python-console/modified/pyredirector.cc b/3rdparty/python-console/modified/pyredirector.cc
new file mode 100644
index 00000000..5f6c43a1
--- /dev/null
+++ b/3rdparty/python-console/modified/pyredirector.cc
@@ -0,0 +1,112 @@
+/**
+python-console
+Copyright (C) 2014 Alex Tsui
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the "Software"), to deal in
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+of the Software, and to permit persons to whom the Software is furnished to do
+so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+*/
+
+#include "pyredirector.h"
+#include <map>
+
+static std::map<PyThreadState *, std::string> thread_strings;
+
+static std::string &redirector_string(PyThreadState *threadState)
+{
+ if (!thread_strings.count(threadState)) {
+ thread_strings[threadState] = "";
+ }
+ return thread_strings[threadState];
+}
+
+std::string redirector_take_output(PyThreadState *threadState)
+{
+ std::string res = redirector_string(threadState);
+ redirector_string(threadState) = "";
+ return res;
+}
+
+static PyObject *redirector_init(PyObject *, PyObject *)
+{
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *redirector_write(PyObject *, PyObject *args)
+{
+ char *output;
+ PyObject *selfi;
+
+ if (!PyArg_ParseTuple(args, "Os", &selfi, &output)) {
+ return NULL;
+ }
+
+ std::string outputString(output);
+ PyThreadState *currentThread = PyThreadState_Get();
+ std::string &resultString = redirector_string(currentThread);
+ resultString = resultString + outputString;
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyMethodDef redirector_methods[] = {
+ {"__init__", redirector_init, METH_VARARGS, "initialize the stdout/err redirector"},
+ {"write", redirector_write, METH_VARARGS, "implement the write method to redirect stdout/err"},
+ {NULL, NULL, 0, NULL},
+};
+
+static PyObject *createClassObject(const char *name, PyMethodDef methods[])
+{
+ PyObject *pClassName = PyUnicode_FromString(name);
+ PyObject *pClassBases = PyTuple_New(0); // An empty tuple for bases is equivalent to `(object,)`
+ PyObject *pClassDic = PyDict_New();
+
+ PyMethodDef *def;
+ // add methods to class
+ for (def = methods; def->ml_name != NULL; def++) {
+ PyObject *func = PyCFunction_New(def, NULL);
+ PyObject *method = PyInstanceMethod_New(func);
+ PyDict_SetItemString(pClassDic, def->ml_name, method);
+ Py_DECREF(func);
+ Py_DECREF(method);
+ }
+
+ // pClass = type(pClassName, pClassBases, pClassDic)
+ PyObject *pClass = PyObject_CallFunctionObjArgs((PyObject *)&PyType_Type, pClassName, pClassBases, pClassDic, NULL);
+
+ Py_DECREF(pClassName);
+ Py_DECREF(pClassBases);
+ Py_DECREF(pClassDic);
+
+ return pClass;
+}
+
+static struct PyModuleDef moduledef = {PyModuleDef_HEAD_INIT, "redirector", 0, -1, 0};
+
+PyMODINIT_FUNC PyInit_redirector(void)
+{
+ PyObject *m = PyModule_Create(&moduledef);
+ if (m) {
+ PyObject *fooClass = createClassObject("redirector", redirector_methods);
+ PyModule_AddObject(m, "redirector", fooClass);
+ Py_DECREF(fooClass);
+ }
+ return m;
+}
+
+void inittab_redirector() { PyImport_AppendInittab("redirector", PyInit_redirector); } \ No newline at end of file
diff --git a/3rdparty/python-console/modified/pyredirector.h b/3rdparty/python-console/modified/pyredirector.h
new file mode 100644
index 00000000..21e20a62
--- /dev/null
+++ b/3rdparty/python-console/modified/pyredirector.h
@@ -0,0 +1,32 @@
+/**
+python-console
+Copyright (C) 2014 Alex Tsui
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the "Software"), to deal in
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+of the Software, and to permit persons to whom the Software is furnished to do
+so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+*/
+
+#ifndef PYREDIRECTOR_H
+#define PYREDIRECTOR_H
+#include <Python.h>
+#include <string>
+
+std::string redirector_take_output(PyThreadState *threadState);
+void inittab_redirector();
+
+#endif // PYREDIRECTOR_H