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

4.5 KiB

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:

    py_newint(py_retval(), 42);
    return true;
    
  2. Setting None explicitly:

    py_newnone(py_retval());
    return true;
    
  3. Using assignment:

    py_assign(py_retval(), some_value);
    return true;
    
  4. Calling functions that set py_retval() internally:

    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):

python scripts/check_binding_retval.py

Verbose Mode

Enable verbose output for debugging:

python scripts/check_binding_retval.py --verbose

Custom Directories

Check specific directories:

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

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:

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

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()

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