switchlang

repository·master·Indexed 42 days ago

https://github.com/mikeckennedy/python-switch

A Python module that adds explicit switch-case functionality using the `with` statement. It provides features not natively available in standard Python `match` or `dict` dispatch, including range matching via `closed_range()`, fall-through support, and result capture via the `.result` attribute. Compatible with Python 3.9 and above.

Tokens
5.8K
Snippets
24
Records
33
Agent score
50%

What's inside switchlang

  1. Enable fall-through behavior in switch cases

    master

    By default, switchlang cases are exclusive. To allow a matched case to execute the subsequent case as well, set fallthrough=True in the s.case() method. When multiple cases execute due to fall-through, the return value of the last executed function is what is stored in s.result.

    value = 2
    
    with switch(value) as s:
        s.case(1, lambda: 'one')
        s.case(2, lambda: 'two', fallthrough=True)
        s.case(3, lambda: 'three')  # runs because of fallthrough
        s.default(lambda: 'other')
    
    print(s.result)  # 'three'
  2. How fall-through works in switchlang

    master

    Fall-through is an opt-in feature. If a case is registered with fallthrough=True, the next registered case will execute regardless of whether its key matches. The execution chain continues until it reaches a case where fallthrough is not True. The s.result will contain the return value of the last function executed in the chain.

    Note: Pass only True or False to the fallthrough argument. None is reserved for internal library use.

    value = 2
    with switch(value) as s:
        s.case(1, lambda: 'one')
        s.case(2, lambda: 'two', fallthrough=True)
        s.case(3, lambda: 'three')     # runs too (its key is never compared), then the chain stops
        s.default(lambda: 'other')
    
    s.result  # 'three'
  3. Understand switchlang exception behavior

    master
    In version 0.1.3 and later, any exception raised inside a switch with block is guaranteed to abort the switch immediately. Even if the exception object is falsy (e.g., an exception that evaluates to False in a boolean context), the switch will stop, and no further case actions will be executed before the exception propagates.
  4. Key matching: equality, hashability, and ranges

    master

    Matching is performed using plain equality (key == value). Because keys are stored in a set, they must be hashable.

    Key Behaviors:

    • Collisions: Keys that compare equal are treated as duplicates (e.g., 1, True, and 1.0 will collide).
    • Lists/Ranges: Providing a list or range as a key expands it so that each item becomes an individual case for the same action.
    • Closed Ranges: closed_range(start, stop) is inclusive of both the start and stop values.
    • Range Overlap Warning: Because ranges are inclusive, adjacent ranges must not overlap. For example, to cover 1-5 and 6-9, use closed_range(1, 5) and closed_range(6, 9). Using closed_range(1, 5) and closed_range(5, 9) will raise a duplicate-case error because 5 is present in both.
  5. How the switch block execution model works

    master

    The switch context manager implements an explicit switch statement where registration and execution are decoupled:

    1. Registration Phase: Inside the with switch(value) as s: block, calling s.case() or s.default() only registers the case. No logic is executed at the moment of registration.
    2. Execution Phase: The matched function (and any subsequent fall-through functions) runs only when the with block exits (during __exit__).
    3. Result Retrieval: The return value of the executed case is stored in s.result. This attribute is only readable after the with block has exited. Attempting to read s.result inside the block will raise an error.

    Critical Requirement for Case Functions: Because execution happens at exit, case functions must take zero parameters. To pass data into a case, you must use a lambda or a closure to bind the arguments at registration time.

    Incorrect: s.case('key', process(val)) (This calls process immediately and registers its return value). Correct: s.case('key', lambda: process(val)) (This registers a function that will be called later).

    from switchlang import switch
    
    val = 'a'
    with switch(val) as s:
        s.case('a', lambda: print('Matched!'))
    # Execution happens here, at the end of the block
  6. Install dev optional dependencies for python-switch

    master

    The dev dependency group includes tools for testing and documentation. If you are contributing to the project or running its test suite, you may need to install these optional dependencies.

    • pytest: Used for running tests.
    • great-docs: Used for documentation (requires python_version >= "3.11").
    # Example command to install dev dependencies (assuming use of pip)
    pip install "switchlang[dev]"
  7. Use the switch context manager

    master

    Use the switch context manager to define a value-based dispatch block. Register cases using s.case() and a fallback using s.default(). Always register s.default() last to ensure it acts as a true catch-all.

    from switchlang import switch, closed_range
    
    action = get_action()
    
    with switch(action) as s:
        s.case(['c', 'a'], create_account)              # Multiple keys map to one action
        s.case('l', log_into_account)                    # Single key match
        s.case('', lambda: None)                         # Explicit 'do nothing' case
        s.case(closed_range(1, 5), lambda: set_level(action)) # Inclusive numeric range
        s.default(unknown_command)                       # Fallback (register last)
    
    print(s.result)   # Access the return value of the matched case after exit
    from switchlang import switch, closed_range
    
    action = get_action()
    
    with switch(action) as s:
        s.case(['c', 'a'], create_account)              # several keys -> one action
        s.case('l', log_into_account)
        s.case('', lambda: None)                        # explicit "do nothing" case
        s.case(closed_range(1, 5), lambda: set_level(action))  # inclusive 1..5
        s.default(unknown_command)                      # ALWAYS register last
    
    print(s.result)   # return value of whichever case ran — only valid here, after the block
  8. Implement a minimal switch block

    master

    To use switchlang, wrap your target value in a with switch(value) as s: block. Inside the block, register cases using s.case(key, action) and a fallback using s.default(action). The result of the executed case is accessed via s.result after the with block has exited.

    Important Rules:

    • Registration vs Execution: Calling s.case() only registers the case; it does not execute the function. Execution happens when the with block exits.
    • Zero-parameter Actions: Because functions are executed during block exit, they must take zero parameters. To pass data to an action, use a lambda or a closure.
    • Result Access: s.result is only valid and readable after the block closes. Attempting to read it inside the block will raise an error.
    from switchlang import switch, closed_range
    
    action = get_action()
    
    with switch(action) as s:
        s.case(['c', 'a'], create_account)              # several keys -> one action
        s.case('l', log_into_account)
        s.case('', lambda: None)                        # explicit "do nothing" case
        s.case(closed_range(1, 5), lambda: set_level(action))  # inclusive 1..5
        s.default(unknown_command)                      # ALWAYS register last
    
    print(s.result)   # return value of whichever case ran
  9. When to use switchlang vs Python's match statement

    master

    Choose switchlang over Python's native match (introduced in 3.10) when you need:

    • Value-based dispatching: match is designed for structural pattern matching (destructuring shapes), while switchlang is designed for dispatching on specific values.
    • Opt-in fall-through: Native match does not support fall-through behavior.
    • Dynamic case lists: You can build case lists at runtime (e.g., s.case(some_list, action)).
    • Captured return values: Easily retrieve the result of the matched case via s.result.
    • Python 3.9 support: switchlang works on versions earlier than 3.10.

    Choose match when performing structural pattern matching on tuples, dataclasses, or nested data structures.