Skip to content
This repository was archived by the owner on Jun 25, 2020. It is now read-only.

Commit 8dc1fbd

Browse files
committed
feature: better user feedback on common exception types
1 parent a175900 commit 8dc1fbd

File tree

3 files changed

+87
-34
lines changed

3 files changed

+87
-34
lines changed

src/python_interpreter.cc

Lines changed: 52 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -67,20 +67,28 @@ Python::Interpreter::Interpreter(){
6767
auto module_name=it->path().stem().string();
6868
if(module_name!="__pycache__"){
6969
auto module=import(module_name);
70-
if(!module)
71-
std::cerr << std::string(Error()) << std::endl;
70+
if(!module){
71+
auto err=std::string(Error());
72+
auto msg="Error loading plugin "+module_name+":\n";
73+
Terminal::get().print(msg,true);
74+
Terminal::get().print(err+"\n");
75+
}
7276
}
7377
}
7478
}
7579

76-
pybind11::module Python::Interpreter::get_loaded_module(const std::string &module_name){
80+
pybind11::module Python::get_loaded_module(const std::string &module_name){
7781
return pybind11::module(PyImport_AddModule(module_name.c_str()), true);
7882
}
7983

80-
pybind11::module Python::Interpreter::import(const std::string &module_name){
84+
pybind11::module Python::import(const std::string &module_name){
8185
return pybind11::module(PyImport_ImportModule(module_name.c_str()), false);
8286
}
8387

88+
pybind11::module Python::reload(pybind11::module &module){
89+
return pybind11::module(PyImport_ReloadModule(module.ptr()),false);
90+
}
91+
8492
void Python::Interpreter::add_path(const boost::filesystem::path &path){
8593
if(path.empty())
8694
return;
@@ -103,26 +111,54 @@ Python::Interpreter::~Interpreter(){
103111
std::cerr << std::string(err) << std::endl;
104112
}
105113

114+
pybind11::object Python::error_occured(){
115+
return pybind11::object(PyErr_Occurred(),true);
116+
}
117+
118+
bool Python::thrown_exception_matches(Error::Type type){
119+
pybind11::object compare;
120+
switch(type){
121+
case Error::Type::Syntax : compare=pybind11::object(PyExc_SyntaxError,false);
122+
case Error::Type::Attribute : compare=pybind11::object(PyExc_AttributeError,false);
123+
case Error::Type::Import : compare=pybind11::object(PyExc_ImportError,false);
124+
}
125+
return PyErr_GivenExceptionMatches(Python::error_occured().ptr(), compare.ptr());
126+
}
127+
106128
Python::Error::Error(){
107-
pybind11::object error(PyErr_Occurred(), false);
108-
if(error){
109-
PyObject *exception,*value,*traceback;
110-
PyErr_Fetch(&exception,&value,&traceback);
111-
PyErr_NormalizeException(&exception,&value,&traceback);
129+
if(error_occured()){
112130
try{
113-
exp=std::string(pybind11::object(exception,false).str());
114-
val=std::string(pybind11::object(value,false).str());
115-
trace=std::string(pybind11::object(traceback,false).str());
116-
} catch (const std::runtime_error &e){
117-
exp=e.what();
131+
PyErr_Fetch(&exp.ptr(),&val.ptr(),&trace.ptr());
132+
PyErr_NormalizeException(&exp.ptr(),&val.ptr(),&trace.ptr());
133+
}catch(const std::exception &e) {
134+
Terminal::get().print(e.what());
118135
}
119136
}
120137
}
121138

122139
Python::Error::operator std::string(){
123-
return exp + "\n" + val + "\n" + trace;
140+
return std::string(exp.str())+"\n"+std::string(val.str())+"\n";
141+
}
142+
143+
Python::SyntaxError::SyntaxError():Error(){
144+
if(val){
145+
_Py_IDENTIFIER(msg);
146+
_Py_IDENTIFIER(lineno);
147+
_Py_IDENTIFIER(offset);
148+
_Py_IDENTIFIER(text);
149+
exp=std::string(pybind11::str(_PyObject_GetAttrId(val.ptr(),&PyId_msg),false));
150+
text=std::string(pybind11::str(_PyObject_GetAttrId(val.ptr(),&PyId_text),false));
151+
pybind11::object py_line_number(_PyObject_GetAttrId(val.ptr(),&PyId_lineno),false);
152+
pybind11::object py_line_offset(_PyObject_GetAttrId(val.ptr(),&PyId_offset),false);
153+
line_number=pybind11::cast<int>(py_line_number);
154+
line_offset=pybind11::cast<int>(py_line_offset);
155+
}
156+
}
157+
158+
Python::SyntaxError::operator std::string(){
159+
return exp+" ("+std::to_string(line_number)+":"+std::to_string(line_offset)+"):\n"+text;
124160
}
125161

126162
Python::Error::operator bool(){
127-
return !exp.empty();
163+
return exp;
128164
}

src/python_interpreter.h

Lines changed: 26 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,28 +6,47 @@
66

77
#include <iostream>
88
using namespace std;
9-
namespace Python {
9+
10+
class Python {
11+
public:
1012
class Interpreter {
1113
private:
1214
Interpreter();
1315
~Interpreter();
1416
wchar_t *argv;
17+
void add_path(const boost::filesystem::path &path);
1518
public:
1619
static Interpreter& get(){
1720
static Interpreter singleton;
1821
return singleton;
1922
}
20-
pybind11::module get_loaded_module(const std::string &module_name);
21-
pybind11::module import(const std::string &module_name);
22-
void add_path(const boost::filesystem::path &path);
2323
};
2424

25+
pybind11::module static get_loaded_module(const std::string &module_name);
26+
pybind11::module static import(const std::string &module_name);
27+
pybind11::module static reload(pybind11::module &module);
28+
2529
class Error {
2630
public:
2731
Error();
2832
operator std::string();
2933
operator bool();
30-
std::string exp, val, trace;
34+
pybind11::object exp, val, trace;
35+
enum Type {Syntax,Attribute,Import};
36+
};
37+
38+
class SyntaxError : Error{
39+
public:
40+
SyntaxError();
41+
operator std::string();
42+
std::string exp, text;
43+
int line_number, line_offset;
3144
};
32-
} // namespace Python
33-
#endif // JUCI_PYTHON_INTERPRETER_H_
45+
46+
bool static thrown_exception_matches(Error::Type exception_type);
47+
48+
private:
49+
pybind11::object static error_occured();
50+
};
51+
52+
#endif // JUCI_PYTHON_INTERPRETER_H_

src/window.cc

Lines changed: 9 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -331,17 +331,15 @@ void Window::set_menu_actions() {
331331
}
332332
if(view->file_path>Config::get().python.plugin_directory){
333333
auto stem=view->file_path.stem().string();
334-
auto module=Python::Interpreter::get().get_loaded_module(stem);
335-
if(module){
336-
auto module_new=pybind11::module(PyImport_ReloadModule(module.ptr()),false);
337-
if(module_new)
338-
Terminal::get().print("Python module "+stem + " has been reloaded \n");
339-
else Python::Error();
340-
}else{
341-
Python::Error();
342-
module=Python::Interpreter::get().import(stem);
343-
if(module)
344-
Terminal::get().print("Python module "+stem + " has been reloaded \n");
334+
auto module=Python::get_loaded_module(stem);
335+
module=module ? Python::reload(module) : Python::import(stem);
336+
if(module)
337+
Terminal::get().print("Plugin `"+stem+"` was reloaded\n");
338+
else {
339+
if(Python::thrown_exception_matches(Python::Error::Type::Syntax))
340+
Terminal::get().print(Python::SyntaxError());
341+
else
342+
Terminal::get().print(Python::Error());
345343
}
346344
}
347345
}

0 commit comments

Comments
 (0)