Advertisement
HasteBin0

A (Dynamic) Switch Statement in Python

Dec 8th, 2018
268
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 13.26 KB | None | 0 0
  1. #!/usr/bin/python3
  2. from typing import Any, Callable, Dict, Iterable, Tuple, Union
  3.  
  4.  
  5. def switch_flat(
  6.         *parameter_trios: [
  7.             object,  # switch
  8.             Iterable[  # list of cases
  9.                 Union[  # each item in the list
  10.                     object,  # case
  11.                     Union[  # pair or trio
  12.                         Tuple[  # trio w/ fxn and args
  13.                             Callable,  # fxn
  14.                             Iterable  # args
  15.                         ],
  16.                         object  # pair w/ constant
  17.                     ]
  18.                 ]
  19.             ]
  20.         ],
  21.  
  22.         default: Tuple[Callable, Iterable[object]] = None,
  23.         error: Tuple[Callable, Iterable[object]] = None
  24. ) -> Any:
  25.     """
  26.    Implement a switch structure from C/C++ into Python.
  27.    The parameter_trios is a list or tuple that starts with a first value called switch or switch value.
  28.    -
  29.    
  30.    After the switch follows one or more trios and/or pairs of values.
  31.    For each trio encountered:
  32.        The trio has a
  33.            case [case] (possible value for switch),
  34.            a function/callable [fxn] (called on switch==case), and
  35.            arguments [args] (fxn([*[*]]args)).
  36.        In other words, when a case matches (via ==) the switch, this function returns the returned value of the
  37.            correlating function call with the arguments distributed.
  38.        If the arguments are a dictionary, the function is called double-distributed (fxn(**args)).
  39.        Else, if the arguments are an iterable, the function is called single-distributed (fxn(*args)).
  40.        Otherwise, the function is called with the single-argument passed (fxn(args)).
  41.    The exception is when the argument after case, fxn, isn't callable:
  42.        In the scenario, no args are passed at all and none (not even "None") should be given.
  43.        The fxn itself, being some constant, is returned.
  44.        The scenario is an encounter with a pair.
  45.    -
  46.    
  47.    After all cases are checked, regardless of whether any matches were found, the function returns the call of
  48.        default[0](default[1]).
  49.    This a called the default case or base case.
  50.    -
  51.    
  52.    The error argument is new and Python-specific addition to this edition of the switch structure.
  53.    It is a default to the base case.
  54.    -
  55.  
  56.    If parameter_trios is empty, the base case is invoked (if given) and then the error is called (error[0](*error[1])),
  57.        if given.
  58.    -
  59.    
  60.    If neither forms ([switch, *[case, fxn[, args]]] or []) apply, a ValueError is raised.
  61.    -
  62.    
  63.    :param parameter_trios: [switch, *[case, fxn[, args]]] or [] in Iterable or Generator form
  64.    :param default: None or [fxn, args]
  65.    :param error: None or [fxn, args]
  66.    :return: None or object
  67.    """
  68.     parameter_trios_iterable: iter = iter(parameter_trios)
  69.     try:
  70.         try:
  71.             switch_value = next(parameter_trios_iterable)  # get the switch value
  72.         except StopIteration:
  73.             raise AssertionError  # parameters are blank
  74.         # iterate over cases
  75.         for case in parameter_trios_iterable:  # trio/pairs ahead
  76.             fxn = next(parameter_trios_iterable)  # get fxn
  77.             is_callable = isinstance(fxn, Callable)  # function (trio) or constant (pair)
  78.             # compare
  79.             if switch_value == case:  # match
  80.                 if is_callable:  # function
  81.                     args = next(parameter_trios_iterable)  # arguments
  82.                     if isinstance(args, Dict):  # dictionary
  83.                         return fxn(**args)
  84.                     elif isinstance(args, Iterable):  # other iterable
  85.                         return fxn(*args)
  86.                     else:  # single constant
  87.                         return fxn(args)
  88.                 else:  # constant
  89.                     return fxn
  90.             else:  # mismatch
  91.                 if is_callable:  # function (trio)
  92.                     next(parameter_trios_iterable)  # gloss over arguments
  93.     except AssertionError:  # blank parameters
  94.         pass
  95.     except StopIteration:  # invalid length
  96.         raise ValueError
  97.     # base case
  98.     if default is not None:
  99.         return default[0](*default[1])
  100.     elif error is not None:
  101.         return error[0](*error[1])
  102.     else:
  103.         return None
  104.  
  105.  
  106. def switch(
  107.         *parameter_trios: [
  108.             object,  # switch
  109.             Iterable[  # list of cases
  110.                 Iterable[  # each case in the list
  111.                     Union[  # each item in the case
  112.                         object,  # case
  113.                         Union[  # pair or trio
  114.                             Tuple[  # trio w/ fxn and args
  115.                                 Callable,  # fxn
  116.                                 Iterable  # args
  117.                             ],
  118.                             object  # pair w/ constant
  119.                         ]
  120.                     ]
  121.                 ]
  122.             ]
  123.         ],
  124.  
  125.         default: Tuple[Callable, Iterable[object]] = None,
  126.         error: Tuple[Callable, Iterable[object]] = None
  127. ) -> Any:
  128.     """ Invoke simple_flat(). """
  129.     parameter_trios_iterable: iter = iter(parameter_trios)
  130.     try:
  131.         try:
  132.             switch_value = next(parameter_trios_iterable)  # get the switch value
  133.         except StopIteration:
  134.             raise AssertionError  # parameters are blank
  135.         # iterate over cases
  136.         for case_trio in parameter_trios_iterable:  # trio/pairs ahead
  137.             try:
  138.                 case = case_trio[0]  # get case
  139.                 fxn = case_trio[1]  # get fxn
  140.             except IndexError:
  141.                 raise AssertionError
  142.             # function (trio) or constant (pair)
  143.             # compare
  144.             if switch_value == case:  # match
  145.                 if isinstance(fxn, Callable):  # function
  146.                     try:
  147.                         args = case_trio[2]  # get arguments
  148.                     except IndexError:
  149.                         raise AssertionError
  150.                     if isinstance(args, Dict):  # dictionary
  151.                         return fxn(**args)
  152.                     elif isinstance(args, Iterable):  # other iterable
  153.                         return fxn(*args)
  154.                     else:  # single constant
  155.                         return fxn(args)
  156.                 else:  # constant
  157.                     return fxn
  158.     except AssertionError:  # blank parameters
  159.         pass
  160.     except StopIteration:  # invalid length
  161.         raise ValueError
  162.     # base case
  163.     if default is not None:
  164.         return default[0](*default[1])
  165.     elif error is not None:
  166.         return error[0](*error[1])
  167.     else:
  168.         return None
  169.  
  170.  
  171. class SwitchTests:  # namespace
  172.     @staticmethod
  173.     def test1_switch_flat_print(*s: [int], default: bool = True, error: bool = True):
  174.         assert len(s) < 2
  175.  
  176.         p: Callable[[Tuple], Iterable[str]] = lambda *st: st
  177.  
  178.         def printer(*args: [str, int]) -> int:
  179.             print(*args)
  180.             return args[1]
  181.  
  182.         a: [] = \
  183.             list(s) + [
  184.                 0, printer, ['Zero', 0],
  185.                 1, printer, ['One', 1],
  186.                 2, printer, ['Two', 2],
  187.                 3, printer, ['Three', 3]
  188.             ] if len(s) == 1 else []
  189.         return switch_flat(
  190.             *a,
  191.             default = (p, ('Default',)) if default else None,
  192.             error = (p, ('Error',)) if error else None)
  193.  
  194.     @staticmethod
  195.     def test1_switch_print(*s: [int], default: bool = True, error: bool = True):
  196.         assert len(s) < 2
  197.  
  198.         p: Callable[[Tuple], Iterable[str]] = lambda *st: st
  199.  
  200.         def printer(*args: [str, int]) -> int:
  201.             print(*args)
  202.             return args[1]
  203.  
  204.         a: [] = \
  205.             list(s) + [
  206.                 (0, printer, ['Zero', 0]),
  207.                 (1, printer, ['One', 1]),
  208.                 (2, printer, ['Two', 2]),
  209.                 (3, printer, ['Three', 3])
  210.             ] if len(s) == 1 else []
  211.         return switch(
  212.             *a,
  213.             default = (p, ('Default',)) if default else None,
  214.             error = (p, ('Error',)) if error else None)
  215.  
  216.     @classmethod
  217.     def test1_switch(cls):
  218.         try:
  219.             print('Testing flat')
  220.             print('Test 1.1.1 returned ', cls.test1_switch_flat_print(), '.', sep = '')
  221.             print('Test 1.1.2 returned ', cls.test1_switch_flat_print(1), '.', sep = '')
  222.             print('Test 1.1.3 returned ', cls.test1_switch_flat_print(2), '.', sep = '')
  223.             print('Test 1.1.4 returned ', cls.test1_switch_flat_print(3), '.', sep = '')
  224.             print('Test 1.1.5 returned ', cls.test1_switch_flat_print(4), '.', sep = '')
  225.             print('Test 1.1.e returned ', cls.test1_switch_flat_print(4), '.', sep = '')
  226.             print('Test 1.1.d returned ', cls.test1_switch_flat_print(4, default = False), '.', sep = '')
  227.             print('Test 1.1.b returned ', cls.test1_switch_flat_print(4, error = False), '.', sep = '')
  228.             print('Test 1.1.r returned ', cls.test1_switch_flat_print(4, default = False, error = False), '.', sep = '')
  229.         except Exception as e:
  230.             print('Error 1', e)
  231.         try:
  232.             print('Testing grouped')
  233.             print('Test 1.2.1 returned ', cls.test1_switch_print(), '.', sep = '')
  234.             print('Test 1.2.2 returned ', cls.test1_switch_print(1), '.', sep = '')
  235.             print('Test 1.2.3 returned ', cls.test1_switch_print(2), '.', sep = '')
  236.             print('Test 1.2.4 returned ', cls.test1_switch_print(3), '.', sep = '')
  237.             print('Test 1.2.5 returned ', cls.test1_switch_print(4), '.', sep = '')
  238.             print('Test 1.2.e returned ', cls.test1_switch_print(4), '.', sep = '')
  239.             print('Test 1.2.d returned ', cls.test1_switch_print(4, default = False), '.', sep = '')
  240.             print('Test 1.2.b returned ', cls.test1_switch_print(4, error = False), '.', sep = '')
  241.             print('Test 1.2.r returned ', cls.test1_switch_print(4, default = False, error = False), '.', sep = '')
  242.         except Exception as e:
  243.             print('Error 2', e)
  244.         print('Done Test 1\n')
  245.  
  246.     @staticmethod
  247.     def test2_switch_flat_print(*s, default: bool = True, error: bool = True):
  248.         assert len(s) < 2
  249.  
  250.         p: Callable[[Tuple], Iterable[str]] = lambda *st: st
  251.  
  252.         a: [] = \
  253.             list(s) + [
  254.                 0, ['Zero', 0],
  255.                 1, ['One', 1],
  256.                 2, ['Two', 2],
  257.                 3, ['Three', 3]
  258.             ] if len(s) == 1 else []
  259.         return switch_flat(
  260.             *a,
  261.             default = (p, ('Default',)) if default else None,
  262.             error = (p, ('Error',)) if error else None)
  263.  
  264.     @staticmethod
  265.     def test2_switch_print(*s, default: bool = True, error: bool = True):
  266.         assert len(s) < 2
  267.  
  268.         p: Callable[[Tuple], Iterable[str]] = lambda *st: st
  269.  
  270.         a: [] = \
  271.             list(s) + [
  272.                 (0, ['Zero', 0]),
  273.                 (1, ['One', 1]),
  274.                 (2, ['Two', 2]),
  275.                 (3, ['Three', 3])
  276.             ] if len(s) == 1 else []
  277.         return switch(*a, default = (p, ('Default',)) if default else None, error = (p, ('Error',)) if error else None)
  278.  
  279.     @classmethod
  280.     def test2_switch(cls):
  281.         try:
  282.             print('Testing flat')
  283.             print('Test 2.1.1 returned ', cls.test2_switch_flat_print(), '.', sep = '')
  284.             print('Test 2.1.2 returned ', cls.test2_switch_flat_print(1), '.', sep = '')
  285.             print('Test 2.1.3 returned ', cls.test2_switch_flat_print(2), '.', sep = '')
  286.             print('Test 2.1.4 returned ', cls.test2_switch_flat_print(3), '.', sep = '')
  287.             print('Test 2.1.5 returned ', cls.test2_switch_flat_print(4), '.', sep = '')
  288.             print('Test 2.1.e returned ', cls.test2_switch_flat_print(4), '.', sep = '')
  289.             print('Test 2.1.d returned ', cls.test2_switch_flat_print(4, default = False), '.', sep = '')
  290.             print('Test 2.1.b returned ', cls.test2_switch_flat_print(4, error = False), '.', sep = '')
  291.             print('Test 2.1.r returned ', cls.test2_switch_flat_print(4, default = False, error = False), '.', sep = '')
  292.         except Exception as e:
  293.             print('Error 3', e)
  294.         try:
  295.             print('Testing grouped')
  296.             print('Test 2.2.1 returned ', cls.test2_switch_print(), '.', sep = '')
  297.             print('Test 2.2.2 returned ', cls.test2_switch_print(1), '.', sep = '')
  298.             print('Test 2.2.3 returned ', cls.test2_switch_print(2), '.', sep = '')
  299.             print('Test 2.2.4 returned ', cls.test2_switch_print(3), '.', sep = '')
  300.             print('Test 2.2.5 returned ', cls.test2_switch_print(4), '.', sep = '')
  301.             print('Test 2.2.e returned ', cls.test2_switch_print(4), '.', sep = '')
  302.             print('Test 2.2.d returned ', cls.test2_switch_print(4, default = False), '.', sep = '')
  303.             print('Test 2.2.b returned ', cls.test2_switch_print(4, error = False), '.', sep = '')
  304.             print('Test 2.2.r returned ', cls.test2_switch_print(4, default = False, error = False), '.', sep = '')
  305.         except Exception as e:
  306.             print('Error 4', e)
  307.         print('Done Test 2\n')
  308.  
  309.     @classmethod
  310.     def test_all_switches(cls):
  311.         cls.test1_switch()
  312.         cls.test2_switch()
  313.  
  314.  
  315. if __name__ == '__main__':
  316.     SwitchTests.test_all_switches()
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement