Errors and validation

A switch statement is a list of claims: these are the keys, these are the actions, this is the fallback. switchlang checks those claims and raises when one is wrong, instead of quietly skipping a case or running the wrong one. This page lists what it checks and when each check runs.

What is checked

Situation Raised When
The same key registered twice (including items of lists and ranges, and keys that compare equal such as 1 and True) ValueError At registration
The action is None ValueError At registration
The action is not callable (often a function that was called instead of passed) ValueError At registration
An empty list as the key ValueError At registration
An unhashable key such as a dict TypeError At registration
closed_range with start >= stop or step < 1 ValueError When closed_range() is called
No case matched and no default() was registered Exception At block exit
Your code raised inside the block that exception At block exit, before any action runs
A case action raised that exception At block exit, from the action

“At registration” means on the s.case(...) line itself, inside the block. The exception leaves the block immediately, and no action runs.

Seeing them

from switchlang import switch, closed_range


def duplicate_key():
    with switch('a') as s:
        s.case('a', lambda: 1)
        s.case('a', lambda: 2)


def action_is_none():
    with switch('a') as s:
        s.case('a', None)


def action_not_callable():
    with switch('a') as s:
        s.case('a', 'not a function')


def empty_list():
    with switch('a') as s:
        s.case([], lambda: 1)


def nothing_matched():
    with switch('z') as s:
        s.case('a', lambda: 1)


def backwards_range():
    closed_range(5, 1)


checks = [duplicate_key, action_is_none, action_not_callable, empty_list, nothing_matched, backwards_range]

for check in checks:
    try:
        check()
    except Exception as e:
        print(f'{check.__name__:20} {type(e).__name__}: {e}')
duplicate_key        ValueError: Duplicate case: a
action_is_none       ValueError: Action for case cannot be None.
action_not_callable  ValueError: Func must be callable.
empty_list           ValueError: You cannot pass an empty collection as the case. It will never match.
nothing_matched      Exception: Value does not match any case and there is no default case: value z
backwards_range      ValueError: Start must be less than stop.

Why “no match” is an error

A dict lookup with .get() returns None when the key is missing. An if / elif chain with no else does nothing. Both are easy to write and easy to not notice. switchlang treats a value that matches nothing, with no default to catch it, as a mistake in the switch, and raises a plain Exception naming the value. If silence is what you want, say so:

s.default(lambda: None)

Exceptions inside the block

If your own code raises while the block is open, the switch is abandoned and the exception propagates unchanged. No case action runs, not even one that had already matched. See How a switch block runs.

If a case action raises, that happens at block exit, when the action runs. The exception propagates from the end of the with statement, and s.result is not set.

Why these are errors and not warnings

Most of these mistakes have the same shape: code that looks right, imports fine, and does the wrong thing on one input. A duplicated key in an if / elif chain is dead code that nothing reports. A function called instead of passed produces a value where an action should be. By raising at the point of registration, switchlang turns each of them into a traceback on the first run of the block, pointing at the line to fix.