Update Python Binding Layer

John McCardle 2025-12-02 03:16:35 +00:00
parent 49f2aa8d2f
commit 973108f14d
1 changed files with 186 additions and 186 deletions

@ -1,187 +1,187 @@
# Python Binding Layer # Python Binding Layer
The Python Binding Layer exposes C++ engine functionality to Python using Python's C API. This system allows game logic to be written in Python while maintaining C++ rendering performance. The Python Binding Layer exposes C++ engine functionality to Python using Python's C API. This system allows game logic to be written in Python while maintaining C++ rendering performance.
## Quick Reference ## Quick Reference
**Related Issues:** **Related Issues:**
- [#126](../../issues/126) - Generate Perfectly Consistent Python Interface (Tier 1) - [#126](../../issues/126) - Generate Perfectly Consistent Python Interface (Tier 1)
- [#109](../../issues/109) - Vector Convenience Methods - [#109](../../issues/109) - Vector Convenience Methods
- [#101](../../issues/101) - Standardize Constructor Arguments - [#101](../../issues/101) - Standardize Constructor Arguments
- [#92](../../issues/92) - Inline C++ Documentation System - [#92](../../issues/92) - Inline C++ Documentation System
- [#91](../../issues/91) - Generate Python Type Stub Files (.pyi) - [#91](../../issues/91) - Generate Python Type Stub Files (.pyi)
**Key Files:** **Key Files:**
- `src/McRFPy_API.h` / `src/McRFPy_API.cpp` - Main Python module definition - `src/McRFPy_API.h` / `src/McRFPy_API.cpp` - Main Python module definition
- `src/PyObjectUtils.h` - Utility functions for Python/C++ conversion - `src/PyObjectUtils.h` - Utility functions for Python/C++ conversion
- `src/UIDrawable.h` - `RET_PY_INSTANCE` macro pattern - `src/UIDrawable.h` - `RET_PY_INSTANCE` macro pattern
- Individual class binding files: `src/UI*.cpp` (PyGetSetDef arrays) - Individual class binding files: `src/UI*.cpp` (PyGetSetDef arrays)
**Reference Documentation:** **Reference Documentation:**
- `PYTHON_BINDING_PATTERNS.md` - Comprehensive pattern reference (repo root) - `PYTHON_BINDING_PATTERNS.md` - Comprehensive pattern reference (repo root)
- [[Adding-Python-Bindings]] - Step-by-step workflow guide - [[Adding-Python-Bindings]] - Step-by-step workflow guide
## Architecture Overview ## Architecture Overview
### Module Structure ### Module Structure
``` ```
mcrfpy (C extension module) mcrfpy (C extension module)
├── Types (Frame, Caption, Sprite, Grid, Entity, etc) ├── Types (Frame, Caption, Sprite, Grid, Entity, etc)
├── Functions (createScene, setScene, animate, etc) ├── Functions (createScene, setScene, animate, etc)
├── Constants (SFML key codes, etc) ├── Constants (SFML key codes, etc)
└── Submodules └── Submodules
└── libtcod (TCOD bindings) └── libtcod (TCOD bindings)
``` ```
**Entry Point:** `src/McRFPy_API.cpp::PyInit_mcrfpy()` **Entry Point:** `src/McRFPy_API.cpp::PyInit_mcrfpy()`
### Binding Patterns ### Binding Patterns
#### Pattern 1: PyGetSetDef for Properties #### Pattern 1: PyGetSetDef for Properties
Properties exposed via getter/setter arrays: Properties exposed via getter/setter arrays:
```cpp ```cpp
PyGetSetDef PyUISprite::getsetters[] = { PyGetSetDef PyUISprite::getsetters[] = {
{"x", (getter)Drawable::get_member, (setter)Drawable::set_member, {"x", (getter)Drawable::get_member, (setter)Drawable::set_member,
"X coordinate", (void*)SPRITE_X}, "X coordinate", (void*)SPRITE_X},
{"texture", (getter)PyUISprite::get_texture, (setter)PyUISprite::set_texture, {"texture", (getter)PyUISprite::get_texture, (setter)PyUISprite::set_texture,
"Sprite texture", NULL}, "Sprite texture", NULL},
{NULL} // Sentinel {NULL} // Sentinel
}; };
``` ```
**Closure Parameter:** Used to identify which property is being accessed **Closure Parameter:** Used to identify which property is being accessed
- Simple types: Integer values (0, 1, 2, 3) - Simple types: Integer values (0, 1, 2, 3)
- UIDrawable types: `(void*)((intptr_t)PyObjectsEnum::TYPE << 8 | member_index)` - UIDrawable types: `(void*)((intptr_t)PyObjectsEnum::TYPE << 8 | member_index)`
**See:** `PYTHON_BINDING_PATTERNS.md` for complete closure encoding reference **See:** `PYTHON_BINDING_PATTERNS.md` for complete closure encoding reference
#### Pattern 2: PyMethodDef for Methods #### Pattern 2: PyMethodDef for Methods
Methods exposed via method definition arrays: Methods exposed via method definition arrays:
```cpp ```cpp
PyMethodDef PyUIGrid::methods[] = { PyMethodDef PyUIGrid::methods[] = {
{"at", (PyCFunction)PyUIGrid::at, METH_VARARGS | METH_KEYWORDS, {"at", (PyCFunction)PyUIGrid::at, METH_VARARGS | METH_KEYWORDS,
"at(pos: tuple) -> GridPoint\n\n" "at(pos: tuple) -> GridPoint\n\n"
"Access grid cell at position.\n\n" "Access grid cell at position.\n\n"
"Args:\n" "Args:\n"
" pos: (x, y) tuple\n\n" " pos: (x, y) tuple\n\n"
"Returns:\n" "Returns:\n"
" GridPoint object at that position"}, " GridPoint object at that position"},
{NULL} {NULL}
}; };
``` ```
**Inline Documentation:** Docstrings extracted by `tools/generate_dynamic_docs.py` **Inline Documentation:** Docstrings extracted by `tools/generate_dynamic_docs.py`
#### Pattern 3: RET_PY_INSTANCE Macro #### Pattern 3: RET_PY_INSTANCE Macro
Converting C++ objects to Python requires type-aware allocation: Converting C++ objects to Python requires type-aware allocation:
```cpp ```cpp
RET_PY_INSTANCE(target); RET_PY_INSTANCE(target);
// Expands to switch on target->derived_type(): // Expands to switch on target->derived_type():
// - Allocates correct Python type (Frame, Caption, Sprite, Grid) // - Allocates correct Python type (Frame, Caption, Sprite, Grid)
// - Assigns shared_ptr to data member // - Assigns shared_ptr to data member
// - Returns PyObject* // - Returns PyObject*
``` ```
**File:** `src/UIDrawable.h::RET_PY_INSTANCE` macro definition **File:** `src/UIDrawable.h::RET_PY_INSTANCE` macro definition
## Common Patterns ## Common Patterns
### Adding a Property ### Adding a Property
See [[Adding-Python-Bindings]] for complete step-by-step workflow. See [[Adding-Python-Bindings]] for complete step-by-step workflow.
**Quick reference:** **Quick reference:**
1. Add to PyGetSetDef array 1. Add to PyGetSetDef array
2. Implement getter/setter functions 2. Implement getter/setter functions
3. Encode closure parameter 3. Encode closure parameter
4. Add inline documentation 4. Add inline documentation
5. Test with Python 5. Test with Python
### Type Preservation in Collections ### Type Preservation in Collections
**Challenge:** Shared pointers can lose Python type information **Challenge:** Shared pointers can lose Python type information
**Solution:** **Solution:**
- Use `RET_PY_INSTANCE` when returning from collections - Use `RET_PY_INSTANCE` when returning from collections
- Maintain Python object references when needed - Maintain Python object references when needed
- See [#112](../../issues/112) for object splitting bug details - See [#112](../../issues/112) for object splitting bug details
### Constructor Standardization ### Constructor Standardization
**Current state:** Inconsistent constructor patterns across types **Current state:** Inconsistent constructor patterns across types
**Planned:** [#101](../../issues/101) - Standardize all constructors to accept: **Planned:** [#101](../../issues/101) - Standardize all constructors to accept:
- Position as `(x, y)` tuple or separate `x, y` args - Position as `(x, y)` tuple or separate `x, y` args
- Size as `(w, h)` tuple or separate `w, h` args - Size as `(w, h)` tuple or separate `w, h` args
- Consistent default values (usually `(0, 0)`) - Consistent default values (usually `(0, 0)`)
## Key Subsystems ## Key Subsystems
### PyArgHelpers ### PyArgHelpers
Standardized argument parsing for tuples vs separate args: Standardized argument parsing for tuples vs separate args:
```cpp ```cpp
// Accept both (x, y) and x, y formats // Accept both (x, y) and x, y formats
PyArgParseTuple_IntIntHelper(args, kwds, x, y, "position", "x", "y"); PyArgParseTuple_IntIntHelper(args, kwds, x, y, "position", "x", "y");
``` ```
**Files:** **Files:**
- `src/PyArgHelpers.h` - Helper function definitions - `src/PyArgHelpers.h` - Helper function definitions
- Used throughout `src/UI*.cpp` for constructor consistency - Used throughout `src/UI*.cpp` for constructor consistency
### Documentation Extraction ### Documentation Extraction
**Pipeline:** **Pipeline:**
1. C++ docstrings in PyMethodDef/PyGetSetDef arrays 1. C++ docstrings in PyMethodDef/PyGetSetDef arrays
2. Compilation embeds docstrings in module 2. Compilation embeds docstrings in module
3. `tools/generate_dynamic_docs.py` extracts via introspection 3. `tools/generate_dynamic_docs.py` extracts via introspection
4. Generates `docs/api_reference_dynamic.html` 4. Generates `docs/api_reference_dynamic.html`
**Format:** See CLAUDE.md "Inline C++ Documentation Format" section **Format:** See CLAUDE.md "Inline C++ Documentation Format" section
## Current Issues & Limitations ## Current Issues & Limitations
**Consistency Issues:** **Consistency Issues:**
- [#126](../../issues/126): Need automated generation for perfect consistency - [#126](../../issues/126): Need automated generation for perfect consistency
- [#101](../../issues/101): Constructor arguments vary by type - [#101](../../issues/101): Constructor arguments vary by type
- [#109](../../issues/109): Vector lacks `[0]`, `[1]` indexing - [#109](../../issues/109): Vector lacks `[0]`, `[1]` indexing
**Type Preservation:** **Type Preservation:**
- Collections can lose Python derived types - Collections can lose Python derived types
- Workaround: `RET_PY_INSTANCE` macro - Workaround: `RET_PY_INSTANCE` macro
- Long-term: Better type tracking in C++ - Long-term: Better type tracking in C++
## Related Systems ## Related Systems
- [[UI-Component-Hierarchy]] - Classes exposed to Python - [[UI-Component-Hierarchy]] - Classes exposed to Python
- [[Grid-System]] - Grid/Entity Python API - [[Grid-System]] - Grid/Entity Python API
- [[Animation-System]] - `animate()` function binding - [[Animation-System]] - `animate()` function binding
## Design Decisions ## Design Decisions
**Why Python C API vs pybind11/SWIG?** **Why Python C API vs pybind11/SWIG?**
- Fine-grained control over type system - Fine-grained control over type system
- Direct integration with CPython internals - Direct integration with CPython internals
- No third-party dependencies - No third-party dependencies
- Performance: Zero-overhead abstraction - Performance: Zero-overhead abstraction
**Tradeoffs:** **Tradeoffs:**
- More verbose than pybind11 - More verbose than pybind11
- Manual memory management required - Manual memory management required
- Type checking done manually - Type checking done manually
- But: Full control, no "magic" - But: Full control, no "magic"
--- ---
**Next Steps:** **Next Steps:**
- Review [[Adding-Python-Bindings]] workflow - Review [[Adding-Python-Bindings]] workflow
- Study `PYTHON_BINDING_PATTERNS.md` for complete patterns - Study `PYTHON_BINDING_PATTERNS.md` for complete patterns
- See [#126](../../issues/126) for automated generation progress - See [#126](../../issues/126) for automated generation progress