Skip to main content

logerr

Rust-like Option and Result types for Python with automatic logging

Tests Coverage Type Checked Python 3.12+

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> and Result<T, E> with method chaining
  • 🔗 Full Combinator Set: zip(), flatten(), and_(), or_(), ok(), err() alongside the core map/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

💡 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. logerr extends 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

Metadata

Release files for logerr 0.3.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for logerr 0.3.0
File Size Uploaded
logerr-0.3.0.tar.gz 53.2 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for logerr 0.3.0
File Interpreter ABI Platform
logerr-0.3.0-py3-none-any.whl Python 3 none any Details

Total release size: 113.0 kB

Release files / logerr-0.3.0.tar.gz

Download URL logerr-0.3.0.tar.gz
Size 53.2 kB
Tags Source
SHA-256 checksum
How to use checksums
ee95a097ad4e6ccf3f2eb8ecad05b2480157882485bef343b7c61a6aee71ee2f
BLAKE2b-256 checksum
How to use checksums
e187321d727b69b08e1532c93dc5728158acdd5d85fa6dfbf0b83472f49a9337
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 26, 2026.

Transparency log

Release files / logerr-0.3.0-py3-none-any.whl

Download URL logerr-0.3.0-py3-none-any.whl
Size 59.9 kB
Tags Python 3
SHA-256 checksum
How to use checksums
d265313ef99efbd0e899fef8a58ffb171941a4146a50e8dea264050dd0bd721f
BLAKE2b-256 checksum
How to use checksums
051aa6bbb725b3dc3f470324a98d4c8a01b192e13b09019ac2fdec3414223294
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 26, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

0.3.0 This release

2 release files

0.2.1

2 release files

0.2.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page