The check_binding_retval.py script is a static analysis tool that validates Python binding functions in the pocketpy codebase. It ensures that all functions bound to Python properly set return values before returning true.
In pocketpy's C API, when a binding function returns true, it indicates successful execution. According to the API conventions, the function MUST set a return value through one of these methods:
Direct assignment using py_new* functions:
py_newint(py_retval(), 42);
return true;
Setting None explicitly:
py_newnone(py_retval());
return true;
Using assignment:
py_assign(py_retval(), some_value);
return true;
Calling functions that set py_retval() internally:
bool ok = py_import("module_name"); // Sets py_retval() on success
if(ok) return true;
Run the checker on default directories (src/bindings and src/modules):
python scripts/check_binding_retval.py
Enable verbose output for debugging:
python scripts/check_binding_retval.py --verbose
Check specific directories:
python scripts/check_binding_retval.py --dirs src/bindings src/modules src/custom
0: No issues found (success)1: Issues found (binding functions missing return value assignment)2: Script error (e.g., file access error)The checker is integrated into the CI/CD pipeline through the GitHub Actions workflow. It runs automatically on:
The check is part of the "Run Script Check" step in .github/workflows/main.yml.
The tool analyzes all functions that are bound to Python through:
py_bindfunc()py_bind()py_bindmagic()py_bindmethod()py_bindproperty()For each bound function, it verifies that:
return true statementspy_retval()The checker recognizes these functions that internally set py_retval():
py_import() - Sets py_retval() to the imported module on successpy_call() - Sets py_retval() to the call resultpy_iter() - Sets py_retval() to the iteratorpy_str() - Sets py_retval() to string representationpy_repr() - Sets py_retval() to repr stringpy_getattr() - Sets py_retval() to attribute valuepy_next() - Sets py_retval() to next valuepy_getitem() - Sets py_retval() to the itempy_vectorcall() - Sets py_retval() to call resultstatic bool my_function(int argc, py_Ref argv) {
PY_CHECK_ARGC(1);
// Do some work...
return true; // BUG: Should set py_retval() before returning!
}
Fix:
static bool my_function(int argc, py_Ref argv) {
PY_CHECK_ARGC(1);
// Do some work...
py_newnone(py_retval()); // Set return value to None
return true;
}
static bool add_numbers(int argc, py_Ref argv) {
PY_CHECK_ARGC(2);
py_i64 a = py_toint(py_arg(0));
py_i64 b = py_toint(py_arg(1));
py_newint(py_retval(), a + b); // Sets return value
return true;
}
static bool import_module(int argc, py_Ref argv) {
PY_CHECK_ARGC(1);
int res = py_import(py_tostr(py_arg(0))); // Sets py_retval() on success
if(res == -1) return false;
if(res) return true; // py_retval() already set by py_import
return ImportError("module not found");
}
The checker may occasionally report false positives for:
If you encounter a false positive, verify that:
return trueWhen adding new patterns or functions that set py_retval() internally:
RETVAL_SETTING_FUNCTIONS set in check_binding_retval.py