mirror of
https://github.com/pocketpy/pocketpy
synced 2025-12-06 18:20:17 +00:00
176 lines
4.5 KiB
Markdown
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/)
|