| 356 | } |
| 357 | |
| 358 | std::vector<long long> runPythonHookContextSnippet(const std::string& registration_script, |
| 359 | const lfs::training::HookContext& snapshot_ctx, |
| 360 | const lfs::training::HookContext& callback_ctx) { |
| 361 | lfs::event::CommandCenterBridge::instance().set(&lfs::training::CommandCenter::instance()); |
| 362 | lfs::training::ControlBoundary::instance().clear_all(); |
| 363 | lfs::training::CommandCenter::instance().update_snapshot( |
| 364 | snapshot_ctx, |
| 365 | /*max_iterations=*/5000, |
| 366 | /*is_paused=*/false, |
| 367 | /*is_running=*/true, |
| 368 | /*stop_requested=*/false, |
| 369 | lfs::training::TrainingPhase::SafeControl); |
| 370 | |
| 371 | PyObject* globals = nullptr; |
| 372 | { |
| 373 | const lfs::python::GilAcquire gil; |
| 374 | globals = PyDict_New(); |
| 375 | if (!globals) { |
| 376 | throw std::runtime_error("Failed to allocate Python globals"); |
| 377 | } |
| 378 | |
| 379 | PyDict_SetItemString(globals, "__builtins__", PyEval_GetBuiltins()); |
| 380 | PyObject* exec_result = PyRun_String(registration_script.c_str(), Py_file_input, globals, globals); |
| 381 | if (!exec_result) { |
| 382 | const auto error = consumePythonError(); |
| 383 | Py_DECREF(globals); |
| 384 | throw std::runtime_error(error); |
| 385 | } |
| 386 | Py_DECREF(exec_result); |
| 387 | } |
| 388 | |
| 389 | lfs::training::ControlBoundary::instance().notify( |
| 390 | lfs::training::ControlHook::PostStep, |
| 391 | callback_ctx); |
| 392 | lfs::training::ControlBoundary::instance().drain_callbacks(); |
| 393 | |
| 394 | std::vector<long long> result; |
| 395 | { |
| 396 | const lfs::python::GilAcquire gil; |
| 397 | auto* records_obj = PyDict_GetItemString(globals, "records"); |
| 398 | if (!records_obj || !PyList_Check(records_obj) || PyList_Size(records_obj) != 1) { |
| 399 | lfs::training::ControlBoundary::instance().clear_all(); |
| 400 | Py_DECREF(globals); |
| 401 | throw std::runtime_error("Hook script did not record exactly one callback invocation"); |
| 402 | } |
| 403 | |
| 404 | auto* record = PyList_GetItem(records_obj, 0); |
| 405 | if (!record || !PyTuple_Check(record)) { |
| 406 | lfs::training::ControlBoundary::instance().clear_all(); |
| 407 | Py_DECREF(globals); |
| 408 | throw std::runtime_error("Recorded hook result is not a tuple"); |
| 409 | } |
| 410 | |
| 411 | result.reserve(static_cast<size_t>(PyTuple_Size(record))); |
| 412 | for (Py_ssize_t i = 0; i < PyTuple_Size(record); ++i) { |
| 413 | result.push_back(PyLong_AsLongLong(PyTuple_GetItem(record, i))); |
| 414 | } |
| 415 |
no test coverage detected