Commit cef4e5aa authored by Jan Möbius's avatar Jan Möbius

Initial commit of the python interpreter

parent 6caf8265
//MIT License
//
//Copyright(c) 2017 Matthias Möller
//https://github.com/TinyTinni/PyLogHook
//
//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.
#pragma once
#include <utility>
#include <string>
#ifdef TYTI_PYLOGHOOK_USE_BOOST
#include <boost/python.hpp>
#else
#include <pybind11/pybind11.h>
#endif
namespace tyti {
namespace pylog {
namespace detail {
#ifdef TYTI_PYLOGHOOK_USE_BOOST
template <typename T>
inline boost::python::object LogHookMakeObject(T t)
{
return boost::python::object(boost::python::make_function(
std::forward<T>(t),
boost::python::default_call_policies(),
boost::mpl::vector<void, const char*>() // signature
));
}
#else
template<typename T>
inline pybind11::object LogHookMakeObject(T t)
{
return pybind11::cpp_function(std::forward<T>(t));
}
#endif
template<typename T>
void redirect_syspipe(T t, const char* pipename)
{
assert(Py_IsInitialized());
PyObject* out = PySys_GetObject(pipename);
// in case python couldnt create stdout/stderr
// this happens in some gui application
// just register a new nameless type for this
if (out == NULL || out == Py_None)
{
std::string register_read_write = std::string("import sys\n\
sys.") + pipename + std::string(" = type(\"\",(object,),{\"write\":lambda self, txt: None, \"flush\":lambda self: None})()\n");
PyRun_SimpleString(register_read_write.c_str());
out = PySys_GetObject(pipename);
}
// overwrite write function
PyObject_SetAttrString(out, "write",
detail::LogHookMakeObject(std::forward<T>(t)).ptr());
}
} // namespace detail
/** \brief redirects sys.stdout
Whenever sys.stdout.write is called, call the given function instead.
Given function has to be the signature "void (const char*)"
Preconditions:
Python has to be initialized.
Calling thread has to hold the GIL. (in case of multi threading)
Exceptions:
Maybe a PyErr occurs. This must be handled by the user.
@param t callbacl function of type "void (const char*)"
*/
template<typename T>
inline void redirect_stdout(T t)
{
detail::redirect_syspipe(std::forward<T>(t), "stdout");
}
template<typename T>
inline void redirect_stderr(T t)
{
detail::redirect_syspipe(std::forward<T>(t), "stderr");
}
}
}
/*===========================================================================*\
* *
* OpenFlipper *
* Copyright (c) 2001-2015, RWTH-Aachen University *
* Department of Computer Graphics and Multimedia *
* All rights reserved. *
* www.openflipper.org *
* *
*---------------------------------------------------------------------------*
* This file is part of OpenFlipper. *
*---------------------------------------------------------------------------*
* *
* Redistribution and use in source and binary forms, with or without *
* modification, are permitted provided that the following conditions *
* are met: *
* *
* 1. Redistributions of source code must retain the above copyright notice, *
* this list of conditions and the following disclaimer. *
* *
* 2. Redistributions in binary form must reproduce the above copyright *
* notice, this list of conditions and the following disclaimer in the *
* documentation and/or other materials provided with the distribution. *
* *
* 3. Neither the name of the copyright holder nor the names of its *
* contributors may be used to endorse or promote products derived from *
* this software without specific prior written permission. *
* *
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS *
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED *
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A *
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER *
* OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, *
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, *
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR *
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF *
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING *
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS *
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *
* *
\*===========================================================================*/
#include <pybind11/include/pybind11/pybind11.h>
#include <pybind11/include/pybind11/embed.h>
#include "PyLogHook.h"
#include "PythonInterpreter.hh"
namespace py = pybind11;
static Core* core_;
static pybind11::module mainModule_;
static PythonInterpreter* interpreter_ = nullptr;
/** \brief Stores a clean dictionary for interpreter reset
*
*/
static PyObject* global_dict_clean_ = nullptr;
void setCorePointer( Core* _core ) {
core_ = _core;
}
PythonInterpreter* PythonInterpreter::getInstance() {
if (interpreter_ == nullptr)
{
interpreter_ = new PythonInterpreter();
}
return interpreter_;
}
PythonInterpreter::PythonInterpreter() {
}
PythonInterpreter::~PythonInterpreter() {
}
bool PythonInterpreter::modulesInitialized() {
///@TODO: Why does this work?
return static_cast<bool>(mainModule_);
}
void PythonInterpreter::initPython() {
if (Py_IsInitialized() )
return;
std::cerr << "A" << std::endl;
py::initialize_interpreter();
std::cerr << "B" << std::endl;
PyEval_InitThreads();
std::cerr << "C" << std::endl;
if (!modulesInitialized()) {
std::cerr << "D" << std::endl;
//dlopen("libpython3.5m.so.1.0", RTLD_LAZY | RTLD_GLOBAL);
py::module main{ py::module::import("__main__") };
// redirect python output
tyti::pylog::redirect_stderr([this](const char*w) {this->pyError(w); });
tyti::pylog::redirect_stdout([this](const char* w) {this->pyOutput(w); });
std::cerr << "E" << std::endl;
// add openflipper module
py::object main_namespace = main.attr("__dict__");
std::cerr << "F" << std::endl;
py::module of_module(py::module::import("openflipper"));
main_namespace["openflipper"] = of_module;
std::cerr << "G" << std::endl;
global_dict_clean_ = PyDict_Copy(PyModule_GetDict(main.ptr()));
std::cerr << "H" << std::endl;
// set the main module member to a validate state, shows, that all modules are successfully initialized
mainModule_ = std::move(main);
std::cerr << "I" << std::endl;
// Do not release the GIL until all modules are initalzed
PyEval_SaveThread();
std::cerr << "J" << std::endl;
}
}
void PythonInterpreter::resetInterpreter()
{
if (!Py_IsInitialized() || !modulesInitialized())
return;
PyGILState_STATE state = PyGILState_Ensure();
PyObject* dict = PyModule_GetDict(mainModule_.ptr());
PyDict_Clear(dict);
PyDict_Update(dict, global_dict_clean_);
PyGILState_Release(state);
}
bool PythonInterpreter::runScript(QString _script) {
std::cerr << "runScript" << std::endl;
// init
try
{
std::cerr << "Run Init Python" << std::endl;
initPython();
std::cerr << ".. Done" << std::endl;
}
catch (py::error_already_set &e)
{
pyError(e.what());
return false;
}
PyGILState_STATE state = PyGILState_Ensure();
PyThreadState* tstate = PyGILState_GetThisThreadState();
auto locals = mainModule_.attr("__dict__");
bool result = true;
try
{
std::cerr << "Now executing script" << std::endl;
py::exec((const char*)_script.toLatin1(), py::globals(), locals);
std::cerr << "Finished successfully" << std::endl;
}
catch (py::error_already_set &e)
{
pyError(e.what());
e.restore();
result = false;
}
catch (const std::runtime_error &e)
{
pyError(e.what());
pyOutput("Restarting Interpreter.");
resetInterpreter();
result = false;
state = PyGILState_Ensure();
}
PyGILState_Release(state);
return result;
}
// Python callback functions
void PythonInterpreter::pyOutput(const char* w)
{
std::cerr << "Python output:\n " << w << std::endl;
Q_EMIT log(LOGOUT, QString(w));
}
void PythonInterpreter::pyError(const char* w)
{
std::cerr << "Python error:\n " << w << std::endl;
Q_EMIT log(LOGERR, QString(w));
}
int add(int i, int j) {
return i + j;
}
//import openflipper
//
//a = openflipper.core()
//
//a.updateView()
//print("AAAAAAAAAAAA")
//a = openflipper.core()
//
//print("BBBBBBBBBB")
//a.updateView()
//print("CCCCCCCCCc")
PYBIND11_EMBEDDED_MODULE(openflipper, m) {
py::class_<Core> core(m, "core");
// core
// .def("__init__",
// [](Core &instance) {
// return core_;
// } );
core.def(py::init([]() { return core_; }));
// core.def("__del__",
// +[](const Core&) -> void {
// std::cerr << "deleting Core" << std::endl;
// });
core.def("updateView", &Core::updateView);
core.def("updateView", &Core::updateView);
core.def("clearAll", &Core::clearAll);
// m.def("targets", &targets);
// m.def("sources", &sources);
// m.def("meshes", &meshes);
//
// m.def("updateView", &Core::updateView);
// m.def("clearAll", &Core::clearAll);
std::cerr << "CorePointer : " << core_ << std::endl;;
}
PYBIND11_EMBEDDED_MODULE(examples, m) {
m.doc() = "pybind11 example plugin"; // optional module docstring
m.def("add", &add, "A function which adds two numbers");
}
/*===========================================================================*\
* *
* OpenFlipper *
* Copyright (c) 2001-2015, RWTH-Aachen University *
* Department of Computer Graphics and Multimedia *
* All rights reserved. *
* www.openflipper.org *
* *
*---------------------------------------------------------------------------*
* This file is part of OpenFlipper. *
*---------------------------------------------------------------------------*
* *
* Redistribution and use in source and binary forms, with or without *
* modification, are permitted provided that the following conditions *
* are met: *
* *
* 1. Redistributions of source code must retain the above copyright notice, *
* this list of conditions and the following disclaimer. *
* *
* 2. Redistributions in binary form must reproduce the above copyright *
* notice, this list of conditions and the following disclaimer in the *
* documentation and/or other materials provided with the distribution. *
* *
* 3. Neither the name of the copyright holder nor the names of its *
* contributors may be used to endorse or promote products derived from *
* this software without specific prior written permission. *
* *
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS *
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED *
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A *
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER *
* OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, *
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, *
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR *
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF *
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING *
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS *
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *
* *
\*===========================================================================*/
#pragma once
#include <OpenFlipper/Core/Core.hh>
/** \brief This class provides OpenFlippers Python interpreter
*
*/
class PythonInterpreter : public QObject {
Q_OBJECT
signals:
/// Log with OUT,WARN or ERR as type
void log(Logtype _type , QString _message );
/// Default logging as OUT
void log(QString _message );
public:
~PythonInterpreter();
/** \brief Creates or returns an instance of the interpreter
*
* There will only be one instance of the PythonInterpreter to ensure
* that we only run one context.
*
* @return
*/
static PythonInterpreter* getInstance();
bool runScript(QString _script);
private:
/** \brief private constructor because of singleton
*
*/
PythonInterpreter();
/** \brief Initialize OpenFlipper Python Interpreter
*
*/
void initPython();
/** \brief Resets the interpreter and all states
*
*/
void resetInterpreter();
bool modulesInitialized();
/** \brief Callback to redirect cout log to OpenFlipper logger
*
* @param w Log string
*/
void pyOutput(const char* w);
/** \brief Callback to redirect cerr log to OpenFlipper logger
*
* @param w Log string
*/
void pyError(const char* w);
};
/** \brief give Core pointer to PythonInterpreter for exporting of Core functionality to python
*
* This function is called by OpenFlipper.cc to pass a pointer of the OpenFlipper Core to
* the python side. This allows the PythonInterpreter class to export functions from the core
* and call them directly.
*
* @param _core Pointer to OpenFlipper application core
*/
void setCorePointer( Core* _core );
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment