Skip to main content

A library to help with the management of alternative implementations of functions

Project description

PyPi Package Build Status Coverage Report Documentation Status

alternative

A tiny, dependency-free library for managing multiple implementations of the same function — especially when you're iterating toward faster or cleaner versions and want those choices to stay explicit, testable, and safe.

Full documentation is available on Read the Docs. The documentation source lives in docs/.

Why use this?

When optimizing a hot path, it’s common to accumulate:

  • a trusted reference implementation
  • one or more candidate rewrites
  • tests to keep them equivalent
  • benchmarks to validate wins

alternative keeps that workflow tidy by making implementation registration and selection first-class.

The same model works for module functions, instance methods, class methods, and static methods. Public typing is shipped in alternative.pyi, so type checkers and IDEs can see the original call signatures instead of losing them behind the decorator objects.

Quick example

import alternative


@alternative.reference
def constant_number():
    return 1


@constant_number.add(default=True)
def alternative_constant_number():
    return 2


@constant_number.add
def unused_alternative_constant_number():
    return 3


# the default=True implementation is used if specified;
# otherwise, the reference is the implicit default
assert constant_number() == 2
# alternative implementations still act like themselves
assert unused_alternative_constant_number() == 3

See the quickstart for registration patterns, defaults, and method examples.

Methods and descriptors

Decorate instance methods directly. For @classmethod and @staticmethod, put @alternative.reference and .add(...) outside the built-in descriptor decorator:

import alternative


class Parser:
    def __init__(self, value: str = ""):
        self.value = value

    @alternative.reference
    def parse(self, value: str) -> int:
        return int(value.strip())

    @parse.add(default=True)
    def parse_fast(self, value: str) -> int:
        return int(value)

    @alternative.reference
    @classmethod
    def from_text(cls, value: str) -> "Parser":
        return cls(value.strip())

    @from_text.add(default=True)
    @classmethod
    def from_text_fast(cls, value: str) -> "Parser":
        return cls(value)

    @alternative.reference
    @staticmethod
    def is_valid(value: str) -> bool:
        return value.strip().isdigit()

    @is_valid.add(default=True)
    @staticmethod
    def is_valid_fast(value: str) -> bool:
        return value.isdigit()

Calling through an instance or class follows normal Python binding rules, and direct implementation calls bind the same way. The full descriptor examples are in Use Methods and Testing Methods.

Pytest features

The pytest helpers are documented in the pytest integration guide.

Pairwise equivalence checks

Use pytest_parametrize_pairs(...) to compare the reference against each candidate implementation.

Single-implementation parametrization

Use pytest_parametrize(...) to run one test body across all implementations.

Runtime tools

Alternatives.measure(...) runs every implementation with the same arguments and measures the results with a callable you provide. See Measure Implementations.

Safety guarantees

The library tries to avoid unpleasant surprises caused by import order or accidental state changes:

  • The selected implementation cannot be changed once it has been used.
  • Implementations cannot be added once they have been inspected, reducing the chance that tests only covered a subset.

Debug mode

Set ALTERNATIVE_DEBUG=1 to record where critical state changes happened (like selecting defaults or inspecting implementations). These locations are surfaced in error messages to make stateful issues easier to track down.

When debug mode is enabled, each Implementation also captures a label with its registration call-site. This label appears in repr(...) and selected debug errors, making it easier to disambiguate implementation instances.

Typing and IDEs

alternative ships a top-level stub file, alternative.pyi, for the public typing surface. It includes overloads for descriptor binding, transparent method/classmethod/staticmethod decoration, and the pytest helpers, while alternative.py stays focused on runtime behavior.

The typing probes are checked with mypy, pyright, pyrefly, and a headless PyCharm inspection script: scripts/pycharm-type-probes.sh. The PyCharm probe covers type assertions, unresolved references, and type checker warnings in typing_tests/type_probes.py.

Known PyCharm caveat: JetBrains PyNestedDecoratorsInspection currently reports a false-positive for correctly typed decorators stacked outside @classmethod or @staticmethod. Runtime behavior and type resolution are correct, and the project does not require # noinspection PyTypeChecker call-site suppressions for these examples.

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

alternative-0.2.0.tar.gz (77.6 kB view details)

Uploaded Source

Built Distribution

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

alternative-0.2.0-py3-none-any.whl (10.8 kB view details)

Uploaded Python 3

File details

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

File metadata

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

File hashes

Hashes for alternative-0.2.0.tar.gz
Algorithm Hash digest
SHA256 ba498997dcff8d52f3a4ad2d73f10417c36bc5fe9b965a55586a3ea5c36af84f
MD5 6390aa5b4e017a5f423f5250c3ff4bce
BLAKE2b-256 7e08114cbad987a4ed231628129c16c704a891a3792f55d4f274bba7d82edb8d

See more details on using hashes here.

Provenance

The following attestation bundles were made for alternative-0.2.0.tar.gz:

Publisher: publish.yml on Code0x58/alternative

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

File details

Details for the file alternative-0.2.0-py3-none-any.whl.

File metadata

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

File hashes

Hashes for alternative-0.2.0-py3-none-any.whl
Algorithm Hash digest
SHA256 76df2a57db0d592c771713a2c1a743de7781144e28d53bff48cfe1387959fe71
MD5 7f05d996c3fe9a12796cdf983ebc7c9a
BLAKE2b-256 28e03284f35242e5bb775bf5e9eff9fdfad8350a675e85b222472fc25c3ff9bb

See more details on using hashes here.

Provenance

The following attestation bundles were made for alternative-0.2.0-py3-none-any.whl:

Publisher: publish.yml on Code0x58/alternative

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