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.0.tar.gz (50.7 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.0-py3-none-any.whl (57.9 kB view details)

Uploaded Python 3

File details

Details for the file logerr-0.2.0.tar.gz.

File metadata

  • Download URL: logerr-0.2.0.tar.gz
  • Upload date:
  • Size: 50.7 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.0.tar.gz
Algorithm Hash digest
SHA256 d7e6c3207e8762332818f52eb2741a19b550e2ccd00b0383f1f831975722793b
MD5 4fcd220530c62ea5162fd8266cbbdd2e
BLAKE2b-256 3ee7bd9d9ef13ff26c7374ca6ff05e05042008dd05533184d05f761cb10ec359

See more details on using hashes here.

Provenance

The following attestation bundles were made for logerr-0.2.0.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.0-py3-none-any.whl.

File metadata

  • Download URL: logerr-0.2.0-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.0-py3-none-any.whl
Algorithm Hash digest
SHA256 4a6a8b7b74e3364ea088d869fe5403355577f8f9a88b5ab8a1f22f70bd6bb1a4
MD5 bbab4386905a7e8612d476c7b26cb6c6
BLAKE2b-256 a76be94a3d5dab9df440838dde85adcd98a0a24bb832bfa768dcb4995d826c7f

See more details on using hashes here.

Provenance

The following attestation bundles were made for logerr-0.2.0-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