Regrest is an automated regression testing and debugging tool for Python. It automatically records function outputs on the first run and validates them on subsequent runs, while providing powerful visualization capabilities for understanding complex data flows.
- π― Zero-effort regression testing - Catch unintended changes without writing test code
- π Automatic test generation - Just add
@regrestdecorator, tests are created automatically - π¬ Visual debugging - Understand complex data flows by visualizing function inputs and outputs
- π Beautiful web dashboard - Explore recorded data with syntax highlighting and hierarchical navigation
- β‘ Faster debugging cycles - Reproduce issues instantly with saved inputs
- π Living documentation - Function behavior examples generated automatically from real executions
- Python 3.9 or higher
pip install regrest
# Optional: Install with Flask for better server performance
pip install regrest[server]from regrest import regrest
@regrest
def calculate_price(items, discount=0):
total = sum(item['price'] for item in items)
return total * (1 - discount)
# First run: records the result
items = [{'price': 100}, {'price': 200}]
result = calculate_price(items, discount=0.1) # Returns 270.0, records it
# Output: [regrest] Recorded: __main__.calculate_price (id: abc123...)
# Second run: validates against recorded result
result = calculate_price(items, discount=0.1) # Returns 270.0, compares with record
# Output: [regrest] Passed: __main__.calculate_price (id: abc123...)@regrest(tolerance=1e-6)
def calculate_pi():
return 3.14159265359To update existing records instead of testing:
@regrest(update=True)
def my_function():
return "new result"Or set the environment variable:
REGREST_UPDATE_MODE=1 python your_script.py| Variable | Description | Values | Default |
|---|---|---|---|
REGREST_LOG_LEVEL |
Log level | DEBUG, INFO, WARNING, ERROR, CRITICAL | INFO |
REGREST_RAISE_ON_ERROR |
Raise exceptions on test failure | True/False | False |
REGREST_UPDATE_MODE |
Update all records | True/False | False |
REGREST_STORAGE_DIR |
Custom storage directory | Directory path | .regrest |
REGREST_FLOAT_TOLERANCE |
Float comparison tolerance | Numeric value | 1e-9 |
Priority: Constructor arguments > Environment variables > Default values
Automatically catch unintended changes in function behavior:
@regrest
def calculate_discount(price, customer_type):
# Business logic that should not change
return price * get_discount_rate(customer_type)
# First run: records the result
calculate_discount(100, "premium") # Records: 85.0
# Later: if logic changes accidentally, test fails
calculate_discount(100, "premium") # Fails if result != 85.0Understand complex data flows by visualizing inputs and outputs:
@regrest
def process_pipeline(data):
"""Complex data transformation."""
return transform(filter(validate(data)))
# Run once to record
process_pipeline(raw_data)
# Visualize in CLI
$ regrest list -k process_pipeline
# Shows: args, kwargs, and results in readable format
# Or browse in web UI
$ regrest serve
# Navigate to http://localhost:8000
# See formatted inputs/outputs with syntax highlightingBenefits for debugging:
- πΈ Snapshot complex objects - See exact state of nested data structures
- π Track changes over time - Compare how outputs evolve as code changes
- ποΈ Visual inspection - Web UI with JSONesque display for easy reading
- π Reproduce issues - Saved inputs allow easy bug reproduction
Generate live examples of function behavior:
@regrest
def api_response_formatter(user_data):
"""Format user data for API response."""
return {
"id": user_data["id"],
"name": f"{user_data['first']} {user_data['last']}",
"email": user_data["email"].lower(),
}
# Run with example inputs
api_response_formatter({"id": 1, "first": "John", "last": "Doe", "email": "JOHN@EXAMPLE.COM"})
# Now `regrest list` shows real input/output examples
# Perfect for API documentation or onboardingregrest list # List all records
regrest list -k calculate # Filter by keywordLists all test records with module, function, arguments, results, and timestamps.
regrest delete abc123def456 # Delete by ID
regrest delete --pattern "test_*" # Delete by pattern
regrest delete --all # Delete all recordsregrest verify # Verify all records
regrest verify -k calculate # Verify only 'calculate' functions
regrest verify --tolerance 0.001 # Custom float toleranceRe-executes all recorded functions with their saved arguments and validates that the outputs match the recorded results. This is useful for:
- Running regression tests in CI/CD pipelines
- Validating that refactoring didn't break existing functionality
- Checking compatibility after dependency updates
Note: Only works with functions defined at module level (not inside test functions or closures).
regrest serve # Start on localhost:8000
regrest serve --port 8080 # Custom port
regrest serve --host 0.0.0.0 # Allow external access
regrest serve --reload # Enable hot reloadAccess the web UI at http://localhost:8000 for:
- Hierarchical view - Organized by module β function β record
- Search & filter - Find records by keyword
- JSONesque display - Syntax-highlighted, readable format
- Record management - Delete individual or all records
graph TB
subgraph "User Code"
A[Decorated Function<br/>@regrest]
end
subgraph "Regrest Core"
B[Decorator<br/>decorator.py]
C[Storage<br/>storage.py]
D[Matcher<br/>matcher.py]
E[Config<br/>config.py]
end
subgraph "Storage Layer"
F[JSON Files<br/>.regrest/*.json]
G[Pickle Serialization<br/>base64 encoded]
end
subgraph "CLI & Server"
H[CLI<br/>typer-based]
I[Web Server<br/>Flask/HTTP]
J[Web UI<br/>Tailwind CSS]
end
A --> B
B --> C
B --> D
B --> E
C --> F
C --> G
H --> C
I --> C
I --> J
style A fill:#e1f5ff
style B fill:#fff4e1
style C fill:#f0e1ff
style D fill:#e1ffe1
style H fill:#ffe1e1
style I fill:#ffe1e1
-
First Run: When you call a function decorated with
@regrest, it executes normally and saves:- Module and function name
- Arguments (args and kwargs)
- Return value
- Timestamp
The record is saved to
.regrest/directory as a JSON file. -
Subsequent Runs: On the next call with the same arguments:
- The function executes
- The result is compared with the recorded value
- If they match β Test passes β
- If they don't match β
RegressionTestErroris raised β
-
Update Mode: When you need to update the expected values:
- Use
@regrest(update=True)orREGREST_UPDATE_MODE=1 - The old record is replaced with the new result
- Use
| Level | Usage | Example |
|---|---|---|
| Global | Configure all tests | from regrest import Config, set_configconfig = Config(storage_dir='.my_records', float_tolerance=1e-6)set_config(config) |
| Per-function | Configure specific function | @regrest(tolerance=1e-9)def precise_calculation(): return 3.141592653589793 |
The matcher intelligently compares:
- Primitives: Exact match for strings, booleans
- Numbers: Tolerance-based for floats, exact for integers
- Collections: Deep comparison for lists, dicts, sets
- Nested structures: Recursive comparison with detailed error messages
Records are stored as JSON files in the .regrest/ directory:
.regrest/
βββ .gitignore # Auto-generated
βββ example.calculate_price.a1b2c3d4.json # Record file
βββ mymodule.process_data.e5f6g7h8.json # Record file
{module}.{function}.{record_id}.json
| Component | Description | Example |
|---|---|---|
module |
Module name where function is defined | example, mymodule |
function |
Function name | calculate_price, process_data |
record_id |
SHA256 hash of arguments (first 16 chars) | a1b2c3d4e5f6g7h8 |
Record ID Generation: Records are uniquely identified by:
- Module name
- Function name
- SHA256 hash of serialized arguments (args + kwargs)
This means different argument combinations create separate records for the same function.
Regrest uses a hybrid encoding approach for maximum compatibility and readability:
| Data Type | Storage Method | Readable | Example |
|---|---|---|---|
| JSON-serializable (int, float, str, bool, list, dict, None) |
JSON | β Yes | {"result": {"type": "json", "data": 270.0}} |
| Non-JSON-serializable (custom classes, complex objects) |
Pickle + Base64 | β No | {"result": {"type": "pickle", "data": "gASV..."}} |
Advantages:
- β Readable: Simple data types are stored as JSON for easy inspection
- β Flexible: Complex objects are automatically pickled
- β Version control friendly: JSON format produces clean diffs
Considerations:
β οΈ Pickle compatibility: May have issues across different Python versionsβ οΈ Custom classes: Must be pickle-serializable (comparison is automatic via__dict__)
Contributions welcome! Run make check before submitting PRs.
MIT License
- Published to PyPI - Install with
pip install regrest - Core decorator functionality (
@regrest) - Hybrid JSON/Pickle storage system
- Smart comparison with floating-point tolerance
- CLI tools (
regrest list,regrest delete) - Custom class support
- Auto
.gitignoregeneration - Environment variable configuration
- Colorful logging output
- Python 3.9+ support
- pyproject.toml-based build system
- ruff + mypy static analysis
- Makefile task automation
- GitHub Actions CI/CD