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

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

logerr-0.2.1.tar.gz (51.3 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

logerr-0.2.1-py3-none-any.whl (57.9 kB view details)

Uploaded Python 3

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

Hashes for logerr-0.2.1.tar.gz
Algorithm Hash digest
SHA256 04d0cd65859e9e96d8fef035ed7cc8c9b1085233e9f1158877338787d23a2f09
MD5 e97deedbefaf2553813574d79b80c3c8
BLAKE2b-256 d1589aa36c8012e67bd2c21f76a0c022997c59620c0169e1112ffea10dbc1337

See more details on using hashes here.

Provenance

The following attestation bundles were made for logerr-0.2.1.tar.gz:

Publisher: publish.yml on jesserobertson/logerr

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

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

Hashes for logerr-0.2.1-py3-none-any.whl
Algorithm Hash digest
SHA256 0430aa4be5359c135c7c27b1be91255afc4d1f647a4d71eb7ac1afe53dd01093
MD5 40dce68b3cc53ccb0995cdb7ef8b5d66
BLAKE2b-256 14ddfef156dd66cc02eeb85df90334b8c5f13718c74c0100afd855c0f0232fa4

See more details on using hashes here.

Provenance

The following attestation bundles were made for logerr-0.2.1-py3-none-any.whl:

Publisher: publish.yml on jesserobertson/logerr

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page