pocketpy/docs/check_binding_retval.md
copilot-swe-agent[bot] 5d246dff53 Add Python binding return value checker tool
Co-authored-by: blueloveTH <28104173+blueloveTH@users.noreply.github.com>
2025-12-03 08:20:28 +00:00

176 lines
4.5 KiB
Markdown

# Binding Return Value Checker
## Overview
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`.
## Purpose
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:
1. **Direct assignment** using `py_new*` functions:
```c
py_newint(py_retval(), 42);
return true;
```
2. **Setting None** explicitly:
```c
py_newnone(py_retval());
return true;
```
3. **Using assignment**:
```c
py_assign(py_retval(), some_value);
return true;
```
4. **Calling functions** that set `py_retval()` internally:
```c
bool ok = py_import("module_name"); // Sets py_retval() on success
if(ok) return true;
```
## Usage
### Basic Usage
Run the checker on default directories (`src/bindings` and `src/modules`):
```bash
python scripts/check_binding_retval.py
```
### Verbose Mode
Enable verbose output for debugging:
```bash
python scripts/check_binding_retval.py --verbose
```
### Custom Directories
Check specific directories:
```bash
python scripts/check_binding_retval.py --dirs src/bindings src/modules src/custom
```
## Exit Codes
- `0`: No issues found (success)
- `1`: Issues found (binding functions missing return value assignment)
- `2`: Script error (e.g., file access error)
## Integration
The checker is integrated into the CI/CD pipeline through the GitHub Actions workflow. It runs automatically on:
- Push events
- Pull request events
The check is part of the "Run Script Check" step in `.github/workflows/main.yml`.
## What It Checks
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:
1. If the function contains `return true` statements
2. There are corresponding assignments to `py_retval()`
3. Comments are excluded from analysis (to avoid false positives)
## Functions That Set py_retval() Internally
The checker recognizes these functions that internally set `py_retval()`:
- `py_import()` - Sets py_retval() to the imported module on success
- `py_call()` - Sets py_retval() to the call result
- `py_iter()` - Sets py_retval() to the iterator
- `py_str()` - Sets py_retval() to string representation
- `py_repr()` - Sets py_retval() to repr string
- `py_getattr()` - Sets py_retval() to attribute value
- `py_next()` - Sets py_retval() to next value
- `py_getitem()` - Sets py_retval() to the item
- `py_vectorcall()` - Sets py_retval() to call result
## Example Issues
### Issue: Missing Return Value
```c
static 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:**
```c
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;
}
```
### Correct: Using py_new* Functions
```c
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;
}
```
### Correct: Calling Functions That Set py_retval()
```c
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");
}
```
## False Positives
The checker may occasionally report false positives for:
1. **Helper functions** that are bound but shouldn't set return values (rare)
2. **Complex control flow** where the return value is set conditionally
If you encounter a false positive, verify that:
1. The function is actually a Python-bound callable
2. The return value is properly set in all code paths leading to `return true`
## Maintenance
When adding new patterns or functions that set `py_retval()` internally:
1. Update the `RETVAL_SETTING_FUNCTIONS` set in `check_binding_retval.py`
2. Add corresponding test cases
3. Update this documentation
## Related Documentation
- [pocketpy C API Documentation](https://pocketpy.dev/c-api/)
- [Binding Functions Guide](https://pocketpy.dev/c-api/bindings/)