Summary
switch.__exit__ decides whether an exception occurred inside the with block using a truthiness check rather than an identity check:
https://github.com/mikeckennedy/python-switch/blob/master/switchlang/__switchlang_impl.py#L138
def __exit__(self, exc_type, exc_val, exc_tb) -> None: if exc_val: # <-- truthiness, not `is not None` raise exc_val ... for func in self._func_stack: self.__result = func()
If an exception is raised inside the block but its object is falsy (i.e. it defines __bool__/__len__ returning False/0), if exc_val: is False. __exit__ skips the early re-raise, falls through, and runs the matched case action before the exception ultimately propagates via the with-statement machinery (since __exit__ returns None).
The documented and intended contract is that an exception inside the block aborts the switch and no case actions run.
Reproduction
from switchlang import switch class FalsyError(Exception): def __bool__(self): return False visited = [] try: with switch(1) as s: s.case(1, lambda: visited.append("case ran!") or 1) raise FalsyError("boom") except FalsyError: pass print(visited) # ['case ran!'] -> BUG: the case action ran
Control case — a normal (truthy) exception behaves correctly and the case action does not run:
visited2 = [] try: with switch(1) as s: s.case(1, lambda: visited2.append("case ran!") or 1) raise RuntimeError("boom") except RuntimeError: pass print(visited2) # [] -> correct
Expected vs. actual
- Expected: any exception raised in the block aborts the switch; no case actions run.
- Actual: an exception whose object is falsy slips past the guard and the matched case action runs before the exception propagates.
Root cause
if exc_val: invokes the exception's __bool__/__len__. The guard should test for the presence of an exception, not its truthiness.
Proposed fix
if exc_val is not None: raise exc_val
This restores the absolute "no case actions run on exception" guarantee. A regression test pinning the falsy-exception case should accompany the change.
Impact
Low real-world likelihood (it requires an exception type with a falsy __bool__/__len__), but it's a correctness hole in a documented guarantee and a trivial, safe fix.