127{
128 PythonGILState gil;
129 if (!gil) {
130 error = "Python interpreter is shutting down";
131 return {};
132 }
133 BOOST_LOG_TRIVIAL(info) << "Finalizing Python plugin capture for key " << capture_key;
134
135
136
137
138
139 {
140 auto clear_active_key = [&capture_key]() {
141 if (g_active_plugin_key == capture_key)
143 };
144 auto discard_pending_for_key = [&capture_key]() {
145 std::lock_guard<std::mutex>
lock(g_registry_mutex);
149 };
150
151 try {
152
153
154
155 py::object package_cls;
156 {
157 std::lock_guard<std::mutex>
lock(g_registry_mutex);
160 package_cls = it->second;
162 }
163 }
164 if (!package_cls) {
165 error = "Plugin did not register a package class; decorate it with @orca.plugin";
166 BOOST_LOG_TRIVIAL(error) << error << " for key " << plugin_key;
167 discard_pending_for_key();
168 clear_active_key();
169 return {};
170 }
171
172
173
174
175 py::object package = package_cls();
176 package.attr("register_capabilities")();
177
178
179
180
181 PluginPermissions requested_permissions;
182 {
183 std::lock_guard<std::mutex>
lock(g_registry_mutex);
186 requested_permissions = std::move(it->second);
188 }
189 }
190 const bool has_permission_request =
191 !requested_permissions.fs_read.empty() || !requested_permissions.fs_readwrite.empty() ||
192 !requested_permissions.network_http.empty() || !requested_permissions.network_socket.empty() ||
193 !requested_permissions.process.empty() || !requested_permissions.threading.empty();
194 if (has_permission_request) {
195 bool granted = false;
196 {
197 py::gil_scoped_release release;
198 granted = PluginAuditManager::instance().request_permissions(plugin_key, requested_permissions);
199 }
200 if (!granted) {
201 error =
_u8L(
"Plugin permission request denied");
202 BOOST_LOG_TRIVIAL(warning) << error << " for key " << plugin_key;
203 discard_pending_for_key();
204 clear_active_key();
205 return {};
206 }
207 }
208 } catch (py::error_already_set& err) {
210 error = err.what();
211 BOOST_LOG_TRIVIAL(error) << "Plugin register_capabilities raised Python exception for key " << plugin_key
212 << " error=" << error;
213 discard_pending_for_key();
214 clear_active_key();
215 return {};
216 } catch (const std::exception& ex) {
217 error = ex.what();
218 BOOST_LOG_TRIVIAL(error) << "Plugin register_capabilities raised exception for key " << plugin_key
219 << " error=" << error;
220 discard_pending_for_key();
221 clear_active_key();
222 return {};
223 }
224 }
225
226
227
228 std::vector<py::object> classes;
229 {
230 std::lock_guard<std::mutex>
lock(g_registry_mutex);
233 classes = std::move(it->second);
235 }
236 }
237
238
239
240 if (g_active_plugin_key == capture_key)
242
243 BOOST_LOG_TRIVIAL(info) << "Collected " << classes.size() << " registered capability class(es) for key " << capture_key;
244
245 std::vector<CapturedCapability> capabilities;
246 capabilities.reserve(classes.size());
247
248
249
250 for (auto& cls : classes) {
251 try {
253 if (!py::isinstance<PluginCapabilityInterface>(
instance)) {
254 error = "Registered capability must inherit from a PluginCapability base";
255 BOOST_LOG_TRIVIAL(error) << "Python plugin capture failed type check for key " << capture_key
256 << " error=" << error;
257 return {};
258 }
259
260 auto capability_iface =
instance.cast<std::shared_ptr<PluginCapabilityInterface>>();
261 if (!capability_iface) {
262 error = "Failed to cast Python capability to PluginCapabilityInterface";
263 BOOST_LOG_TRIVIAL(error) << "Python plugin capture failed cast for key " << capture_key
264 << " error=" << error;
265 return {};
266 }
267
268
269
270 std::string name = capability_iface->get_name();
271
272
273
274
275 if (name.find(';') != std::string::npos) {
276 error = "Capability name must not contain ';': " + name;
277 BOOST_LOG_TRIVIAL(error) << "Python plugin capture rejected capability for key " << capture_key
278 << " error=" << error;
279 return {};
280 }
281
282 auto handle = std::make_shared<PluginInstanceHandle>();
284 handle->plugin = std::move(capability_iface);
285
286 CapturedCapability captured;
287
288
289 captured.instance = std::shared_ptr<PluginCapabilityInterface>(handle, handle->plugin.get());
290 captured.name = std::move(name);
291 capabilities.emplace_back(std::move(captured));
292 } catch (py::error_already_set& err) {
293
294
296 error = err.what();
297 BOOST_LOG_TRIVIAL(error) << "Python plugin capture raised Python exception for key " << capture_key
298 << " error=" << error;
299 return {};
300 } catch (const std::exception& ex) {
301 error = ex.what();
302 BOOST_LOG_TRIVIAL(error) << "Python plugin capture raised exception for key " << capture_key
303 << " error=" << error;
304 return {};
305 }
306 }
307
308 BOOST_LOG_TRIVIAL(info) << "Instantiated " << capabilities.size() << " Python capability instance(s) for key " << plugin_key;
309 return capabilities;
310}
static PythonPluginBridge & instance()
Definition PythonPluginBridge.cpp:97
void log_python_exception_keep(pybind11::error_already_set &err)
Definition PythonInterpreter.cpp:96
#define _u8L(s)
Definition I18N.hpp:11