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