Skip to main content

Sovic

sovic is a typed Python utility package for explicit error handling, collection helpers, and small composable tools. It starts with a Result-style API for code that should return either a successful value or an error value without raising at the call site.

The package is inspired by Result types commonly found in languages such as Rust:

  • Ok(value) represents a successful operation.
  • Err(error) represents a failed operation.
  • Result[T, E] is the union of Ok[T] | Err[E].
  • ResultProtocol[T, E] describes the shared Result interface.
  • to_result converts selected exceptions raised by a function into Err(Exception).
  • map, map_err, and and_then help transform and chain Result values.

Why Sovic?

Python exceptions are useful, but they can make expected failures implicit. sovic is for functions where failure is part of the normal return path, such as validation, parsing, request handling, or adapters around exception-raising code.

Use sovic when you want:

  • explicit success and error branches in function signatures;
  • simple pattern matching with Ok(value) and Err(error);
  • typed error handling without adding runtime dependencies;
  • small composable helpers for transforming and chaining results.

Installation

This project is managed with uv and requires Python 3.12 or newer.

uv sync --all-groups

Basic Usage

from sovic import Err, Ok, Result


def parse_user_id(raw: str) -> Result[int, str]:
    if raw.isdigit():
        return Ok(int(raw))

    return Err("user id must be numeric")


result = parse_user_id("42")

if result.is_ok():
    user_id = result.ok()
else:
    message = result.err()

Transforming Results

Use map() to transform a successful value, map_err() to transform an error, and and_then() to chain another operation that returns a Result.

from sovic import Err, Ok, Result


def parse_user_id(raw: str) -> Result[int, str]:
    if raw.isdigit():
        return Ok(int(raw))

    return Err("user id must be numeric")


def require_positive(user_id: int) -> Result[int, str]:
    if user_id > 0:
        return Ok(user_id)

    return Err("user id must be positive")


result = (
    parse_user_id("42")
    .map(lambda user_id: user_id + 1)
    .and_then(require_positive)
    .map_err(lambda error: f"invalid user id: {error}")
)

Pattern Matching

Ok and Err can be used with Python pattern matching.

from sovic import Err, Ok


match parse_user_id("abc"):
    case Ok(value):
        print(f"parsed id: {value}")
    case Err(error):
        print(f"invalid input: {error}")

Converting Exceptions

Use to_result when you want a function that may raise an exception to return a Result instead.

from sovic import Err, Ok, to_result


@to_result()
def divide(a: float, b: float) -> float:
    return a / b


match divide(8, 0):
    case Ok(value):
        print(value)
    case Err(error):
        print(f"failed: {error}")

Without arguments, to_result() catches Exception and returns Err(Exception). It does not catch BaseException, so interrupts such as KeyboardInterrupt and SystemExit are not swallowed.

Pass exception types to catch only expected failures. Exceptions that are not selected continue to propagate.

from sovic import to_result


@to_result(ValueError, TypeError)
def parse_number(value: str) -> int:
    return int(value)

API Reference

Ok[T]

Successful result container.

  • ok() -> T: returns the contained value.
  • err() -> Never: raises ResultValueError.
  • unwrap() -> T: returns the contained value.
  • unwrap_err() -> Never: raises ResultValueError.
  • is_ok() -> bool: returns True.
  • is_err() -> bool: returns False.
  • map(func) -> Ok[U]: returns Ok(func(value)).
  • map_err(func) -> Ok[T]: returns the original Ok.
  • and_then(func) -> Result[U, E]: returns func(value).

Err[E]

Failed result container.

  • ok() -> Never: raises ResultValueError.
  • err() -> E: returns the contained error.
  • unwrap() -> Never: raises ResultValueError.
  • unwrap_err() -> E: returns the contained error.
  • is_ok() -> bool: returns False.
  • is_err() -> bool: returns True.
  • map(func) -> Err[E]: returns the original Err.
  • map_err(func) -> Err[F]: returns Err(func(error)).
  • and_then(func) -> Err[E]: returns the original Err.

Result[T, E]

Type alias for Ok[T] | Err[E].

ResultProtocol[T, E]

Structural protocol for objects that expose the same Result-style methods as Ok and Err.

to_result

Decorator factory that converts a callable from Callable[P, T] into Callable[P, Result[T, Exception]]. Use to_result() to catch every Exception, or pass one or more exception types to catch selected failures.

Development

uv run pytest
uv run ruff check .
uv run ruff format .
uv run pyrefly check

Metadata

Release files for sovic 0.2.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 sovic 0.2.0
File Size Uploaded Provenance Actions
sovic-0.2.0.tar.gz 5.6 kB Without provenance Details for sovic-0.2.0.tar.gz

Built distribution (wheel)

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

Total release size: 11.5 kB

Release files / sovic-0.2.0.tar.gz

Download URL sovic-0.2.0.tar.gz
Size 5.6 kB
Tags Source
SHA-256 checksum
How to use checksums
a5f0567dec1a30ecf81e67e75b1bca02c0a8acb525651ffed69814bf1d8bc653
BLAKE2b-256 checksum
How to use checksums
420af1afd3c58563f62a1fc7a2f5338e4bcff5ff319360617e67cdb584294105
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.11.21 {"installer":{"name":"uv","version":"0.11.21","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

Release files / sovic-0.2.0-py3-none-any.whl

Download URL sovic-0.2.0-py3-none-any.whl
Size 5.9 kB
Tags Python 3
SHA-256 checksum
How to use checksums
1948badc85ac91589c38ae6e914fbc44aa90638b9f63b721c227660097bcc774
BLAKE2b-256 checksum
How to use checksums
5394d7ea70be27b6d25d1b8fefa014bd8a3cc46cca14614b96eb02e69a754660
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.11.21 {"installer":{"name":"uv","version":"0.11.21","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

Security for sovic 0.2.0

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.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Release history Release notifications | RSS feed

This release

0.2.0 This release

2 release files No provenance

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