logerr
Rust-like Option and Result types for Python with automatic logging
logerr brings the power of Rust's Option<T> and Result<T, E> types to Python, with automatic logging of error cases using loguru. Write clean, functional error-handling code while maintaining excellent observability.
🌟 Features
- 🦀 Rust-like Types: Familiar
Option<T>andResult<T, E>with method chaining - 🔗 Full Combinator Set:
zip(),flatten(),and_(),or_(),ok(),err()alongside the coremap/then/filter - 🪵 Automatic Logging: Error cases logged automatically with configurable levels
- ⚙️ Highly Configurable: Per-library settings via confection
- 🔒 Type Safe: Full mypy support with proper generic types
- 🧪 Well Tested: 500+ tests including property-based tests and comprehensive doctests
- 🚀 Clean API: Discoverable, IDE-friendly interface
🚀 Quick Start
>>> from logerr import Result, Ok, Err, Some, Nothing
>>> # Simple successful case
>>> success = Ok(42)
>>> success.map(lambda x: x * 2).unwrap()
84
>>> # Error case with fallback
>>> error = Err("something failed")
>>> error.unwrap_or("default value")
'default value'
>>> # Chain operations elegantly
>>> Ok("hello").map(str.upper).map(len).unwrap()
5
✨ The key difference: Errors are automatically logged with full context - no manual logging required!
See it in action - no manual logging call, just automatic capture of the error context:
>>> import sys
>>> from loguru import logger
>>> import logerr
>>> _ = logerr.configure(enabled=True, level="ERROR") # deterministic for this demo
>>> handler_id = logger.add(sys.stdout, format="{level} | {message}")
>>> from logerr import Err
>>> _ = Err("Database connection failed") # doctest: +ELLIPSIS
ERROR | Result error in ...Database connection failed
>>> _ = logger.remove(handler_id)
>>> logerr.reset_config()
>>> from logerr import Option
>>> # Work with optional values using functional pipeline
>>> user_data = {"name": "Alice"}
>>> # Functional pipeline for nullable values
>>> contact = (
... Option.from_nullable(user_data.get("email"))
... .filter(lambda email: "@" in email) # Validate email format
... .unwrap_or("no-email@example.com")
... )
>>> print(contact)
no-email@example.com
>>> # Chain operations elegantly with automatic error handling
>>> processed = (
... Ok("hello world")
... .map(str.upper) # Ok("HELLO WORLD")
... .map(lambda s: s.split()) # Ok(["HELLO", "WORLD"])
... .map(len) # Ok(2)
... .unwrap_or(0) # 2
... )
>>> print(processed)
2
📦 Installation
Currently available from source:
git clone https://github.com/jesserobertson/logerr
cd logerr
pip install -e .
Optional Features
Recipes Module: Advanced patterns and utilities for specialized use cases:
# Install retry patterns (tenacity)
pixi run -e retry # or: pip install "logerr[retry]"
# Install dataframe/table conversion utilities (pymongo, pandas)
pixi run -e tables # or: pip install "logerr[tables]"
# Use in your code
from logerr.recipes import retry, config
@retry.on_err() # retries on Err, default: 3 attempts with exponential backoff
def flaky_operation() -> Result[int, str]:
return Ok(42)
# Functional utilities (no extra install needed - these are core)
from logerr.utilities import validate, pipe, try_chain
config.configure_advanced({"libraries": {"my_module": {"level": "DEBUG"}}})
# NoSQL to DataFrame conversion with data quality logging
import pandas as pd
from logerr.recipes.dataframes import Required, from_mongo
schema = {"user_id": Required[str], "email": Required[str], "name": str}
df = from_mongo(db.users, {"status": "active"}, schema=schema).unwrap_or(pd.DataFrame())
🔍 Why logerr?
See the Difference
Traditional approach (manual logging required):
>>> import json
>>> def load_config():
... try:
... with open("config.json") as f:
... return json.load(f)
... except Exception as e:
... print(f"Failed to load config: {e}") # Manual logging
... return None
>>> config = load_config() # doctest: +SKIP
>>> if config is None: # doctest: +SKIP
... print("Using defaults") # doctest: +SKIP
With logerr (automatic logging + functional style):
>>> from logerr import Result
>>> from logerr.utilities import execute
>>> import json
>>> def load_config():
... return execute(lambda: json.load(open("config.json")))
>>> # Functional pipeline with error recovery
>>> config = (
... load_config()
... .unwrap_or({}) # Fallback to empty config
... )
>>> print(type(config))
<class 'dict'>
Traditional Tradeoffs vs logerr
| Approach | Pros | Cons |
|---|---|---|
| Exceptions | Clear error info | Hard to follow, requires try/catch |
| None returns | Simple | Loses error context, silent failures |
| Tuple returns | Explicit | Verbose, easy to misuse |
| 🦀 logerr | Explicit + Automatic logging + Composable + Type safe | Learning curve |
logerr gives you the best of all worlds:
✅ Explicit error handling like Go or Rust
✅ Composable operations through method chaining
✅ Automatic observability without manual logging
✅ Type safety that catches errors at development time
📖 Documentation
- Getting Started - Learn the basics
- Result Types - Handle operations that might fail
- Option Types - Work with nullable values
- Configuration - Customize logging behavior
- API Reference - Complete API documentation
💡 Examples
Here's a "full-stack" pipeline that combines retry logic, NoSQL loading, and schema validation - and gets observability into all three for free:
import pandas as pd
from logerr import Result, configure
from logerr.recipes import retry
from logerr.recipes.dataframes import Required, from_mongo
configure(level="INFO") # surface retry attempts and data quality issues as they happen
# Required fields error if missing; other fields are optional by default
schema = {"user_id": Required[str], "email": Required[str], "age": int}
@retry.on_err() # retries on connection failure: 3 attempts, exponential backoff
def load_active_users() -> Result:
return from_mongo(
db.users, {"status": "active"}, schema=schema, report_name="active_users"
)
df = load_active_users().unwrap_or_else(lambda error: pd.DataFrame())
# What you'd see in the logs (abbreviated - one ERROR line is logged per
# missing occurrence, not aggregated):
#
# INFO | load_active_users succeeded after 2 attempts
# ERROR | Missing required field 'email' in document
# INFO | Data Quality Summary for 'active_users': 1847/2000 records processed successfully (92.4% success rate)
# WARNING | Field 'age': 43/2000 missing (2.2% missing rate)
No manual logging calls anywhere in that pipeline - the retry attempts, the missing-field errors, and the data quality summary are all captured automatically.
See the Examples guide for more: web applications, file processing pipelines, configuration management, and circuit-breaker patterns.
⚙️ Configuration
Core Configuration (simple and lightweight):
>>> import logerr
>>> # Basic configuration - just the essentials
>>> result = logerr.configure(enabled=True, level="WARNING")
>>> result.is_ok()
True
>>> # Just change log level
>>> result = logerr.configure(level="INFO")
>>> result.is_ok()
True
Advanced Configuration (per-library settings, custom formats, file-based
config — no extra install needed, confection is already a core dependency;
this just lives under logerr.recipes.config to keep the top-level API
small):
from logerr.recipes.config import configure_advanced, configure_from_confection
configure_advanced({
"level": "WARNING",
"libraries": {
"myapp.database": {"level": "ERROR"},
"myapp.api": {"level": "DEBUG"},
"third_party_lib": {"enabled": False},
},
"capture_locals": True,
})
# Or load the same settings from a confection config file (see the
# Configuration guide for the expected [logerr] file format)
configure_from_confection("config.cfg")
🧪 Development
This project uses pixi for development:
# Install dependencies
pixi install
# Install with retry/tables extras for advanced patterns and utilities
pixi install -e retry
pixi install -e tables
# Run fast tests
pixi run -e dev test fast
# Run all tests, including doctests
pixi run -e dev test all
# Type checking
pixi run -e dev quality typecheck
# Build documentation (default environment - combines dev + docs features)
pixi run docs build
# Serve documentation locally
pixi run docs serve
🤝 Contributing
Contributions are welcome! Please feel free to submit a Pull Request. For major changes, please open an issue first to discuss what you would like to change.
📄 License
This project is licensed under the MIT License - see the LICENSE file for details.
🙏 Acknowledgments
This project builds upon excellent prior work:
- MaT1g3R/option - The original Python implementation of Rust-like Option and Result types that inspired this project.
logerrextends their elegant API design with automatic logging capabilities. - Rust's std::option and std::result - The foundational design patterns and method names
- loguru - The excellent logging library that powers our automatic error logging
- confection - Flexible configuration management system
- tenacity - Robust retry library that powers our retry decorators and resilient operations
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file logerr-0.2.1.tar.gz.
File metadata
- Download URL: logerr-0.2.1.tar.gz
- Upload date:
- Size: 51.3 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
04d0cd65859e9e96d8fef035ed7cc8c9b1085233e9f1158877338787d23a2f09
|
|
| MD5 |
e97deedbefaf2553813574d79b80c3c8
|
|
| BLAKE2b-256 |
d1589aa36c8012e67bd2c21f76a0c022997c59620c0169e1112ffea10dbc1337
|
Provenance
The following attestation bundles were made for logerr-0.2.1.tar.gz:
Publisher:
publish.yml on jesserobertson/logerr
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
logerr-0.2.1.tar.gz -
Subject digest:
04d0cd65859e9e96d8fef035ed7cc8c9b1085233e9f1158877338787d23a2f09 - Sigstore transparency entry: 2482572496
- Sigstore integration time:
-
Permalink:
jesserobertson/logerr@f61f78291eb6f2773b8a9c13f1e1377f63c4ee29 -
Branch / Tag:
refs/heads/main - Owner: https://github.com/jesserobertson
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@f61f78291eb6f2773b8a9c13f1e1377f63c4ee29 -
Trigger Event:
workflow_dispatch
-
Statement type:
File details
Details for the file logerr-0.2.1-py3-none-any.whl.
File metadata
- Download URL: logerr-0.2.1-py3-none-any.whl
- Upload date:
- Size: 57.9 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
0430aa4be5359c135c7c27b1be91255afc4d1f647a4d71eb7ac1afe53dd01093
|
|
| MD5 |
40dce68b3cc53ccb0995cdb7ef8b5d66
|
|
| BLAKE2b-256 |
14ddfef156dd66cc02eeb85df90334b8c5f13718c74c0100afd855c0f0232fa4
|
Provenance
The following attestation bundles were made for logerr-0.2.1-py3-none-any.whl:
Publisher:
publish.yml on jesserobertson/logerr
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
logerr-0.2.1-py3-none-any.whl -
Subject digest:
0430aa4be5359c135c7c27b1be91255afc4d1f647a4d71eb7ac1afe53dd01093 - Sigstore transparency entry: 2482572554
- Sigstore integration time:
-
Permalink:
jesserobertson/logerr@f61f78291eb6f2773b8a9c13f1e1377f63c4ee29 -
Branch / Tag:
refs/heads/main - Owner: https://github.com/jesserobertson
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@f61f78291eb6f2773b8a9c13f1e1377f63c4ee29 -
Trigger Event:
workflow_dispatch
-
Statement type: