1+ import uuid
2+ from typing import Callable , Any
3+
4+
5+ class switch :
6+ """
7+ python-switch is a module-level implementation of the switch statement for Python.
8+ See https://github.com/mikeckennedy/python-switch for full details.
9+ Copyright Michael Kennedy (https://twitter.com/mkennedy)
10+ """
11+ __no_result = uuid .uuid4 ()
12+ __default = uuid .uuid4 ()
13+
14+ def __init__ (self , value ):
15+ self .value = value
16+ self .cases = set ()
17+ self ._found = False
18+ self .__result = switch .__no_result
19+ self ._falling_through = False
20+ self ._func_stack = []
21+
22+ def default (self , func : Callable [[], Any ]):
23+ """
24+ Use as option final statement in switch block.
25+
26+ with switch(val) as s:
27+ s.case(...)
28+ s.case(...)
29+ s.default(function)
30+
31+ :param func: Any callable taking no parameters to be executed if this (default) case matches.
32+ :return: None
33+ """
34+ self .case (switch .__default , func )
35+
36+ def case (self , key , func : Callable [[], Any ], fallthrough = False ):
37+ """
38+ Specify a case for the switch block:
39+
40+ with switch(val) as s:
41+ s.case('a', function)
42+ s.case('b', function, fallthrough=True)
43+ s.default(function)
44+
45+ :param key: Key for the case test (if this is a list or range, the items will each be added as a case)
46+ :param func: Any callable taking no parameters to be executed if this case matches.
47+ :param fallthrough: Optionally fall through to the subsequent case (defaults to False)
48+ :return:
49+ """
50+ if fallthrough is not None :
51+ if self ._falling_through :
52+ self ._func_stack .append (func )
53+ if not fallthrough :
54+ self ._falling_through = False
55+
56+ if isinstance (key , list ) or isinstance (key , range ):
57+ found = False
58+ for i in key :
59+ if self .case (i , func , fallthrough = None ):
60+ found = True
61+ if fallthrough is not None :
62+ self ._falling_through = fallthrough
63+ return found
64+
65+ if key in self .cases :
66+ raise ValueError ("Duplicate case: {}" .format (key ))
67+ if not func :
68+ raise ValueError ("Action for case cannot be None." )
69+ if not callable (func ):
70+ raise ValueError ("Func must be callable." )
71+
72+ self .cases .add (key )
73+ if key == self .value or not self ._found and key == self .__default :
74+ self ._func_stack .append (func )
75+ self ._found = True
76+ if fallthrough is not None :
77+ self ._falling_through = fallthrough
78+ return True
79+
80+ def __enter__ (self ):
81+ return self
82+
83+ def __exit__ (self , exc_type , exc_val , exc_tb ):
84+ if exc_val :
85+ raise exc_val
86+
87+ if not self ._func_stack :
88+ raise Exception ("Value does not match any case and there "
89+ "is no default case: value {}" .format (self .value ))
90+
91+ for func in self ._func_stack :
92+ # noinspection PyCallingNonCallable
93+ self .__result = func ()
94+
95+ @property
96+ def result (self ):
97+ if self .__result == switch .__no_result :
98+ raise Exception ("No result has been computed (did you access "
99+ "switch.result inside the with block?)" )
100+
101+ return self .__result
102+
103+
104+ def closed_range (start : int , stop : int , step = 1 ) -> range :
105+ if start >= stop :
106+ raise ValueError ("Start must be less than stop." )
107+
108+ return range (start , stop + step , step )
0 commit comments