@@ -631,7 +631,6 @@ void Python_context::set_python_error(const shcore::Exception &exc,
631
631
632
632
void Python_context::set_python_error (PyObject *obj,
633
633
const std::string &location) {
634
- log_info (" Python error: %s" , location.c_str ());
635
634
PyErr_SetString (obj, location.c_str ());
636
635
}
637
636
@@ -1139,38 +1138,123 @@ Value Python_context::execute_module(const std::string &file_name,
1139
1138
return ret_val;
1140
1139
}
1141
1140
1142
- bool Python_context::load_plugin (const std::string &file_name) {
1143
- bool ret_val = true ;
1141
+ bool Python_context::load_plugin (const Plugin_definition &plugin) {
1144
1142
WillEnterPython lock;
1143
+ bool ret_val = false ;
1145
1144
shcore::Scoped_naming_style ns (shcore::NamingStyle::LowerCaseUnderscores);
1146
1145
1147
- const auto file = fopen (file_name.c_str (), " r" );
1146
+ PyObject *plugin_file = nullptr ;
1147
+ PyObject *plugin_path = nullptr ;
1148
+ PyObject *parent_plugin_path = nullptr ;
1149
+ PyObject *result = nullptr ;
1150
+ PyObject *globals = nullptr ;
1151
+ PyObject *sys_path_backup = nullptr ;
1152
+ FILE *file = nullptr ;
1153
+ bool executed = false ;
1154
+ std::string file_name = plugin.file ;
1155
+ try {
1156
+ file = fopen (file_name.c_str (), " r" );
1157
+ if (nullptr == file) {
1158
+ throw std::runtime_error (shcore::str_format (
1159
+ " Failed opening the file: %s" , errno_to_string (errno).c_str ()));
1160
+ }
1161
+
1162
+ plugin_file = PyString_FromString (file_name.c_str ());
1163
+ if (nullptr == plugin_file) {
1164
+ throw std::runtime_error (" Unable to get plugin file" );
1165
+ }
1166
+
1167
+ std::string plugin_dir = shcore::path::dirname (file_name);
1168
+ plugin_path = PyString_FromString (plugin_dir.c_str ());
1169
+ if (nullptr == plugin_path) {
1170
+ throw std::runtime_error (" Unable to get plugin path" );
1171
+ }
1172
+
1173
+ std::string parent_plugin_dir;
1174
+ if (!plugin.main ) {
1175
+ parent_plugin_dir = shcore::path::dirname (plugin_dir);
1176
+ parent_plugin_path = PyString_FromString (parent_plugin_dir.c_str ());
1177
+ if (nullptr == parent_plugin_path) {
1178
+ throw std::runtime_error (" Unable to get the parent plugin path" );
1179
+ }
1180
+ }
1181
+
1182
+ PyObject *sys_path = PySys_GetObject (const_cast <char *>(" path" ));
1183
+ if (nullptr == sys_path) {
1184
+ throw std::runtime_error (" Failed getting sys.path" );
1185
+ }
1186
+
1187
+ // copy globals, so executed scripts does not pollute global
1188
+ // namespace
1189
+ globals = PyDict_Copy (_globals);
1190
+ if (PyDict_SetItemString (globals, " __file__" , plugin_file)) {
1191
+ throw std::runtime_error (" Failed setting __file__" );
1192
+ }
1193
+
1194
+ // Backups the original sys.path, in case the plugin load fails sys.path
1195
+ // will be set back to the original content before the plugin load
1196
+ sys_path_backup = PyList_GetSlice (sys_path, 0 , PyList_Size (sys_path));
1197
+ if (nullptr == sys_path_backup) {
1198
+ std::string error = fetch_and_clear_exception ();
1199
+
1200
+ throw std::runtime_error (
1201
+ shcore::str_format (" Failed backing up sys.path: %s" , error.c_str ()));
1202
+ }
1203
+
1204
+ if (PyList_Insert (sys_path, 0 , plugin_path)) {
1205
+ throw std::runtime_error (" Failed adding plugin path to sys.path" );
1206
+ }
1207
+
1208
+ if (!plugin.main ) {
1209
+ if (PyList_Insert (sys_path, 0 , parent_plugin_path)) {
1210
+ throw std::runtime_error (
1211
+ " Failed adding parent plugin path to sys.path" );
1212
+ }
1213
+ }
1148
1214
1149
- if (nullptr == file) {
1150
- ret_val = false ;
1151
- log_error (" Error loading Python file '%s':\n\t Failed opening the file: %s" ,
1152
- file_name.c_str (), errno_to_string (errno).c_str ());
1153
- } else {
1154
- // copy globals, so executed scripts does not pollute global namespace
1155
- PyObject *globals = PyDict_Copy (_globals);
1156
1215
// PyRun_FileEx() will close the file
1157
- // plugins are loaded in sandboxed environment, we're not using the
1158
- // compiler flags here
1159
- const auto result =
1160
- PyRun_FileEx (file, shcore::path::basename (file_name).c_str (),
1161
- Py_file_input, globals, nullptr , true );
1216
+ // plugins are loaded in sandboxed environment, we're not using
1217
+ // the compiler flags here
1218
+ result = PyRun_FileEx (file, shcore::path::basename (file_name).c_str (),
1219
+ Py_file_input, globals, nullptr , true );
1220
+
1221
+ executed = true ;
1162
1222
1223
+ // In case of an error, gets the description and clears the error condition
1224
+ // in python to be able to restore the sys.path
1225
+ std::string error;
1163
1226
if (nullptr == result) {
1164
- ret_val = false ;
1165
- log_error (" Error loading Python file '%s':\n\t Execution failed: %s" ,
1166
- file_name.c_str (), fetch_and_clear_exception ().c_str ());
1227
+ error = fetch_and_clear_exception ();
1167
1228
}
1168
1229
1169
- Py_XDECREF (result);
1170
- Py_XDECREF (globals);
1230
+ // The plugin paths are required on sys.path at the moment the plugin is
1231
+ // loaded, after that they are not required so the original sys.path is
1232
+ // restored
1233
+ PySys_SetObject (const_cast <char *>(" path" ), sys_path_backup);
1234
+
1235
+ // If there was an error, raises the corresponding exception
1236
+ if (nullptr == result) {
1237
+ throw std::runtime_error (
1238
+ shcore::str_format (" Execution failed: %s" , error.c_str ()));
1239
+ } else {
1240
+ ret_val = true ;
1241
+ }
1242
+ } catch (const std::runtime_error &error) {
1243
+ log_error (" Error loading Python file '%s':\n\t %s" , file_name.c_str (),
1244
+ error.what ());
1245
+
1246
+ if (file && !executed) {
1247
+ fclose (file);
1248
+ }
1171
1249
}
1250
+ Py_XDECREF (plugin_file);
1251
+ Py_XDECREF (result);
1252
+ Py_XDECREF (globals);
1253
+ Py_XDECREF (plugin_path);
1254
+ Py_XDECREF (sys_path_backup);
1255
+
1172
1256
return ret_val;
1173
- }
1257
+ } // namespace shcore
1174
1258
1175
1259
std::string Python_context::fetch_and_clear_exception () {
1176
1260
std::string exception ;
0 commit comments