This file provides instructions for AI coding agents (including GitHub Copilot) working on this repository.
This repository provides Python bindings for the Rust library egglog, enabling the use of e-graphs in Python for optimization, symbolic computation, and analysis. It is a hybrid project combining:
- Python code in
python/egglog/- The main Python API and library - Rust code in
src/- PyO3-based bindings to the egglog Rust library - Documentation in
docs/- Sphinx-based documentation
python/egglog/- Main Python package source codepython/tests/- Python test suite (pytest-based)src/- Rust source code for Python bindings (PyO3)docs/- Documentation source files (Sphinx)test-data/- Test data filespyproject.toml- Python project configuration and dependenciesCargo.toml- Rust project configurationuv.lock- Locked dependencies (managed by uv)
- uv - Package manager (https://github.com/astral-sh/uv)
- Rust toolchain - Version pinned in
rust-toolchain.toml - Python - Version pinned in
.python-version
# Install dependencies
uv sync --all-extras
# Reinstall the Rust extension after changing code in `src/`
uv sync --reinstall-package egglog --all-extras
# Run tests
uv run pytest --benchmark-disable -vvv --durations=10
# Type checking with mypy
make mypy
# Stub testing
make stubtest
# Build documentation
make docs
# Refresh the bundled visualizer assets
make clean
make
# Format code (auto-run by pre-commit)
uv run ruff format .
# Lint code (auto-run by pre-commit)
uv run ruff check --fix .- Line length: 120 characters maximum
- Type hints: Use type annotations for public APIs and functions
- Formatting: Use Ruff for code formatting and linting
- Testing: Write tests using pytest in
python/tests/ - Docstrings: Use clear, concise docstrings for public functions and classes
The project uses Ruff for linting and formatting with specific rules:
- Allows uppercase variable names (N806, N802)
- Allows star imports (F405, F403)
- Allows
execand subprocess usage (S102, S307, S603) - Allows
Anytype annotations (ANN401) - Test files don't require full type annotations
See pyproject.toml for complete Ruff configuration.
- mypy is used for static type checking
- Run
make mypyto type check Python code - Run
make stubtestto validate type stubs against runtime behavior - Exclusions:
__snapshots__,_build,conftest.py
- Tests are located in
python/tests/ - Use pytest with snapshot testing (syrupy)
- Benchmarks use pytest-benchmark and CodSpeed
- Run tests with:
uv run pytest --benchmark-disable -vvv
- Edition: Rust 2024 (experimental)
- FFI: Uses PyO3 for Python bindings
- Main library: Uses egglog from git (saulshanabrook/egg-smol, clone-cost branch)
src/lib.rs- Main library entry pointsrc/egraph.rs- E-graph implementationsrc/conversions.rs- Type conversions between Python and Rustsrc/py_object_sort.rs- Python object handlingsrc/extract.rs- Extraction functionalitysrc/error.rs- Error handlingsrc/serialize.rs- Serialization supportsrc/termdag.rs- Term DAG operationssrc/utils.rs- Utility functions
All public Python APIs are exported from the top-level egglog module. Anything that is public should be exported in python/egglog/__init__.py at the top level.
The egglog.bindings module provides lower-level access to the Rust implementation for advanced use cases.
python/egglog/__init__.py- Top-level module exports, defines the public APIpython/egglog/egraph.py- Main EGraph class and e-graph managementpython/egglog/egraph_state.py- E-graph state and execution managementpython/egglog/runtime.py- Runtime system for expression evaluation and method definitionspython/egglog/builtins.py- Built-in types (i64, f64, String, Vec, etc.) and operationspython/egglog/declarations.py- Class, function, and method declaration decoratorspython/egglog/conversion.py- Type conversion between Python and egglog typespython/egglog/pretty.py- Pretty printing for expressions and e-graph visualizationpython/egglog/deconstruct.py- Deconstruction of Python values into egglog expressionspython/egglog/thunk.py- Lazy evaluation supportpython/egglog/type_constraint_solver.py- Type inference and constraint solvingpython/egglog/config.py- Configuration settingspython/egglog/ipython_magic.py- IPython/Jupyter integrationpython/egglog/visualizer_widget.py- Interactive visualization widgetpython/egglog/version_compat.py- Python version compatibility utilitiespython/egglog/examples/- End-to-end samples and tutorials demonstrating the APIpython/egglog/exp/- Experimental Array API integrations and code generation helpers
The compiled extension artifact python/egglog/bindings.cpython-*.so is generated by uv sync and should not be edited manually.
- Imports: Follow Ruff's import sorting
- Naming:
- Python: snake_case for functions and variables, PascalCase for classes
- Rust: Follow standard Rust conventions
- Comments: Use clear, explanatory comments for complex logic
- Documentation: Keep docs synchronized with code changes
When making changes:
- Update or add tests in
python/tests/for Python changes - Run the full test suite before committing
- Ensure type checking passes with
make mypy - Build documentation if changing public APIs
- Follow existing code patterns and style
- Keep changes minimal and focused
- Ensure the automatic changelog entry in
docs/changelog.md(added when opening the PR) accurately reflects your change and add manual notes if additional clarification is needed
- Define e-graph classes by inheriting from
egglog.Expr - Use
@egraph.functiondecorator for functions - Use
@egraph.methoddecorator for methods - Leverage type annotations for better IDE support
- Use
get_literal_value(expr)or the.valueproperty to get Python values from primitives - Use pattern matching with
match/casefor destructuring egglog primitives - Use
get_callable_fn(expr)to get the underlying Python function from a callable expression - Use
get_callable_args(expr)to get arguments to a callable
- The underlying Rust library uses Rayon for parallelism
- Control worker thread count via
RAYON_NUM_THREADSenvironment variable - Defaults to single thread if not set
- Use PyO3's
#[pyclass]and#[pymethods]macros - Handle errors with appropriate Python exceptions
- Convert between Rust and Python types in
conversions.rs
Documentation is built with Sphinx:
- Source files in
docs/ - Build with
make docs - Output in
docs/_build/html/ - Hosted on ReadTheDocs
- Unit tests: Test individual functions and classes
- Integration tests: Test complete workflows
- Snapshot tests: Use syrupy for snapshot testing of complex outputs
- Benchmarks: Performance testing with pytest-benchmark and pytest-codspeed
- Parallel testing: Use pytest-xdist for faster test runs
- Type checking: Validate type stubs and annotations
- The library uses Rust for performance-critical operations
- Benchmarking is done via CodSpeed for continuous performance monitoring
- Profile with release builds (
cargo build --release) when needed