/** * @file wf_c.c * @brief C implementation that embeds Python to call wandb. * * This file has been generated using LLM AI code agents and has not yet been thoroughly tested. * * Uses the Python C API to: * 1. Start an embedded Python interpreter. * 2. Import the `wandb` package. * 3. Forward init / log / config / finish calls to wandb. */ #include "wf_c.h" /* Python.h must come before any standard headers on some platforms. */ #include <Python.h> #include <stdlib.h> #include <stdio.h> #include <string.h> #include <wchar.h> /* ------------------------------------------------------------------ */ /* Module-level state */ /* ------------------------------------------------------------------ */ static int python_started = 0; /* 1 after Py_Initialize() */ static PyObject *wandb_module = NULL; static PyObject *wandb_run = NULL; /* Sweep-agent threading state (lives in '__athena_sweep__' module dict) */ static PyObject *sweep_globals = NULL; /* dict holding threading objects */ /* ------------------------------------------------------------------ */ /* Helper: print the current Python exception, then clear it. */ /* ------------------------------------------------------------------ */ static void print_py_error(const char *ctx) { if (PyErr_Occurred()) { fprintf(stderr, "[wandb_c] Python error in %s:\n", ctx); PyErr_Print(); PyErr_Clear(); } } /* ------------------------------------------------------------------ */ /* Helper: add runtime site-packages from WF_PYTHON_PATH. */ /* ------------------------------------------------------------------ */ static void configure_python_sys_path(void) { const char *python_path = getenv("WF_PYTHON_PATH"); PyObject *sys_path = NULL; char *paths_copy = NULL; char *cursor = NULL; char *entry = NULL; if (!python_path || python_path[0] == '\0') { return; } sys_path = PySys_GetObject("path"); if (!sys_path || !PyList_Check(sys_path)) { fprintf(stderr, "[wandb_c] WARNING: unable to access sys.path for WF_PYTHON_PATH.\n"); return; } paths_copy = strdup(python_path); if (!paths_copy) { fprintf(stderr, "[wandb_c] WARNING: failed to copy WF_PYTHON_PATH.\n"); return; } cursor = paths_copy; while ((entry = strsep(&cursor, ":")) != NULL) { PyObject *py_entry = NULL; int contains = 0; if (entry[0] == '\0') { continue; } py_entry = PyUnicode_FromString(entry); if (!py_entry) { print_py_error("PyUnicode_FromString(sys.path entry)"); continue; } contains = PySequence_Contains(sys_path, py_entry); if (contains == 0) { if (PyList_Append(sys_path, py_entry) != 0) { print_py_error("PyList_Append(sys.path)"); } } else if (contains < 0) { print_py_error("PySequence_Contains(sys.path)"); } Py_DECREF(py_entry); } free(paths_copy); } /* ------------------------------------------------------------------ */ /* Helper: start Python using the interpreter selected by setup_env. */ /* ------------------------------------------------------------------ */ static int ensure_python_started(void) { const char *python_bin = getenv("WF_PYTHON_BIN"); const char *python_home = getenv("WF_PYTHON_HOME"); PyConfig config; PyStatus status; wchar_t *program_name = NULL; wchar_t *home = NULL; int used_config = 0; if (python_started) { return 0; } if ((!python_bin || python_bin[0] == '\0') && (!python_home || python_home[0] == '\0')) { Py_Initialize(); } else { PyConfig_InitPythonConfig(&config); used_config = 1; config.parse_argv = 0; if (python_bin && python_bin[0] != '\0') { program_name = Py_DecodeLocale(python_bin, NULL); if (!program_name) { fprintf(stderr, "[wandb_c] Failed to decode WF_PYTHON_BIN.\n"); PyConfig_Clear(&config); return -1; } status = PyConfig_SetString(&config, &config.program_name, program_name); if (PyStatus_Exception(status)) { fprintf(stderr, "[wandb_c] Failed to configure Python program name: %s\n", status.err_msg ? status.err_msg : "unknown error"); PyMem_RawFree(program_name); PyConfig_Clear(&config); return -1; } } if (python_home && python_home[0] != '\0') { home = Py_DecodeLocale(python_home, NULL); if (!home) { fprintf(stderr, "[wandb_c] Failed to decode WF_PYTHON_HOME.\n"); PyMem_RawFree(program_name); PyConfig_Clear(&config); return -1; } status = PyConfig_SetString(&config, &config.home, home); if (PyStatus_Exception(status)) { fprintf(stderr, "[wandb_c] Failed to configure Python home: %s\n", status.err_msg ? status.err_msg : "unknown error"); PyMem_RawFree(program_name); PyMem_RawFree(home); PyConfig_Clear(&config); return -1; } } status = Py_InitializeFromConfig(&config); if (PyStatus_Exception(status)) { fprintf(stderr, "[wandb_c] Failed to initialise the Python interpreter: %s\n", status.err_msg ? status.err_msg : "unknown error"); PyMem_RawFree(program_name); PyMem_RawFree(home); PyConfig_Clear(&config); return -1; } } if (used_config) { PyMem_RawFree(program_name); PyMem_RawFree(home); PyConfig_Clear(&config); } if (!Py_IsInitialized()) { fprintf(stderr, "[wandb_c] Failed to initialise the Python interpreter.\n"); return -1; } python_started = 1; configure_python_sys_path(); return 0; } /* ------------------------------------------------------------------ */ /* wandb_init_c */ /* ------------------------------------------------------------------ */ int wandb_init_c(const char *project, const char *name, const char *entity, const char *sweep_id) { PyObject *init_fn = NULL; PyObject *kwargs = NULL; PyObject *result = NULL; int rc = -1; /* Start the interpreter if needed. */ if (!python_started) { if (ensure_python_started() != 0) { return -1; } } /* If a sweep_id was supplied, set WANDB_SWEEP_ID so that wandb.init() * joins the sweep and receives the sweep-sampled hyperparameters. */ if (sweep_id && sweep_id[0] != '\0') { PyObject *os_mod = PyImport_ImportModule("os"); if (os_mod) { PyObject *environ = PyObject_GetAttrString(os_mod, "environ"); if (environ) { PyObject *py_sid = PyUnicode_FromString(sweep_id); if (py_sid) { PyObject_SetItem(environ, PyUnicode_FromString("WANDB_SWEEP_ID"), py_sid); Py_DECREF(py_sid); } Py_DECREF(environ); } Py_DECREF(os_mod); } } /* Import wandb. */ printf("[wandb_c] Importing wandb module...\n"); if (!wandb_module) { wandb_module = PyImport_ImportModule("wandb"); if (!wandb_module) { print_py_error("import wandb"); return -1; } } printf("[wandb_c] wandb module imported successfully.\n"); /* Build keyword arguments for wandb.init(). */ kwargs = PyDict_New(); if (!kwargs) { print_py_error("PyDict_New"); goto cleanup; } { PyObject *py_project = PyUnicode_FromString(project); if (!py_project) { print_py_error("project string"); goto cleanup; } PyDict_SetItemString(kwargs, "project", py_project); Py_DECREF(py_project); } if (name && name[0] != '\0') { PyObject *py_name = PyUnicode_FromString(name); if (!py_name) { print_py_error("name string"); goto cleanup; } PyDict_SetItemString(kwargs, "name", py_name); Py_DECREF(py_name); } if (entity && entity[0] != '\0') { PyObject *py_entity = PyUnicode_FromString(entity); if (!py_entity) { print_py_error("entity string"); goto cleanup; } PyDict_SetItemString(kwargs, "entity", py_entity); Py_DECREF(py_entity); } /* Call wandb.init(**kwargs). */ init_fn = PyObject_GetAttrString(wandb_module, "init"); if (!init_fn) { print_py_error("wandb.init lookup"); goto cleanup; } result = PyObject_Call(init_fn, PyTuple_New(0), kwargs); if (!result) { print_py_error("wandb.init call"); goto cleanup; } /* Keep a reference to the run object. */ Py_XDECREF(wandb_run); wandb_run = result; result = NULL; /* ownership transferred */ rc = 0; cleanup: Py_XDECREF(result); Py_XDECREF(init_fn); Py_XDECREF(kwargs); return rc; } /* ------------------------------------------------------------------ */ /* wandb_log_metric_c */ /* ------------------------------------------------------------------ */ void wandb_log_metric_c(const char *key, double value, int step) { PyObject *log_fn = NULL; PyObject *dict = NULL; PyObject *kwargs = NULL; PyObject *result = NULL; if (!wandb_module) return; log_fn = PyObject_GetAttrString(wandb_module, "log"); if (!log_fn) { print_py_error("wandb.log lookup"); return; } dict = PyDict_New(); if (!dict) { print_py_error("PyDict_New"); goto done; } { PyObject *py_val = PyFloat_FromDouble(value); PyDict_SetItemString(dict, key, py_val); Py_DECREF(py_val); } kwargs = PyDict_New(); PyDict_SetItemString(kwargs, "data", dict); /* wandb.log(data=...) */ if (step >= 0) { PyObject *py_step = PyLong_FromLong((long)step); PyDict_SetItemString(kwargs, "step", py_step); Py_DECREF(py_step); } result = PyObject_Call(log_fn, PyTuple_New(0), kwargs); if (!result) print_py_error("wandb.log call"); done: Py_XDECREF(result); Py_XDECREF(kwargs); Py_XDECREF(dict); Py_XDECREF(log_fn); } /* ------------------------------------------------------------------ */ /* wandb_log_metrics_c */ /* ------------------------------------------------------------------ */ void wandb_log_metrics_c(const char **keys, const double *values, int count, int step) { PyObject *log_fn = NULL; PyObject *dict = NULL; PyObject *kwargs = NULL; PyObject *result = NULL; int i; if (!wandb_module || count <= 0) return; log_fn = PyObject_GetAttrString(wandb_module, "log"); if (!log_fn) { print_py_error("wandb.log lookup"); return; } dict = PyDict_New(); for (i = 0; i < count; i++) { PyObject *py_val = PyFloat_FromDouble(values[i]); PyDict_SetItemString(dict, keys[i], py_val); Py_DECREF(py_val); } kwargs = PyDict_New(); PyDict_SetItemString(kwargs, "data", dict); if (step >= 0) { PyObject *py_step = PyLong_FromLong((long)step); PyDict_SetItemString(kwargs, "step", py_step); Py_DECREF(py_step); } result = PyObject_Call(log_fn, PyTuple_New(0), kwargs); if (!result) print_py_error("wandb.log call"); Py_XDECREF(result); Py_XDECREF(kwargs); Py_XDECREF(dict); Py_XDECREF(log_fn); } /* ------------------------------------------------------------------ */ /* wandb_config_set_int_c */ /* ------------------------------------------------------------------ */ void wandb_config_set_int_c(const char *key, int value) { PyObject *config = NULL; PyObject *update = NULL; PyObject *dict = NULL; PyObject *result = NULL; if (!wandb_run) return; config = PyObject_GetAttrString(wandb_run, "config"); if (!config) { print_py_error("run.config"); return; } update = PyObject_GetAttrString(config, "update"); if (!update) { print_py_error("config.update"); goto done; } dict = PyDict_New(); { PyObject *py_val = PyLong_FromLong((long)value); PyDict_SetItemString(dict, key, py_val); Py_DECREF(py_val); } result = PyObject_CallFunctionObjArgs(update, dict, NULL); if (!result) print_py_error("config.update call"); done: Py_XDECREF(result); Py_XDECREF(dict); Py_XDECREF(update); Py_XDECREF(config); } /* ------------------------------------------------------------------ */ /* wandb_config_set_real_c */ /* ------------------------------------------------------------------ */ void wandb_config_set_real_c(const char *key, double value) { PyObject *config = NULL; PyObject *update = NULL; PyObject *dict = NULL; PyObject *result = NULL; if (!wandb_run) return; config = PyObject_GetAttrString(wandb_run, "config"); if (!config) { print_py_error("run.config"); return; } update = PyObject_GetAttrString(config, "update"); if (!update) { print_py_error("config.update"); goto done; } dict = PyDict_New(); { PyObject *py_val = PyFloat_FromDouble(value); PyDict_SetItemString(dict, key, py_val); Py_DECREF(py_val); } result = PyObject_CallFunctionObjArgs(update, dict, NULL); if (!result) print_py_error("config.update call"); done: Py_XDECREF(result); Py_XDECREF(dict); Py_XDECREF(update); Py_XDECREF(config); } /* ------------------------------------------------------------------ */ /* wandb_config_set_str_c */ /* ------------------------------------------------------------------ */ void wandb_config_set_str_c(const char *key, const char *value) { PyObject *config = NULL; PyObject *update = NULL; PyObject *dict = NULL; PyObject *result = NULL; if (!wandb_run) return; config = PyObject_GetAttrString(wandb_run, "config"); if (!config) { print_py_error("run.config"); return; } update = PyObject_GetAttrString(config, "update"); if (!update) { print_py_error("config.update"); goto done; } dict = PyDict_New(); { PyObject *py_val = PyUnicode_FromString(value); PyDict_SetItemString(dict, key, py_val); Py_DECREF(py_val); } result = PyObject_CallFunctionObjArgs(update, dict, NULL); if (!result) print_py_error("config.update call"); done: Py_XDECREF(result); Py_XDECREF(dict); Py_XDECREF(update); Py_XDECREF(config); } /* ------------------------------------------------------------------ */ /* wandb_config_get_int_c */ /* ------------------------------------------------------------------ */ int wandb_config_get_int_c(const char *key, int default_value) { PyObject *config = NULL; PyObject *val = NULL; int result = default_value; if (!wandb_run) return default_value; config = PyObject_GetAttrString(wandb_run, "config"); if (!config) { PyErr_Clear(); return default_value; } val = PyObject_GetItem(config, PyUnicode_FromString(key)); if (!val) { PyErr_Clear(); } else { if (PyLong_Check(val)) result = (int)PyLong_AsLong(val); else if (PyFloat_Check(val)) result = (int)PyFloat_AsDouble(val); Py_DECREF(val); } Py_DECREF(config); return result; } /* ------------------------------------------------------------------ */ /* wandb_config_get_real_c */ /* ------------------------------------------------------------------ */ double wandb_config_get_real_c(const char *key, double default_value) { PyObject *config = NULL; PyObject *val = NULL; double result = default_value; if (!wandb_run) return default_value; config = PyObject_GetAttrString(wandb_run, "config"); if (!config) { PyErr_Clear(); return default_value; } val = PyObject_GetItem(config, PyUnicode_FromString(key)); if (!val) { PyErr_Clear(); } else { if (PyFloat_Check(val)) result = PyFloat_AsDouble(val); else if (PyLong_Check(val)) result = (double)PyLong_AsLong(val); Py_DECREF(val); } Py_DECREF(config); return result; } /* ------------------------------------------------------------------ */ /* wandb_config_get_str_c */ /* ------------------------------------------------------------------ */ int wandb_config_get_str_c(const char *key, char *buf, int buf_len) { PyObject *config = NULL; PyObject *val = NULL; const char *s = NULL; int found = 0; if (!wandb_run || !buf || buf_len <= 0) return 0; config = PyObject_GetAttrString(wandb_run, "config"); if (!config) { PyErr_Clear(); return 0; } val = PyObject_GetItem(config, PyUnicode_FromString(key)); if (!val) { PyErr_Clear(); } else { if (PyUnicode_Check(val)) { s = PyUnicode_AsUTF8(val); if (s) { strncpy(buf, s, (size_t)(buf_len - 1)); buf[buf_len - 1] = '\0'; found = 1; } } Py_DECREF(val); } Py_DECREF(config); return found; } /* ------------------------------------------------------------------ */ /* wandb_sweep_c */ /* ------------------------------------------------------------------ */ int wandb_sweep_c(const char *config_json, const char *project, const char *entity, char *sweep_id_buf, int sweep_id_buf_len) { PyObject *json_mod = NULL; PyObject *loads_fn = NULL; PyObject *cfg_dict = NULL; PyObject *sweep_fn = NULL; PyObject *kwargs = NULL; PyObject *py_str = NULL; PyObject *sweep_id = NULL; const char *id_str = NULL; int rc = -1; /* Ensure Python + wandb are initialised (reuse wandb_init path). */ if (!python_started) { if (ensure_python_started() != 0) { return -1; } } if (!wandb_module) { wandb_module = PyImport_ImportModule("wandb"); if (!wandb_module) { print_py_error("import wandb"); return -1; } } /* Parse the JSON config string into a Python dict. */ json_mod = PyImport_ImportModule("json"); if (!json_mod) { print_py_error("import json"); return -1; } loads_fn = PyObject_GetAttrString(json_mod, "loads"); Py_DECREF(json_mod); if (!loads_fn) { print_py_error("json.loads"); return -1; } py_str = PyUnicode_FromString(config_json); cfg_dict = PyObject_CallFunctionObjArgs(loads_fn, py_str, NULL); Py_DECREF(py_str); Py_DECREF(loads_fn); if (!cfg_dict) { print_py_error("json.loads call"); return -1; } /* Build keyword arguments for wandb.sweep(). */ kwargs = PyDict_New(); if (!kwargs) { Py_DECREF(cfg_dict); return -1; } if (project && project[0] != '\0') { PyObject *p = PyUnicode_FromString(project); PyDict_SetItemString(kwargs, "project", p); Py_DECREF(p); } if (entity && entity[0] != '\0') { PyObject *e = PyUnicode_FromString(entity); PyDict_SetItemString(kwargs, "entity", e); Py_DECREF(e); } /* Call wandb.sweep(cfg_dict, **kwargs). */ sweep_fn = PyObject_GetAttrString(wandb_module, "sweep"); if (!sweep_fn) { print_py_error("wandb.sweep lookup"); goto cleanup; } { PyObject *args = PyTuple_Pack(1, cfg_dict); sweep_id = PyObject_Call(sweep_fn, args, kwargs); Py_DECREF(args); } if (!sweep_id) { print_py_error("wandb.sweep call"); goto cleanup; } /* Copy the sweep ID string into the caller's buffer. */ if (PyUnicode_Check(sweep_id)) { id_str = PyUnicode_AsUTF8(sweep_id); if (id_str && sweep_id_buf && sweep_id_buf_len > 0) { strncpy(sweep_id_buf, id_str, (size_t)(sweep_id_buf_len - 1)); sweep_id_buf[sweep_id_buf_len - 1] = '\0'; } } rc = 0; cleanup: Py_XDECREF(sweep_id); Py_XDECREF(sweep_fn); Py_XDECREF(kwargs); Py_XDECREF(cfg_dict); return rc; } /* ------------------------------------------------------------------ */ /* wandb_agent_c */ /* ------------------------------------------------------------------ */ int wandb_agent_c(const char *sweep_id, const char *project, const char *entity, int count) { PyObject *agent_fn = NULL; PyObject *kwargs = NULL; PyObject *result = NULL; int rc = -1; if (!wandb_module) { fprintf(stderr, "[wandb_c] wandb_agent_c: wandb not initialised. " "Call wandb_sweep_c (or wandb_init_c) first.\n"); return -1; } agent_fn = PyObject_GetAttrString(wandb_module, "agent"); if (!agent_fn) { print_py_error("wandb.agent lookup"); return -1; } kwargs = PyDict_New(); if (!kwargs) { Py_DECREF(agent_fn); return -1; } if (project && project[0] != '\0') { PyObject *p = PyUnicode_FromString(project); PyDict_SetItemString(kwargs, "project", p); Py_DECREF(p); } if (entity && entity[0] != '\0') { PyObject *e = PyUnicode_FromString(entity); PyDict_SetItemString(kwargs, "entity", e); Py_DECREF(e); } if (count > 0) { PyObject *c = PyLong_FromLong((long)count); PyDict_SetItemString(kwargs, "count", c); Py_DECREF(c); } { PyObject *py_id = PyUnicode_FromString(sweep_id); PyObject *args = PyTuple_Pack(1, py_id); result = PyObject_Call(agent_fn, args, kwargs); Py_DECREF(args); Py_DECREF(py_id); } if (!result) { print_py_error("wandb.agent call"); goto cleanup; } rc = 0; cleanup: Py_XDECREF(result); Py_XDECREF(kwargs); Py_XDECREF(agent_fn); return rc; } /* ================================================================== */ /* Sweep-agent threading API */ /* */ /* Architecture: */ /* - wandb_sweep_start_agent_c : starts wandb.agent in a Python */ /* thread with a callback that (a) calls wandb.init, (b) writes */ /* the sampled config to sweep_globals["params_json"], (c) sets */ /* "params_ready" Event, then (d) waits on "run_done" Event. */ /* - wandb_sweep_params_c : waits for "params_ready", copies */ /* the params JSON into the caller's buffer, then clears the */ /* event. Also updates wandb_run from sweep_globals["run"]. */ /* - wandb_sweep_run_done_c : sets "run_done" Event so the */ /* callback can call wandb.finish() and the agent can request */ /* the next run. */ /* ================================================================== */ /* Inline Python that sets up the threading infrastructure. * Executed once by wandb_sweep_start_agent_c. */ static const char *SWEEP_SETUP_CODE = "import threading, json as _json, wandb as _wandb\n" "\n" "params_ready = threading.Event()\n" "run_done = threading.Event()\n" "params_json = '{}'\n" "_run_obj = None\n" "\n" "def _agent_callback():\n" " global params_json, _run_obj\n" " _run_obj = _wandb.init()\n" " cfg = dict(_run_obj.config)\n" " params_json = _json.dumps(cfg)\n" " params_ready.set()\n" " while not run_done.is_set():\n" " import time; time.sleep(0.02)\n" " run_done.clear()\n" " _wandb.finish()\n" ; /* ------------------------------------------------------------------ */ /* wandb_sweep_start_agent_c */ /* Starts wandb.agent(sweep_id, function=_agent_callback, count=N) */ /* in a background Python thread. Must be called after */ /* wandb_sweep_c. */ /* ------------------------------------------------------------------ */ int wandb_sweep_start_agent_c(const char *sweep_id, const char *project, const char *entity, int count) { PyObject *glb = NULL; PyObject *result = NULL; int rc = -1; if (!python_started || !wandb_module) { fprintf(stderr, "[wandb_c] wandb_sweep_start_agent_c: " "call wandb_sweep_c first.\n"); return -1; } /* Create a fresh dict to serve as our module globals. */ Py_XDECREF(sweep_globals); sweep_globals = PyDict_New(); if (!sweep_globals) return -1; /* Inject __builtins__ so exec works. */ PyDict_SetItemString(sweep_globals, "__builtins__", PyEval_GetBuiltins()); /* Run the setup code — defines _agent_callback, Events, etc. */ result = PyRun_String(SWEEP_SETUP_CODE, Py_file_input, sweep_globals, sweep_globals); if (!result) { print_py_error("sweep setup code"); return -1; } Py_DECREF(result); /* Build the agent-launch code string. */ { char launch[1024]; const char *proj_arg = (project && project[0]) ? project : ""; const char *entity_arg = (entity && entity[0]) ? entity : ""; snprintf(launch, sizeof(launch), "import threading as _t\n" "_agent_thread = _t.Thread(\n" " target=_wandb.agent,\n" " kwargs={'sweep_id': '%s',\n" " 'function': _agent_callback,\n" " 'count': %d,\n" " 'project': '%s',\n" " 'entity': '%s'})\n" "_agent_thread.daemon = True\n" "_agent_thread.start()\n", sweep_id, count, proj_arg, entity_arg); result = PyRun_String(launch, Py_file_input, sweep_globals, sweep_globals); if (!result) { print_py_error("agent thread launch"); return -1; } Py_DECREF(result); } rc = 0; return rc; } /* ------------------------------------------------------------------ */ /* wandb_sweep_params_c */ /* Blocks until the agent callback has called wandb.init() and */ /* written the sampled params as JSON. Copies the JSON into buf. */ /* Also updates the internal wandb_run pointer. */ /* Returns 1 if params were received, 0 on timeout/error. */ /* ------------------------------------------------------------------ */ int wandb_sweep_params_c(char *buf, int buf_len, double timeout_s) { PyObject *params_ready = NULL; PyObject *wait_fn = NULL; PyObject *wait_result = NULL; PyObject *params_str = NULL; const char *s = NULL; int got_params = 0; if (!sweep_globals) { fprintf(stderr, "[wandb_c] wandb_sweep_params_c: " "call wandb_sweep_start_agent_c first.\n"); return 0; } params_ready = PyDict_GetItemString(sweep_globals, "params_ready"); if (!params_ready) { print_py_error("params_ready lookup"); return 0; } /* Call params_ready.wait(timeout=timeout_s) */ wait_fn = PyObject_GetAttrString(params_ready, "wait"); if (!wait_fn) { print_py_error("Event.wait"); return 0; } { PyObject *py_timeout = PyFloat_FromDouble(timeout_s); PyObject *args = PyTuple_Pack(1, py_timeout); wait_result = PyObject_Call(wait_fn, args, NULL); Py_DECREF(args); Py_DECREF(py_timeout); } Py_DECREF(wait_fn); if (!wait_result) { print_py_error("Event.wait call"); return 0; } got_params = PyObject_IsTrue(wait_result); /* True = event set (params ready) */ Py_DECREF(wait_result); if (!got_params) { fprintf(stderr, "[wandb_c] wandb_sweep_params_c: timed out waiting " "for sweep agent callback.\n"); return 0; } /* Clear the event: params_ready.clear() */ { PyObject *clear_fn = PyObject_GetAttrString(params_ready, "clear"); if (clear_fn) { PyObject *r = PyObject_CallNoArgs(clear_fn); Py_XDECREF(r); Py_DECREF(clear_fn); } } /* Copy params JSON into caller's buffer. */ params_str = PyDict_GetItemString(sweep_globals, "params_json"); if (params_str && PyUnicode_Check(params_str)) { s = PyUnicode_AsUTF8(params_str); if (s && buf && buf_len > 0) { strncpy(buf, s, (size_t)(buf_len - 1)); buf[buf_len - 1] = '\0'; } } /* Also grab the wandb_run object so wandb_log / wandb_config_set work. */ { PyObject *run_obj = PyDict_GetItemString(sweep_globals, "_run_obj"); if (run_obj && run_obj != Py_None) { Py_XDECREF(wandb_run); Py_INCREF(run_obj); wandb_run = run_obj; } } return 1; } /* ------------------------------------------------------------------ */ /* wandb_sweep_run_done_c */ /* Signals the agent callback that training is done. */ /* The callback will then call wandb.finish() and the agent will */ /* request the next run from the sweep controller. */ /* ------------------------------------------------------------------ */ void wandb_sweep_run_done_c(void) { PyObject *run_done = NULL; PyObject *set_fn = NULL; PyObject *result = NULL; if (!sweep_globals) return; run_done = PyDict_GetItemString(sweep_globals, "run_done"); if (!run_done) { print_py_error("run_done lookup"); return; } set_fn = PyObject_GetAttrString(run_done, "set"); if (set_fn) { result = PyObject_CallNoArgs(set_fn); Py_XDECREF(result); Py_DECREF(set_fn); } /* Release run reference — the callback will call wandb.finish(). */ Py_XDECREF(wandb_run); wandb_run = NULL; } /* ================================================================== */ /* End sweep-agent threading API */ /* ================================================================== */ /* ------------------------------------------------------------------ */ /* wandb_finish_c */ /* Finishes the current wandb run but keeps Python alive so that */ /* wandb_init_c can be called again (e.g. for sweep runs). */ /* ------------------------------------------------------------------ */ void wandb_finish_c(void) { PyObject *finish_fn = NULL; PyObject *result = NULL; if (wandb_module) { finish_fn = PyObject_GetAttrString(wandb_module, "finish"); if (finish_fn) { result = PyObject_CallNoArgs(finish_fn); if (!result) print_py_error("wandb.finish call"); Py_XDECREF(result); Py_XDECREF(finish_fn); } } /* Release the run object so wandb_init_c can create a new one. */ Py_XDECREF(wandb_run); wandb_run = NULL; /* Keep wandb_module and python_started — Python stays alive. */ } /* ------------------------------------------------------------------ */ /* wandb_shutdown_c */ /* Full teardown: release all Python objects and finalise the */ /* interpreter. Call once after all runs are done. */ /* ------------------------------------------------------------------ */ void wandb_shutdown_c(void) { /* Give wandb's background service thread a moment to flush the * last run's data before we tear down the interpreter. */ { PyObject *time_mod = PyImport_ImportModule("time"); if (time_mod) { PyObject *sleep_fn = PyObject_GetAttrString(time_mod, "sleep"); if (sleep_fn) { PyObject *t = PyFloat_FromDouble(2.0); PyObject *r = PyObject_CallFunctionObjArgs(sleep_fn, t, NULL); Py_XDECREF(r); Py_DECREF(t); Py_DECREF(sleep_fn); } Py_DECREF(time_mod); } } Py_XDECREF(wandb_run); wandb_run = NULL; Py_XDECREF(sweep_globals); sweep_globals = NULL; Py_XDECREF(wandb_module); wandb_module = NULL; if (python_started) { Py_Finalize(); python_started = 0; } }