diff --git a/ci/update_ci.sh b/ci/update_ci.sh index 93ebb0e0..ef8ab197 100755 --- a/ci/update_ci.sh +++ b/ci/update_ci.sh @@ -32,11 +32,11 @@ function windows () { if [ "$PLATFORM" == "x86" ]; then arch=i686 fi - sh -c "pacman -S --noconfirm git mingw-w64-${arch}-cmake make mingw-w64-${arch}-toolchain mingw-w64-${arch}-clang mingw-w64-${arch}-gtkmm3 mingw-w64-${arch}-gtksourceviewmm3 mingw-w64-${arch}-boost mingw-w64-${arch}-aspell mingw-w64-${arch}-aspell-en mingw-w64-${arch}-libgit2" + sh -c "pacman -S --noconfirm git mingw-w64-${arch}-pygobject-devel mingw-w64-${arch}-cmake make mingw-w64-${arch}-toolchain mingw-w64-${arch}-clang mingw-w64-${arch}-gtkmm3 mingw-w64-${arch}-gtksourceviewmm3 mingw-w64-${arch}-boost mingw-w64-${arch}-aspell mingw-w64-${arch}-aspell-en mingw-w64-${arch}-libgit2" } if [ "$TRAVIS_OS_NAME" == "" ]; then TRAVIS_OS_NAME=windows fi -$TRAVIS_OS_NAME \ No newline at end of file +$TRAVIS_OS_NAME diff --git a/docs/install.md b/docs/install.md index 41b74055..6d8496a2 100644 --- a/docs/install.md +++ b/docs/install.md @@ -138,7 +138,7 @@ make install Install dependencies (replace `x86_64` with `i686` for 32-bit MSYS2 installs): ```sh -pacman -S git mingw-w64-x86_64-cmake make mingw-w64-x86_64-toolchain mingw-w64-x86_64-clang mingw-w64-x86_64-gtkmm3 mingw-w64-x86_64-gtksourceviewmm3 mingw-w64-x86_64-boost mingw-w64-x86_64-aspell mingw-w64-x86_64-aspell-en mingw-w64-x86_64-libgit2 mingw-w64-x86_64-universal-ctags-git +pacman -S git mingw-w64-x86_64-cmake make mingw-w64-x86_64-toolchain mingw-w64-x86_64-clang mingw-w64-x86_64-gtkmm3 mingw-w64-x86_64-gtksourceviewmm3 mingw-w64-x86_64-boost mingw-w64-x86_64-aspell mingw-w64-x86_64-aspell-en mingw-w64-x86_64-gobject-introspection mingw-w64-x86_64-pygobject-devel mingw-w64-x86_64-python3-gobject mingw-w64-x86_64-libgit2 mingw-w64-x86_64-universal-ctags-git ``` Note that juCi++ must be built and run in a MinGW Shell (for instance MinGW-w64 Win64 Shell). diff --git a/pybind11 b/pybind11 index d2385e8f..07de0d86 160000 --- a/pybind11 +++ b/pybind11 @@ -1 +1 @@ -Subproject commit d2385e8fc6a3008cab96532c99db4c3d57541fc5 +Subproject commit 07de0d8627101be53986e841cd4e21ee38c2498a diff --git a/src/config.cc b/src/config.cc index 01380bc6..c2ee7a4e 100644 --- a/src/config.cc +++ b/src/config.cc @@ -56,7 +56,7 @@ void Config::find_or_create_config_files() { auto config_json = config_dir/"config.json"; boost::filesystem::create_directories(config_dir); // io exp captured by calling method - boost::filesystem::create_directories(home/"plugins"); + boost::filesystem::create_directories(home_juci_path/"plugins"); if (!boost::filesystem::exists(config_json)) filesystem::write(config_json, default_config_file); @@ -118,8 +118,8 @@ void Config::retrieve_config() { } } #endif - python.plugin_directory=cfg.get("python.plugin_directory",(home/"plugins").string()); - python.site_packages=cfg.get("python.site_packages","/usr/lib/python3.5/site-packages"); + python.plugin_directory=cfg.get("python.plugin_directory",(home_juci_path/"plugins").string()); + python.site_packages=cfg.get("python.site_packages",""); } bool Config::add_missing_nodes(const boost::property_tree::ptree &default_cfg, std::string parent_path) { diff --git a/src/menu.cc b/src/menu.cc index 7c7147af..8317f01e 100644 --- a/src/menu.cc +++ b/src/menu.cc @@ -478,6 +478,8 @@ void Menu::set_keys() { } } +Menu::Menu() : plugin_menu(nullptr) {} + void Menu::build() { try { builder = Gtk::Builder::create_from_string(menu_xml); diff --git a/src/menu.h b/src/menu.h index 752b7ceb..a6a091cc 100644 --- a/src/menu.h +++ b/src/menu.h @@ -6,7 +6,8 @@ #include class Menu { - Menu() {} +private: + Menu(); public: static Menu &get() { static Menu singleton; @@ -21,11 +22,9 @@ class Menu { Glib::RefPtr juci_menu; Glib::RefPtr window_menu; - std::unique_ptr right_click_line_menu; std::unique_ptr right_click_selected_menu; Glib::RefPtr plugin_menu; - private: Glib::RefPtr builder; }; diff --git a/src/python_interpreter.cc b/src/python_interpreter.cc index af423590..5dff081a 100644 --- a/src/python_interpreter.cc +++ b/src/python_interpreter.cc @@ -20,24 +20,17 @@ static wchar_t* DecodeLocale(const char* arg, size_t *size) inline pybind11::module pyobject_from_gobj(gpointer ptr){ auto obj=G_OBJECT(ptr); if(obj) - return pybind11::module(pygobject_new(obj), false); - return pybind11::module(Py_None, false); + return pybind11::reinterpret_steal(pygobject_new(obj)); + return pybind11::reinterpret_steal(Py_None); } Python::Interpreter::Interpreter(){ -#ifdef _WIN32 - auto root_path=Config::get().terminal.msys2_mingw_path; - append_path(root_path/"include/python3.5m"); - append_path(root_path/"lib/python3.5"); - long long unsigned size = 0L; -#else - long unsigned size = 0L; -#endif + auto init_juci_api=[](){ - pybind11::module(pygobject_init(-1,-1,-1),false); + auto module = pybind11::reinterpret_steal(pygobject_init(-1,-1,-1)); pybind11::module api("jucpp","Python bindings for juCi++"); api - .def("get_juci_home",[](){return Config::get().juci_home_path().string();}) + .def("get_juci_home",[](){return Config::get().home_juci_path.string();}) .def("get_plugin_folder",[](){return Config::get().python.plugin_directory;}); api .def_submodule("editor") @@ -45,7 +38,7 @@ Python::Interpreter::Interpreter(){ auto view=Notebook::get().get_current_view(); if(view) return pyobject_from_gobj(view->gobj()); - return pybind11::module(Py_None,false); + return pybind11::reinterpret_steal(Py_None); }) .def("get_file_path",[](){ auto view=Notebook::get().get_current_view(); @@ -55,13 +48,12 @@ Python::Interpreter::Interpreter(){ }); api .def("get_gio_plugin_menu",[](){ - auto &plugin_menu=Menu::get().plugin_menu; - if(!plugin_menu){ - plugin_menu=Gio::Menu::create(); - plugin_menu->append(""); - Menu::get().window_menu->append_submenu("_Plugins",plugin_menu); + if(!Menu::get().plugin_menu){ + Menu::get().plugin_menu=Gio::Menu::create(); + Menu::get().plugin_menu->append(""); + Menu::get().window_menu->append_submenu("_Plugins",Menu::get().plugin_menu); } - return pyobject_from_gobj(plugin_menu->gobj()); + return pyobject_from_gobj(Menu::get().plugin_menu->gobj()); }) .def("get_gio_window_menu",[](){return pyobject_from_gobj(Menu::get().window_menu->gobj());}) .def("get_gio_juci_menu",[](){return pyobject_from_gobj(Menu::get().juci_menu->gobj());}) @@ -76,129 +68,87 @@ Python::Interpreter::Interpreter(){ return api.ptr(); }; PyImport_AppendInittab("jucipp", init_juci_api); + Config::get().load(); - auto plugin_path=Config::get().python.plugin_directory; - add_path(Config::get().python.site_packages); - add_path(plugin_path); + configure_path(); Py_Initialize(); + #ifdef _WIN32 + long long unsigned size = 0L; + #else + long unsigned size = 0L; + #endif argv=DecodeLocale("",&size); PySys_SetArgv(0,&argv); - auto sys=get_loaded_module("sys"); - auto exc_func=[](pybind11::object type,pybind11::object value,pybind11::object traceback){ - if(!given_exception_matches(type,PyExc_SyntaxError)) - Terminal::get().print(Error(type,value,traceback),true); - else - Terminal::get().print(SyntaxError(type,value,traceback),true); - }; - sys.attr("excepthook")=pybind11::cpp_function(exc_func); boost::filesystem::directory_iterator end_it; - for(boost::filesystem::directory_iterator it(plugin_path);it!=end_it;it++){ + for(boost::filesystem::directory_iterator it(Config::get().python.plugin_directory);it!=end_it;it++){ auto module_name=it->path().stem().string(); if(module_name.empty()) - break; + continue; auto is_directory=boost::filesystem::is_directory(it->path()); auto has_py_extension=it->path().extension()==".py"; auto is_pycache=module_name=="__pycache__"; if((is_directory && !is_pycache)||has_py_extension){ - auto module=import(module_name); - if(!module){ - auto msg="Error loading plugin `"+module_name+"`:\n"; - auto err=std::string(Error()); - Terminal::get().print(msg+err+"\n"); + try { + pybind11::module::import(module_name.c_str()); + } catch (pybind11::error_already_set &error) { + Terminal::get().print("Error loading plugin `"+module_name+"`:\n"+error.what()+"\n"); } } } + auto sys=find_module("sys"); + if(sys){ + auto exc_func=[](pybind11::object type,pybind11::object value,pybind11::object traceback){ + std::cerr << "ERROR FUNCTION"; + }; + sys.attr("excepthook")=pybind11::cpp_function(exc_func); + } else { + std::cerr << "Failed to set exception hook\n"; + } } -pybind11::module Python::get_loaded_module(const std::string &module_name){ - return pybind11::module(PyImport_AddModule(module_name.c_str()), true); -} - -pybind11::module Python::import(const std::string &module_name){ - return pybind11::module(PyImport_ImportModule(module_name.c_str()), false); -} - -pybind11::module Python::reload(pybind11::module &module){ - return pybind11::module(PyImport_ReloadModule(module.ptr()),false); +pybind11::module Python::Interpreter::find_module(const std::string &module_name){ + return pybind11::reinterpret_borrow(PyImport_AddModule(module_name.c_str())); } -Python::SyntaxError::SyntaxError(pybind11::object type,pybind11::object value,pybind11::object traceback) -: Error(type,value,traceback){} - -Python::Error::Error(pybind11::object type,pybind11::object value,pybind11::object traceback){ - exp=type; - val=value; - trace=traceback; +pybind11::module Python::Interpreter::reload(pybind11::module &module){ + auto reload=pybind11::reinterpret_steal(PyImport_ReloadModule(module.ptr())); + if(!reload) + throw pybind11::error_already_set(); + return reload; } -void Python::Interpreter::add_path(const boost::filesystem::path &path){ - if(path.empty()) - return; - std::wstring sys_path(Py_GetPath()); - if(!sys_path.empty()) -#ifdef _WIN32 - sys_path += ';'; -#else - sys_path += ':'; -#endif - sys_path += path.generic_wstring(); +void Python::Interpreter::configure_path(){ + const std::vector python_path = { + "/usr/lib/python3.6", + "/usr/lib/python3.6/lib-dynload", + "/usr/lib/python3.6/site-packages", + Config::get().python.site_packages, + Config::get().python.plugin_directory + }; + std::wstring sys_path; + for(auto &path:python_path){ + if(path.empty()) + continue; + if(!sys_path.empty()){ + #ifdef _WIN32 + sys_path += ';'; + #else + sys_path += ':'; + #endif + } + sys_path += path.generic_wstring(); + } Py_SetPath(sys_path.c_str()); } Python::Interpreter::~Interpreter(){ - auto err=Error(); if(Py_IsInitialized()) Py_Finalize(); - if(err) - std::cerr << std::string(err) << std::endl; + if(error()) + std::cerr << pybind11::error_already_set().what() << std::endl; } -pybind11::object Python::error_occured(){ - return pybind11::object(PyErr_Occurred(),true); +pybind11::object Python::Interpreter::error(){ + return pybind11::reinterpret_borrow(PyErr_Occurred()); } -bool Python::thrown_exception_matches(pybind11::handle exception_type){ - return PyErr_ExceptionMatches(exception_type.ptr()); -} - -bool Python::given_exception_matches(const pybind11::object &exception, pybind11::handle exception_type){ - return PyErr_GivenExceptionMatches(exception.ptr(),exception_type.ptr()); -} - -Python::Error::Error(){ - if(error_occured()){ - try{ - PyErr_Fetch(&exp.ptr(),&val.ptr(),&trace.ptr()); - PyErr_NormalizeException(&exp.ptr(),&val.ptr(),&trace.ptr()); - }catch(const std::exception &e) { - Terminal::get().print(e.what(),true); - } - } -} - -Python::Error::operator std::string(){ - return std::string(exp.str())+"\n"+std::string(val.str())+"\n"; -} - -Python::SyntaxError::SyntaxError():Error(){ - if(val){ - _Py_IDENTIFIER(msg); - _Py_IDENTIFIER(lineno); - _Py_IDENTIFIER(offset); - _Py_IDENTIFIER(text); - exp=std::string(pybind11::str(_PyObject_GetAttrId(val.ptr(),&PyId_msg),false)); - text=std::string(pybind11::str(_PyObject_GetAttrId(val.ptr(),&PyId_text),false)); - pybind11::object py_line_number(_PyObject_GetAttrId(val.ptr(),&PyId_lineno),false); - pybind11::object py_line_offset(_PyObject_GetAttrId(val.ptr(),&PyId_offset),false); - line_number=pybind11::cast(py_line_number); - line_offset=pybind11::cast(py_line_offset); - } -} - -Python::SyntaxError::operator std::string(){ - return exp+" ("+std::to_string(line_number)+":"+std::to_string(line_offset)+"):\n"+text; -} - -Python::Error::operator bool(){ - return exp || trace || val; -} diff --git a/src/python_interpreter.h b/src/python_interpreter.h index 0853793b..b30ef1ae 100644 --- a/src/python_interpreter.h +++ b/src/python_interpreter.h @@ -7,48 +7,23 @@ #include using namespace std; -class Python { - public: +namespace Python { class Interpreter { + public: + pybind11::module static find_module(const std::string &module_name); + pybind11::module static reload(pybind11::module &module); + pybind11::object static error(); private: Interpreter(); ~Interpreter(); wchar_t *argv; - void add_path(const boost::filesystem::path &path); + void configure_path(); public: static Interpreter& get(){ static Interpreter singleton; return singleton; } }; - - pybind11::module static get_loaded_module(const std::string &module_name); - pybind11::module static import(const std::string &module_name); - pybind11::module static reload(pybind11::module &module); - - class Error { - public: - Error(); - Error(pybind11::object type,pybind11::object value,pybind11::object traceback); - operator std::string(); - operator bool(); - pybind11::object exp, val, trace; - }; - - class SyntaxError : public Error{ - public: - SyntaxError(); - SyntaxError(pybind11::object type,pybind11::object value,pybind11::object traceback); - operator std::string(); - std::string exp, text; - int line_number, line_offset; - }; - - bool static thrown_exception_matches(pybind11::handle exception_type); - bool static given_exception_matches(const pybind11::object &exception,pybind11::handle exception_type); - -private: - pybind11::object static error_occured(); }; #endif // JUCI_PYTHON_INTERPRETER_H_ diff --git a/src/window.cc b/src/window.cc index 97bd621e..73387ac5 100644 --- a/src/window.cc +++ b/src/window.cc @@ -396,26 +396,36 @@ void Window::set_menu_actions() { Notebook::get().get_view(c)->configure(); Notebook::get().configure(c); } - if(view->file_path.extension().string()==".py"){ - auto file_path=view->file_path; - while(file_path.has_parent_path()){ - auto parent=file_path.parent_path(); - if(parent==Config::get().python.plugin_directory){ - auto stem=file_path.stem().string(); - auto module=Python::get_loaded_module(stem); - module=module ? Python::reload(module) : Python::import(stem); - if(module) - Terminal::get().print("Plugin `"+stem+"` was reloaded\n"); - else { - if(Python::thrown_exception_matches(PyExc_SyntaxError)) - Terminal::get().print(Python::SyntaxError()); - else - Terminal::get().print(Python::Error()); - } - break; - } - file_path=parent; + } + const auto refresh_module = [](const std::string &stem){ + auto module = Python::Interpreter::find_module(stem); + if(module) { + try { + module = pybind11::reinterpret_steal(PyImport_ReloadModule(module.ptr())); + } catch (const pybind11::error_already_set &error) { + Terminal::get().print("Plugin `"+stem+"` didn't reload\n"+error.what()+"\n"); + } + } else { + { pybind11::error_already_set(); } + try { + module = pybind11::module::import(stem.c_str()); + } catch (const pybind11::error_already_set &error) { + Terminal::get().print("Plugin `"+stem+"` didn't reload\n"+error.what()+"\n"); + } + } + if(module) + Terminal::get().print("Plugin `"+stem+"` was reloaded\n"); + }; + if(view->file_path.extension().string()==".py"){ + auto file_path=view->file_path; + while(file_path.has_parent_path()){ + auto parent=file_path.parent_path(); + if(parent==Config::get().python.plugin_directory){ + auto stem=file_path.stem().string(); + refresh_module(stem); + break; } + file_path=parent; } } }