Skip to content

API Reference

The entire implementation is a C extension (conditional_method._c); cfg is a thin import shim.

cfg module

from conditional_method import (
    cfg,
    if_,
    cm,
    cfg_attr,
    assert_all_true,
    _get_failed,
    debug,
    debug_enabled,
)

@cfg(condition=...)

Conditional method selection. Aliases: @cm, @if_ — all three are the same object (cm is if_ is cfg is True).

  • condition: bool | Callable[[Callable], bool] — required.
  • Use as a factory (@cfg(condition=...)) or directly (cfg(func, condition=...)).

@cfg_attr(condition=..., decorators=[...])

Conditionally apply decorators.

  • condition: bool | Callable[[Callable], bool] — required.
  • decorators: Sequence[Callable] — applied in order when true.

debug(message) / debug_enabled() -> bool

Opt-in C debug logging, gated by the __conditional_method_debug__ environment variable (any value other than "false" enables it).

export __conditional_method_debug__=true

_get_mod_qual_func_name(func) -> str

Internal helper returning module.qualname for a function, unwrapping __wrapped__ / __func__ / fget as needed. Raises TypeError when no name can be determined.

assert_all_true() -> None

Eager module-level validation. Selection is per name, so a decorated name fails only when all of its candidate implementations are false; a single true candidate is enough.

Raises TypeError naming every decorated name that ended up as a _TypeErrorRaiser with no condition=True winner (i.e. every name whose candidates were all false). A name with at least one true candidate is never reported. Returns None when every decorated name is satisfied.

Call it as the last line of a config/feature-flag module to fail fast at import time instead of at first call:

from conditional_method import cfg, assert_all_true


# One true candidate is enough, so `work` is NOT reported.
@cfg(condition=ENV == "production")
def work(): ...


@cfg(condition=ENV == "development")
def work(): ...


# Every candidate for this name is false, so `broken` IS reported.
@cfg(condition=False)
def broken(): ...


assert_all_true()  # TypeError naming `broken` only

_get_failed() -> list[str]

Returns the list of qualified names whose decorated condition ended up with no true winner (i.e. that became a _TypeErrorRaiser; empty when all conditions are true). Useful for introspection and tests; it is what assert_all_true() checks under the hood.

The recorded failures are append-only per name: _failed_qualnames grows as false-only names are decorated and is not wiped when a raiser is created or called. A name is removed from the list only when a later condition=True winner for that same name resolves it (or when the set is cleared explicitly, e.g. conditional_method._c._failed_qualnames.clear()). This means assert_all_true()/_get_failed() report every failing name across the module/process, not just the most recent one.

conditional_method._c internals

Exposed for testing only (names prefixed with _ are not part of the public API):

Name Purpose
_cm_cache / _cfg_attr_cache module-level implementation caches; values are weakrefs to true-condition winners (and strong refs to _TypeErrorRaiser placeholders), so they do not pin functions/modules alive after their class is collected. Swept of dead entries once they exceed an internal high-water mark
_TypeErrorRaiser placeholder object raising TypeError on call/__set_name__
_CfgCallable callable heap type wrapping the module aliases (cm._cache)
_raise_exec create a _TypeErrorRaiser
_cm_wrapper / cfg_attr_wrapper internal decorator wrappers
_failed_qualnames append-only set of names with no true winner (backing _get_failed/assert_all_true)
set_alloc_fail_count test-only (PY_CFG_TESTING builds) allocation-failure injection

Errors

Situation Raised
@cfg with no condition TypeError
@cfg used without brackets TypeError
no condition true at class build TypeError: None of the conditions is true for ...
condition callable raises TypeError TypeError: Error calling \condition` for ...`
cfg_attr with a non-sequence decorators TypeError: decorators must be a sequence
cfg_attr with no condition ValueError / TypeError

See Errors for details.