Skip to main content

Tiny utilities for raising exceptions on a condition and rendering them as readable strings

Project description

exceptly

PyPI Python Tests License

Tiny utilities for raising exceptions on a condition and rendering them as readable strings.

Installation

pip install exceptly

Requires Python 3.11+.

Why exceptly?

Two small annoyances show up in every Python codebase:

  • Guard clauses take three lines. if not value: raise ValueError("missing") is four tokens of logic wrapped in ceremony. raise_if_not(value, ValueError, "missing") is the same thing on one line, with the exception type spelled out up front where the reader can see it.
  • repr(e) and str(e) are both wrong for logs. repr(e) gives you ValueError('missing') (quoted, parenthesised, noisy). str(e) gives you 'missing' — no type at all. repr_exception(e) gives you ValueError: missing — the form humans actually want in log lines and assertion messages.

No dependencies, no configuration, no magic — three functions that do one thing each.

Quick Start

from exceptly import raise_if, raise_if_not, repr_exception


def set_positive(value: int) -> None:
    raise_if_not(isinstance(value, int), TypeError, f"expected int, got {type(value).__qualname__}")
    raise_if(value <= 0, ValueError, "expected a positive value, got", value)
    # ... actual work


try:
    set_positive(None)
except TypeError as e:
    assert repr_exception(e) == "TypeError: expected int, got NoneType"

try:
    set_positive(0)
except ValueError as e:
    assert repr_exception(e) == "ValueError: ('expected a positive value, got', 0)"

Overview

raise_if | raise_if_not | repr_exception


raise_if

Raises the given exception when condition is truthy; does nothing otherwise.

Use when: you want an inline guard clause without the if ...: raise ... boilerplate, and you want the exception type to be visible at the start of the call rather than buried inside the if body.

from exceptly import raise_if

# Pass a pre-built exception instance:
raise_if(x < 0, ValueError("negative value"))

# Or pass the class and its constructor args — the instance is only built if the condition fires:
raise_if(x < 0, ValueError, "negative value")
raise_if(x < 0, ValueError, "negative value", x)  # ValueError('negative value', -1)

The two forms are equivalent, but the class-plus-args form avoids constructing the exception when the guard does not trip — useful when the message is expensive to format (e.g. repr of a large object).

raise_if_not

Inverse of raise_if: raises when condition is falsy.

Use when: the natural phrasing of the guard is positive ("must be non-empty", "must be an int") rather than negative. Also a drop-in replacement for assert in code paths where assert is unreliable (stripped by python -O, silent in production configs).

from exceptly import raise_if_not

raise_if_not(isinstance(value, int), TypeError, f"expected int, got {type(value).__qualname__}")
raise_if_not(items, ValueError("items must not be empty"))

repr_exception

Renders an exception as "ClassName: message", or "ClassName" when the exception carries no message.

Use when: writing log lines, assertion messages, or error responses. repr(e) is cluttered (ValueError('missing')); str(e) loses the type ('missing'); f"{type(e).__name__}: {e}" is the hand-rolled version you end up writing anyway.

from exceptly import repr_exception

repr_exception(ValueError("not found"))     # "ValueError: not found"
repr_exception(RuntimeError())              # "RuntimeError"
repr_exception(ValueError(1, 2, 3))         # "ValueError: (1, 2, 3)"


class DomainError(Exception):
    pass


repr_exception(DomainError("boom"))         # "DomainError: boom"

The output is plain text, safe for logs and assertion messages, and never raises.

License

MIT

Project details


Download files

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

Source Distribution

exceptly-1.0.0.tar.gz (5.6 kB view details)

Uploaded Source

Built Distribution

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

exceptly-1.0.0-py3-none-any.whl (4.7 kB view details)

Uploaded Python 3

File details

Details for the file exceptly-1.0.0.tar.gz.

File metadata

  • Download URL: exceptly-1.0.0.tar.gz
  • Upload date:
  • Size: 5.6 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for exceptly-1.0.0.tar.gz
Algorithm Hash digest
SHA256 33af87b0b0d99db28066fc5ac0cf7405980492ee1792e46c1e9fb4d5ab6bde83
MD5 f0c4ae3053b0ba4a515b64d6eef6bdf8
BLAKE2b-256 99561057876729b66f14dff39a7d654434686491685e2b0b9730b9876564c823

See more details on using hashes here.

Provenance

The following attestation bundles were made for exceptly-1.0.0.tar.gz:

Publisher: publish-pypi.yml on miriada-io/exceptly

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

File details

Details for the file exceptly-1.0.0-py3-none-any.whl.

File metadata

  • Download URL: exceptly-1.0.0-py3-none-any.whl
  • Upload date:
  • Size: 4.7 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for exceptly-1.0.0-py3-none-any.whl
Algorithm Hash digest
SHA256 372e3e9bddd4b83336e9a51009b00f576c18736663211baf381c08a3c052b52d
MD5 ee732639715abf844b93d8c2f780333e
BLAKE2b-256 87ffec02b539144fcf042226d6f8f7ebf233259565a9cd94e86fb16e0cba4b85

See more details on using hashes here.

Provenance

The following attestation bundles were made for exceptly-1.0.0-py3-none-any.whl:

Publisher: publish-pypi.yml on miriada-io/exceptly

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 Pingdom Monitoring Sentry Error logging StatusPage Status page