how to make a calculator: tests and solutions


how to make a calculator 1: tests

the code from calculator/tests/test_calculator.py from how to make a calculator

 1import random
 2import src.calculator
 3import unittest
 4
 5
 6def a_random_number():
 7    return random.triangular(-1000.0, 1000.0)
 8
 9
10class TestCalculator(unittest.TestCase):
11
12    random_first_number = a_random_number()
13    random_second_number = a_random_number()
14
15    def test_addition(self):
16        self.assertEqual(
17            src.calculator.add(
18                self.random_first_number,
19                self.random_second_number
20            ),
21            self.random_first_number+self.random_second_number
22        )
23
24    def test_subtraction(self):
25        self.assertEqual(
26            src.calculator.subtract(
27                self.random_first_number,
28                self.random_second_number
29            ),
30            self.random_first_number-self.random_second_number
31        )
32
33    def test_multiplication(self):
34        self.assertEqual(
35            src.calculator.multiply(
36                self.random_first_number,
37                self.random_second_number
38            ),
39            self.random_first_number*self.random_second_number
40        )
41
42    def test_division(self):
43        self.assertEqual(
44            src.calculator.divide(
45                self.random_first_number,
46                self.random_second_number
47            ),
48            self.random_first_number/self.random_second_number
49        )
50
51
52# Exceptions seen
53# AssertionError
54# NameError
55# AttributeError
56# TypeError

how to make a calculator 1: solutions

the solutions in calculator/src/calculator.py from how to make a calculator

 1def add(first_input, second_input):
 2    return first_input + second_input
 3
 4
 5def subtract(first_input, second_input):
 6    return first_input - second_input
 7
 8
 9def multiply(first_input, second_input):
10    return first_input * second_input
11
12
13def divide(first_input, second_input):
14    return first_input / second_input

how to make a calculator 2: tests

the code from calculator/tests/test_calculator.py from how to make a calculator 2

 1import random
 2import src.calculator
 3import unittest
 4
 5
 6def a_random_number():
 7    return random.triangular(-1000.0, 1000.0)
 8
 9
10class TestCalculator(unittest.TestCase):
11
12    random_first_number = a_random_number()
13    random_second_number = a_random_number()
14
15    def test_addition(self):
16        self.assertEqual(
17            src.calculator.add(
18                self.random_first_number,
19                self.random_second_number
20            ),
21            self.random_first_number+self.random_second_number
22        )
23
24    def test_subtraction(self):
25        self.assertEqual(
26            src.calculator.subtract(
27                self.random_first_number,
28                self.random_second_number
29            ),
30            self.random_first_number-self.random_second_number
31        )
32
33    def test_multiplication(self):
34        self.assertEqual(
35            src.calculator.multiply(
36                self.random_first_number,
37                self.random_second_number
38            ),
39            self.random_first_number*self.random_second_number
40        )
41
42    def test_division(self):
43        self.assertEqual(
44            src.calculator.divide(
45                self.random_first_number,
46                self.random_second_number
47            ),
48            self.random_first_number/self.random_second_number
49        )
50        with self.assertRaises(ZeroDivisionError):
51            src.calculator.divide(self.random_first_number, 0)
52
53
54# Exceptions seen
55# AssertionError
56# NameError
57# AttributeError
58# TypeError

how to make a calculator 3: tests

the code from calculator/tests/test_calculator.py from how to make a calculator 3

 1import random
 2import src.calculator
 3import unittest
 4
 5
 6def a_random_number():
 7    return random.triangular(-1000.0, 1000.0)
 8
 9
10class TestCalculator(unittest.TestCase):
11
12    random_first_number = a_random_number()
13    random_second_number = a_random_number()
14
15    def test_addition(self):
16        self.assertEqual(
17            src.calculator.add(
18                self.random_first_number,
19                self.random_second_number
20            ),
21            self.random_first_number+self.random_second_number
22        )
23
24    def test_subtraction(self):
25        self.assertEqual(
26            src.calculator.subtract(
27                self.random_first_number,
28                self.random_second_number
29            ),
30            self.random_first_number-self.random_second_number
31        )
32
33    def test_multiplication(self):
34        self.assertEqual(
35            src.calculator.multiply(
36                self.random_first_number,
37                self.random_second_number
38            ),
39            self.random_first_number*self.random_second_number
40        )
41
42    def test_division(self):
43        try:
44            self.assertEqual(
45                src.calculator.divide(
46                    self.random_first_number,
47                    self.random_second_number
48                ),
49                self.random_first_number/self.random_second_number
50            )
51        except ZeroDivisionError:
52            self.assertEqual(
53                src.calculator.divide(self.random_first_number, 0),
54                'brmph?! I cannot divide by 0. Try again...'
55            )
56
57
58# Exceptions seen
59# AssertionError
60# NameError
61# AttributeError
62# TypeError

how to make a calculator 3: solutions

the solutions in calculator/src/calculator.py from how to make a calculator 3

 1def subtract(first_input, second_input):
 2    return first_input - second_input
 3
 4
 5def multiply(first_input, second_input):
 6    return first_input * second_input
 7
 8
 9def divide(first_input, second_input):
10    try:
11        return first_input / second_input
12    except ZeroDivisionError:
13        return 'brmph?! I cannot divide by 0. Try again...'
14
15
16def add(first_input, second_input):
17    return first_input + second_input

how to make a calculator 4: tests

the code from calculator/tests/test_calculator.py from how to make a calculator 4

 1import random
 2import src.calculator
 3import unittest
 4
 5
 6def a_random_number():
 7    return random.triangular(-1000.0, 1000.0)
 8
 9
10class TestCalculator(unittest.TestCase):
11
12    random_first_number = a_random_number()
13    random_second_number = a_random_number()
14
15    def test_addition(self):
16        self.assertEqual(
17            src.calculator.add(
18                self.random_first_number,
19                self.random_second_number
20            ),
21            self.random_first_number+self.random_second_number
22        )
23
24    def test_subtraction(self):
25        self.assertEqual(
26            src.calculator.subtract(
27                self.random_first_number,
28                self.random_second_number
29            ),
30            self.random_first_number-self.random_second_number
31        )
32
33    def test_multiplication(self):
34        self.assertEqual(
35            src.calculator.multiply(
36                self.random_first_number,
37                self.random_second_number
38            ),
39            self.random_first_number*self.random_second_number
40        )
41
42    def test_division(self):
43        try:
44            self.assertEqual(
45                src.calculator.divide(
46                    self.random_first_number,
47                    self.random_second_number
48                ),
49                self.random_first_number/self.random_second_number
50            )
51        except ZeroDivisionError:
52            self.assertEqual(
53                src.calculator.divide(self.random_first_number, 0),
54                'brmph?! I cannot divide by 0. Try again...'
55            )
56
57    def test_calculator_sends_message_when_input_is_not_a_number(self):
58        error_message = 'brmph?! Numbers only. Try again...'
59        
60        self.assertEqual(
61            src.calculator.add(None, None),
62            error_message
63        )
64        self.assertEqual(
65            src.calculator.divide(None, None),
66            error_message
67        )
68        self.assertEqual(
69            src.calculator.multiply(None, None),
70            error_message
71        )
72        self.assertEqual(
73            src.calculator.subtract(None, None),
74            error_message
75        )
76        self.assertEqual(
77            src.calculator.add('1', '1'),
78            error_message
79        )
80        self.assertEqual(
81            src.calculator.divide('1', '1'),
82            error_message
83        )
84        self.assertEqual(
85            src.calculator.multiply('1', '1'),
86            error_message
87        )
88        self.assertEqual(
89            src.calculator.subtract('1', '1'),
90            error_message
91        )
92
93
94# Exceptions seen
95# AssertionError
96# NameError
97# AttributeError
98# TypeError

how to make a calculator 4: solutions

the solutions in calculator/src/calculator.py from how to make a calculator 4

 1def numbers_only(function):
 2    def decorator(first_input, second_input):
 3        error_message = 'brmph?! Numbers only. Try again...'
 4        if first_input is None or second_input is None:
 5            return error_message
 6        else:
 7            try:
 8                return function(first_input, second_input)
 9            except TypeError:
10                return error_message
11    return decorator
12
13
14@numbers_only
15def subtract(first_input, second_input):
16    return first_input - second_input
17
18
19@numbers_only
20def multiply(first_input, second_input):
21    return first_input * second_input
22
23
24@numbers_only
25def divide(first_input, second_input):
26    try:
27        return first_input / second_input
28    except ZeroDivisionError:
29        return 'brmph?! I cannot divide by 0. Try again...'
30
31
32@numbers_only
33def add(first_input, second_input):
34    if (
35        isinstance(first_input, str)
36        or
37        isinstance(second_input, str)
38    ):
39        return 'brmph?! Numbers only. Try again...'
40    else:
41        return first_input + second_input

how to make a calculator 5: tests

the code from calculator/tests/test_calculator.py from how to make a calculator 5

  1import random
  2import src.calculator
  3import unittest
  4
  5
  6def a_random_number():
  7    return random.triangular(-1000.0, 1000.0)
  8
  9
 10class TestCalculator(unittest.TestCase):
 11
 12    random_first_number = a_random_number()
 13    random_second_number = a_random_number()
 14
 15    def test_addition(self):
 16        self.assertEqual(
 17            src.calculator.add(
 18                self.random_first_number,
 19                self.random_second_number
 20            ),
 21            self.random_first_number+self.random_second_number
 22        )
 23
 24    def test_subtraction(self):
 25        self.assertEqual(
 26            src.calculator.subtract(
 27                self.random_first_number,
 28                self.random_second_number
 29            ),
 30            self.random_first_number-self.random_second_number
 31        )
 32
 33    def test_multiplication(self):
 34        self.assertEqual(
 35            src.calculator.multiply(
 36                self.random_first_number,
 37                self.random_second_number
 38            ),
 39            self.random_first_number*self.random_second_number
 40        )
 41
 42    def test_division(self):
 43        try:
 44            self.assertEqual(
 45                src.calculator.divide(
 46                    self.random_first_number,
 47                    self.random_second_number
 48                ),
 49                self.random_first_number/self.random_second_number
 50            )
 51        except ZeroDivisionError:
 52            self.assertEqual(
 53                src.calculator.divide(self.random_first_number, 0),
 54                'brmph?! I cannot divide by 0. Try again...'
 55            )
 56
 57    def test_calculator_sends_message_when_input_is_not_a_number(self):
 58        error_message = 'brmph?! Numbers only. Try again...'
 59
 60        self.assertEqual(
 61            src.calculator.add(None, None),
 62            error_message
 63        )
 64        self.assertEqual(
 65            src.calculator.divide(None, None),
 66            error_message
 67        )
 68        self.assertEqual(
 69            src.calculator.multiply(None, None),
 70            error_message
 71        )
 72        self.assertEqual(
 73            src.calculator.subtract(None, None),
 74            error_message
 75        )
 76        self.assertEqual(
 77            src.calculator.add('1', '1'),
 78            error_message
 79        )
 80        self.assertEqual(
 81            src.calculator.divide('1', '1'),
 82            error_message
 83        )
 84        self.assertEqual(
 85            src.calculator.multiply('1', '1'),
 86            error_message
 87        )
 88        self.assertEqual(
 89            src.calculator.subtract('1', '1'),
 90            error_message
 91        )
 92
 93        a_list = [0, 1, 2, 3]
 94
 95        self.assertEqual(
 96            src.calculator.add(a_list, 0),
 97            error_message
 98        )
 99        self.assertEqual(
100            src.calculator.divide(a_list, 1),
101            error_message
102        )
103        self.assertEqual(
104            src.calculator.multiply(a_list, 2),
105            error_message
106        )
107        self.assertEqual(
108            src.calculator.subtract(a_list, 3),
109            error_message
110        )
111
112    def test_calculator_w_list_items(self):
113        two_numbers = [
114            self.random_first_number,
115            self.random_second_number
116        ]
117
118        self.assertEqual(
119            src.calculator.add(
120                two_numbers[0],
121                two_numbers[1]
122            ),
123            self.random_first_number+self.random_second_number
124        )
125        self.assertEqual(
126            src.calculator.divide(
127                two_numbers[-2],
128                two_numbers[-1]
129            ),
130            self.random_first_number/self.random_second_number
131        )
132        self.assertEqual(
133            src.calculator.multiply(
134                two_numbers[1],
135                two_numbers[-1]
136            ),
137            self.random_second_number*self.random_second_number
138        )
139        self.assertEqual(
140            src.calculator.subtract(
141                two_numbers[-2],
142                two_numbers[0]
143            ),
144            self.random_first_number-self.random_first_number
145        )
146        self.assertEqual(
147            src.calculator.add(*two_numbers),
148            self.random_first_number+self.random_second_number
149        )
150        self.assertEqual(
151            src.calculator.divide(*two_numbers),
152            self.random_first_number/self.random_second_number
153        )
154        self.assertEqual(
155            src.calculator.multiply(*two_numbers),
156            self.random_first_number*self.random_second_number
157        )
158        self.assertEqual(
159            src.calculator.subtract(*two_numbers),
160            self.random_first_number-self.random_second_number
161        )
162
163    def test_calculator_raises_type_error_when_given_more_than_two_inputs(self):
164        not_two_numbers = [0, 1, 2]
165
166        with self.assertRaises(TypeError):
167            src.calculator.add(*not_two_numbers)
168        with self.assertRaises(TypeError):
169            src.calculator.divide(*not_two_numbers)
170        with self.assertRaises(TypeError):
171            src.calculator.multiply(*not_two_numbers)
172        with self.assertRaises(TypeError):
173            src.calculator.subtract(*not_two_numbers)
174
175
176# Exceptions seen
177# AssertionError
178# NameError
179# AttributeError
180# TypeError

how to make a calculator 5: solutions

the solutions in calculator/src/calculator.py from how to make a calculator 5

 1def numbers_only(function):
 2    def wrapper(first_input, second_input):
 3        good_types = (int, float)
 4        error_message = 'brmph?! Numbers only. Try again...'
 5
 6        if not (isinstance(first_input, good_types) and isinstance(second_input, good_types)):
 7            return error_message
 8        else:
 9            try:
10                return function(first_input, second_input)
11            except TypeError:
12                return error_message
13    return wrapper
14
15
16@numbers_only
17def subtract(first_input, second_input):
18    return first_input - second_input
19
20
21@numbers_only
22def multiply(first_input, second_input):
23    return first_input * second_input
24
25
26@numbers_only
27def divide(first_input, second_input):
28    try:
29        return first_input / second_input
30    except ZeroDivisionError:
31        return 'brmph?! I cannot divide by 0. Try again...'
32
33
34@numbers_only
35def add(first_input, second_input):
36    return first_input + second_input

how to make a calculator 6: tests

the code from calculator/tests/test_calculator.py from how to make a calculator 6

  1import random
  2import src.calculator
  3import unittest
  4
  5
  6def a_random_number():
  7    return random.triangular(-1000.0, 1000.0)
  8
  9
 10class TestCalculator(unittest.TestCase):
 11
 12    def setUp(self):
 13        self.random_first_number = a_random_number()
 14        self.random_second_number = a_random_number()
 15
 16    def test_addition(self):
 17        self.assertEqual(
 18            src.calculator.add(
 19                self.random_first_number,
 20                self.random_second_number
 21            ),
 22            self.random_first_number+self.random_second_number
 23        )
 24
 25    def test_subtraction(self):
 26        self.assertEqual(
 27            src.calculator.subtract(
 28                self.random_first_number,
 29                self.random_second_number
 30            ),
 31            self.random_first_number-self.random_second_number
 32        )
 33
 34    def test_multiplication(self):
 35        self.assertEqual(
 36            src.calculator.multiply(
 37                self.random_first_number,
 38                self.random_second_number
 39            ),
 40            self.random_first_number*self.random_second_number
 41        )
 42
 43    def test_division(self):
 44        try:
 45            self.assertEqual(
 46                src.calculator.divide(
 47                    self.random_first_number,
 48                    self.random_second_number
 49                ),
 50                self.random_first_number/self.random_second_number
 51            )
 52        except ZeroDivisionError:
 53            self.assertEqual(
 54                src.calculator.divide(self.random_first_number, 0),
 55                'brmph?! I cannot divide by 0. Try again...'
 56            )
 57
 58    def test_calculator_w_list_items(self):
 59        two_numbers = [
 60            self.random_first_number,
 61            self.random_second_number
 62        ]
 63
 64        self.assertEqual(
 65            src.calculator.add(
 66                two_numbers[0],
 67                two_numbers[1]
 68            ),
 69            self.random_first_number+self.random_second_number
 70        )
 71        self.assertEqual(
 72            src.calculator.divide(
 73                two_numbers[-2],
 74                two_numbers[-1]
 75            ),
 76            self.random_first_number/self.random_second_number
 77        )
 78        self.assertEqual(
 79            src.calculator.multiply(
 80                two_numbers[1],
 81                two_numbers[-1]
 82            ),
 83            self.random_second_number*self.random_second_number
 84        )
 85        self.assertEqual(
 86            src.calculator.subtract(
 87                two_numbers[-2],
 88                two_numbers[0]
 89            ),
 90            self.random_first_number-self.random_first_number
 91        )
 92        self.assertEqual(
 93            src.calculator.add(*two_numbers),
 94            self.random_first_number+self.random_second_number
 95        )
 96        self.assertEqual(
 97            src.calculator.divide(*two_numbers),
 98            self.random_first_number/self.random_second_number
 99        )
100        self.assertEqual(
101            src.calculator.multiply(*two_numbers),
102            self.random_first_number*self.random_second_number
103        )
104        self.assertEqual(
105            src.calculator.subtract(*two_numbers),
106            self.random_first_number-self.random_second_number
107        )
108
109    def test_calculator_raises_type_error_when_given_more_than_two_inputs(self):
110        not_two_numbers = [0, 1, 2]
111
112        with self.assertRaises(TypeError):
113            src.calculator.add(*not_two_numbers)
114        with self.assertRaises(TypeError):
115            src.calculator.divide(*not_two_numbers)
116        with self.assertRaises(TypeError):
117            src.calculator.multiply(*not_two_numbers)
118        with self.assertRaises(TypeError):
119            src.calculator.subtract(*not_two_numbers)
120
121    def test_calculator_sends_message_when_input_is_not_a_number(self):
122        error_message = 'brmph?! Numbers only. Try again...'
123
124        [
125            self.assertEqual(
126                src.calculator.add(data_type, a_random_number()),
127                error_message
128            ) for data_type in (
129                None,
130                True, False,
131                str(), 'text',
132                tuple(), (0, 1, 2, 'n'),
133                list(), [0, 1, 2, 'n'],
134                set(), {0, 1, 2, 'n'},
135                dict(), {'key': 'value'},
136            )
137        ]
138
139        for data_type in (
140            None,
141            True, False,
142            str(), 'text',
143            tuple(), (0, 1, 2, 'n'),
144            list(), [0, 1, 2, 'n'],
145            set(), {0, 1, 2, 'n'},
146            dict(), {'key': 'value'},
147        ):
148            with self.subTest(data_type=data_type):
149                self.assertEqual(
150                    src.calculator.add(
151                        data_type, a_random_number()
152                    ),
153                    error_message
154                )
155                self.assertEqual(
156                    src.calculator.divide(
157                        data_type, a_random_number()
158                    ),
159                    error_message
160                )
161                self.assertEqual(
162                    src.calculator.multiply(
163                        data_type, a_random_number()
164                    ),
165                    error_message
166                )
167                self.assertEqual(
168                    src.calculator.subtract(
169                        data_type, a_random_number()
170                    ),
171                    error_message
172                )
173
174
175# Exceptions seen
176# AssertionError
177# NameError
178# AttributeError
179# TypeError

how to make a calculator 6: solutions

the solutions in calculator/src/calculator.py from how to make a calculator 5

 1def numbers_only(function):
 2    def decorator(first_input, second_input):
 3        bad_data_types = (str, list, bool)
 4        error_message = 'brmph?! Numbers only. Try again...'
 5
 6        for value in (first_input, second_input):
 7            if (
 8                value is None
 9                or
10                isinstance(value, bad_data_types)
11            ):
12                return error_message
13
14        try:
15            return function(first_input, second_input)
16        except TypeError:
17            return error_message
18    return decorator
19
20
21@numbers_only
22def subtract(first_input, second_input):
23    return first_input - second_input
24
25
26@numbers_only
27def multiply(first_input, second_input):
28    return first_input * second_input
29
30
31@numbers_only
32def divide(first_input, second_input):
33    try:
34        return first_input / second_input
35    except ZeroDivisionError:
36        return 'brmph?! I cannot divide by 0. Try again...'
37
38
39@numbers_only
40def add(first_input, second_input):
41    return first_input + second_input

how to make a calculator 7: tests

the code from calculator/tests/test_calculator.py from how to make a calculator 7

  1import random
  2import src.calculator
  3import unittest
  4
  5
  6def a_random_number():
  7    return random.triangular(-1000.0, 1000.0)
  8
  9
 10class TestCalculator(unittest.TestCase):
 11
 12    def setUp(self):
 13        self.random_first_number = a_random_number()
 14        self.random_second_number = a_random_number()
 15
 16        x = self.random_first_number
 17        y = self.random_second_number
 18
 19        try:
 20            division_result = x / y
 21        except ZeroDivisionError:
 22            division_result = 'brmph?! I cannot divide by 0. Try again...'
 23
 24        self.arithmetic_tests = {
 25            'addition': {
 26                'function': src.calculator.add,
 27                'expectation': x+y,
 28            },
 29            'subtraction': {
 30                'function': src.calculator.subtract,
 31                'expectation': x-y,
 32            },
 33            'division': {
 34                'function': src.calculator.divide,
 35                'expectation': division_result,
 36            },
 37            'multiplication': {
 38                'function': src.calculator.multiply,
 39                'expectation': x*y,
 40            }
 41        }
 42
 43    def test_calculator_w_list_items(self):
 44        # two_numbers = [
 45        #     self.random_first_number,
 46        #     self.random_second_number
 47        # ]
 48        a_dictionary = {
 49            'x': self.random_first_number,
 50            'y': self.random_second_number
 51        }
 52        two_numbers = list(a_dictionary.values())
 53
 54        for operation in self.arithmetic_tests:
 55            with self.subTest(operation=operation):
 56                self.assertEqual(
 57                    self.arithmetic_tests[operation]['function'](
 58                        *two_numbers
 59                    ),
 60                    self.arithmetic_tests[operation]['expectation']
 61                )
 62
 63        self.assertEqual(
 64            src.calculator.add(
 65                two_numbers[0],
 66                two_numbers[1]
 67            ),
 68            self.random_first_number+self.random_second_number
 69        )
 70        self.assertEqual(
 71            src.calculator.divide(
 72                two_numbers[-2],
 73                two_numbers[-1]
 74            ),
 75            self.random_first_number/self.random_second_number
 76        )
 77        self.assertEqual(
 78            src.calculator.multiply(
 79                two_numbers[1],
 80                two_numbers[-1]
 81            ),
 82            self.random_second_number*self.random_second_number
 83        )
 84        self.assertEqual(
 85            src.calculator.subtract(
 86                two_numbers[-2],
 87                two_numbers[0]
 88            ),
 89            self.random_first_number-self.random_first_number
 90        )
 91
 92    def test_calculator_w_dictionary_items(self):
 93        two_numbers = {
 94            'first_input': self.random_first_number,
 95            'second_input': self.random_second_number,
 96        }
 97
 98        for operation in self.arithmetic_tests:
 99            with self.subTest(operation=operation):
100                self.assertEqual(
101                    self.arithmetic_tests[operation]['function'](
102                        **two_numbers
103                    ),
104                    self.arithmetic_tests[operation]['expectation']
105                )
106
107        self.assertEqual(
108            src.calculator.add(
109                two_numbers['first_input'],
110                two_numbers['second_input']
111            ),
112            self.random_first_number+self.random_second_number
113        )
114        self.assertEqual(
115            src.calculator.divide(
116                two_numbers['first_input'],
117                two_numbers['second_input']
118            ),
119            self.random_first_number/self.random_second_number
120        )
121        self.assertEqual(
122            src.calculator.multiply(
123                two_numbers['second_input'],
124                two_numbers['second_input']
125            ),
126            self.random_second_number*self.random_second_number
127        )
128        self.assertEqual(
129            src.calculator.subtract(
130                two_numbers['first_input'],
131                two_numbers['first_input']
132            ),
133            self.random_first_number-self.random_first_number
134        )
135
136    def test_calculator_raises_type_error_when_given_more_than_two_inputs(self):
137        for operation in self.arithmetic_tests:
138            with (
139                self.subTest(operation=operation),
140                self.assertRaises(TypeError),
141            ):
142                self.arithmetic_tests[operation]['function'](
143                    [0, 1, 2]
144                )
145
146    def test_calculator_sends_message_when_input_is_not_a_number(self):
147        for bad_input in (
148            None,
149            True, False,
150            str(), 'text',
151            tuple(), (0, 1, 2, 'n'),
152            list(), [0, 1, 2, 'n'],
153            set(), {0, 1, 2, 'n'},
154            dict(), {'key': 'value'},
155        ):
156            for operation in self.arithmetic_tests:
157                with self.subTest(
158                    operation=operation,
159                    bad_input=bad_input,
160                ):
161                    self.assertEqual(
162                        self.arithmetic_tests[operation]['function'](
163                            bad_input, a_random_number()
164                        ),
165                        'brmph?! Numbers only. Try again...'
166                    )
167
168    def test_calculator_functions(self):
169        for operation in self.arithmetic_tests:
170            with self.subTest(operation=operation):
171                self.assertEqual(
172                    self.arithmetic_tests[operation]['function'](
173                        self.random_first_number,
174                        self.random_second_number
175                    ),
176                    self.arithmetic_tests[operation]['expectation']
177                )
178
179
180# Exceptions seen
181# AssertionError
182# NameError
183# AttributeError
184# TypeError
185# KeyError

how to make a calculator 8: tests

the code from calculator/tests/test_calculator.py from how to make a calculator 8

  1import random
  2import src.calculator
  3import unittest
  4
  5
  6def a_random_number():
  7    return random.triangular(-1000.0, 1000.0)
  8
  9
 10class TestCalculator(unittest.TestCase):
 11
 12    @staticmethod
 13    def get_division_result(x, y):
 14        try:
 15            return x / y
 16        except ZeroDivisionError:
 17            return 'brmph?! I cannot divide by 0. Try again...'
 18
 19    def setUp(self):
 20        self.random_first_number = a_random_number()
 21        self.random_second_number = a_random_number()
 22
 23        x = self.random_first_number
 24        y = self.random_second_number
 25
 26        self.calculator_tests = {
 27            'add': x+y,
 28            'subtract': x-y,
 29            'multiply': x*y,
 30            'divide': self.get_division_result(x, y)
 31        }
 32
 33    def test_calculator_w_list_items(self):
 34        # two_numbers = [
 35        #     self.random_first_number,
 36        #     self.random_second_number
 37        # ]
 38        a_dictionary = {
 39            'x': self.random_first_number,
 40            'y': self.random_second_number
 41        }
 42        two_numbers = list(a_dictionary.values())
 43
 44        for operation in self.calculator_tests:
 45            with self.subTest(operation=operation):
 46                self.assertEqual(
 47                    src.calculator.__getattribute__(operation)(
 48                        *two_numbers
 49                    ),
 50                    self.calculator_tests[operation]
 51                )
 52
 53        self.assertEqual(
 54            src.calculator.add(
 55                two_numbers[0],
 56                two_numbers[1]
 57            ),
 58            self.random_first_number+self.random_second_number
 59        )
 60        self.assertEqual(
 61            src.calculator.divide(
 62                two_numbers[-2],
 63                two_numbers[-1]
 64            ),
 65            self.random_first_number/self.random_second_number
 66        )
 67        self.assertEqual(
 68            src.calculator.multiply(
 69                two_numbers[1],
 70                two_numbers[-1]
 71            ),
 72            self.random_second_number*self.random_second_number
 73        )
 74        self.assertEqual(
 75            src.calculator.subtract(
 76                two_numbers[-2],
 77                two_numbers[0]
 78            ),
 79            self.random_first_number-self.random_first_number
 80        )
 81
 82    def test_calculator_w_dictionary_items(self):
 83        two_numbers = {
 84            'first_input': self.random_first_number,
 85            'second_input': self.random_second_number,
 86        }
 87
 88        for operation in self.calculator_tests:
 89            with self.subTest(operation=operation):
 90                self.assertEqual(
 91                    src.calculator.__getattribute__(operation)(
 92                        **two_numbers
 93                    ),
 94                    self.calculator_tests[operation]
 95                )
 96
 97        self.assertEqual(
 98            src.calculator.add(
 99                two_numbers['first_input'],
100                two_numbers['second_input']
101            ),
102            self.random_first_number+self.random_second_number
103        )
104        self.assertEqual(
105            src.calculator.divide(
106                two_numbers['first_input'],
107                two_numbers['second_input']
108            ),
109            self.random_first_number/self.random_second_number
110        )
111        self.assertEqual(
112            src.calculator.multiply(
113                two_numbers['second_input'],
114                two_numbers['second_input']
115            ),
116            self.random_second_number*self.random_second_number
117        )
118        self.assertEqual(
119            src.calculator.subtract(
120                two_numbers['first_input'],
121                two_numbers['first_input']
122            ),
123            self.random_first_number-self.random_first_number
124        )
125
126    def test_calculator_raises_type_error_when_given_more_than_two_inputs(self):
127        for operation in self.calculator_tests:
128            with (
129                self.subTest(operation=operation),
130                self.assertRaises(TypeError),
131            ):
132                src.calculator.__getattribute__(operation)(
133                    [0, 1, 2]
134                )
135
136    def test_calculator_sends_message_when_input_is_not_a_number(self):
137        for bad_input in (
138            None,
139            True, False,
140            str(), 'text',
141            tuple(), (0, 1, 2, 'n'),
142            list(), [0, 1, 2, 'n'],
143            set(), {0, 1, 2, 'n'},
144            dict(), {'key': 'value'},
145        ):
146            for operation in self.calculator_tests:
147                with self.subTest(
148                    operation=operation,
149                    bad_input=bad_input,
150                ):
151                    self.assertEqual(
152                        src.calculator.__getattribute__(operation)(
153                            bad_input, a_random_number()
154                        ),
155                        'brmph?! Numbers only. Try again...'
156                    )
157
158    def test_calculator_functions(self):
159        for operation in self.calculator_tests:
160            with self.subTest(operation=operation):
161                self.assertEqual(
162                    src.calculator.__getattribute__(operation)(
163                        self.random_first_number,
164                        self.random_second_number
165                    ),
166                    self.calculator_tests[operation]
167                )
168
169
170# Exceptions seen
171# AssertionError
172# NameError
173# AttributeError
174# TypeError
175# KeyError

how to make a calculator 9: tests

the code from calculator/tests/test_calculator_website.py from how to make a calculator 9

 1import src.website
 2import tests.test_calculator
 3import unittest
 4
 5
 6class TestCalculatorWebsite(unittest.TestCase):
 7
 8    def setUp(self):
 9        self.client = src.website.app.test_client()
10        self.x = tests.test_calculator.a_random_number()
11
12    def test_home_page(self):
13        response = self.client.get('/')
14        self.assertEqual(response.status_code, 200)
15        self.assertIn(
16            b'<h1>Calculator</h1>',
17            response.data
18        )
19
20    def test_calculations(self):
21        y = tests.test_calculator.a_random_number()
22
23        operations = {
24            'add': '+',
25            'subtract': '-',
26            'divide': '/',
27            'multiply': '*',
28        }
29
30        for operation in operations:
31            with self.subTest(operation=operation):
32                response = self.client.post(
33                    '/calculate',
34                    data={
35                        'first_input': self.x,
36                        'second_input': y,
37                        'operation': operation,
38                    }
39                )
40                self.assertEqual(response.status_code, 200)
41
42                function = src.calculator.__getattribute__(
43                    operation
44                )
45                result = function(self.x, y)
46                self.assertEqual(
47                    response.data.decode(),
48                    (
49                        f'<h2>{self.x} {operations[operation]} {y} '
50                        f'= {result}</h2>'
51                    )
52                )
53
54    def test_website_handling_zero_division_error(self):
55        response = self.client.post(
56            '/calculate',
57            data={
58                'first_input': self.x,
59                'second_input': 0,
60                'operation': 'divide',
61            }
62        )
63        self.assertEqual(
64            response.data.decode(),
65            (
66                f'<h2>{self.x} / 0.0 = '
67                'brmph?! I cannot divide by 0. Try again...</h2>'
68            )
69        )
70
71
72# Exceptions seen
73# NameError
74# ModuleNotFoundError
75# AttributeError
76# AssertionError
77# jinja2.exceptions.TemplateNotFound
78# SyntaxError

the code from calculator/tests/test_calculator.py from how to make a calculator 9

  1import random
  2import src.calculator
  3import unittest
  4
  5
  6def a_random_number():
  7    return random.triangular(-1000.0, 1000.0)
  8
  9
 10class TestCalculator(unittest.TestCase):
 11
 12    @staticmethod
 13    def get_division_result(x, y):
 14        try:
 15            return x / y
 16        except ZeroDivisionError:
 17            return 'brmph?! I cannot divide by 0. Try again...'
 18
 19    def setUp(self):
 20        self.random_first_number = a_random_number()
 21        self.random_second_number = a_random_number()
 22
 23        x = self.random_first_number
 24        y = self.random_second_number
 25
 26        self.calculator_tests = {
 27            'add': x+y,
 28            'subtract': x-y,
 29            'multiply': x*y,
 30            'divide': self.get_division_result(x, y)
 31        }
 32
 33    def test_calculator_w_list_items(self):
 34        # two_numbers = [
 35        #     self.random_first_number,
 36        #     self.random_second_number
 37        # ]
 38        a_dictionary = {
 39            'x': self.random_first_number,
 40            'y': self.random_second_number
 41        }
 42        two_numbers = list(a_dictionary.values())
 43
 44        for operation in self.calculator_tests:
 45            with self.subTest(operation=operation):
 46                self.assertEqual(
 47                    src.calculator.__getattribute__(operation)(
 48                        *two_numbers
 49                    ),
 50                    self.calculator_tests[operation]
 51                )
 52
 53        self.assertEqual(
 54            src.calculator.add(
 55                two_numbers[0],
 56                two_numbers[1]
 57            ),
 58            self.random_first_number+self.random_second_number
 59        )
 60        self.assertEqual(
 61            src.calculator.divide(
 62                two_numbers[-2],
 63                two_numbers[-1]
 64            ),
 65            self.random_first_number/self.random_second_number
 66        )
 67        self.assertEqual(
 68            src.calculator.multiply(
 69                two_numbers[1],
 70                two_numbers[-1]
 71            ),
 72            self.random_second_number*self.random_second_number
 73        )
 74        self.assertEqual(
 75            src.calculator.subtract(
 76                two_numbers[-2],
 77                two_numbers[0]
 78            ),
 79            self.random_first_number-self.random_first_number
 80        )
 81
 82    def test_calculator_w_dictionary_items(self):
 83        two_numbers = {
 84            'first_input': self.random_first_number,
 85            'second_input': self.random_second_number,
 86        }
 87
 88        for operation in self.calculator_tests:
 89            with self.subTest(operation=operation):
 90                self.assertEqual(
 91                    src.calculator.__getattribute__(operation)(
 92                        **two_numbers
 93                    ),
 94                    self.calculator_tests[operation]
 95                )
 96
 97        self.assertEqual(
 98            src.calculator.add(
 99                two_numbers['first_input'],
100                two_numbers['second_input']
101            ),
102            self.random_first_number+self.random_second_number
103        )
104        self.assertEqual(
105            src.calculator.divide(
106                two_numbers['first_input'],
107                two_numbers['second_input']
108            ),
109            self.random_first_number/self.random_second_number
110        )
111        self.assertEqual(
112            src.calculator.multiply(
113                two_numbers['second_input'],
114                two_numbers['second_input']
115            ),
116            self.random_second_number*self.random_second_number
117        )
118        self.assertEqual(
119            src.calculator.subtract(
120                two_numbers['first_input'],
121                two_numbers['first_input']
122            ),
123            self.random_first_number-self.random_first_number
124        )
125
126    def test_calculator_raises_type_error_when_given_more_than_two_inputs(self):
127        for operation in self.calculator_tests:
128            with (
129                self.subTest(operation=operation),
130                self.assertRaises(TypeError),
131            ):
132                src.calculator.__getattribute__(operation)(
133                    [0, 1, 2]
134                )
135
136    def test_calculator_sends_message_when_inputs_are_not_numbers(self):
137        for bad_input in (
138            None,
139            True, False,
140            str(), 'text',
141            tuple(), (0, 1, 2, 'n'),
142            list(), [0, 1, 2, 'n'],
143            set(), {0, 1, 2, 'n'},
144            dict(), {'key': 'value'},
145        ):
146            for operation in self.calculator_tests:
147                with self.subTest(
148                    operation=operation,
149                    bad_input=bad_input,
150                ):
151                    self.assertEqual(
152                        src.calculator.__getattribute__(operation)(
153                            bad_input, a_random_number()
154                        ),
155                        'brmph?! Numbers only. Try again...'
156                    )
157                    self.assertEqual(
158                        src.calculator.__getattribute__(operation)(
159                            a_random_number(), bad_input
160                        ),
161                        'brmph?! Numbers only. Try again...'
162                    )
163
164    def test_calculator_functions(self):
165        for operation in self.calculator_tests:
166            with self.subTest(operation=operation):
167                self.assertEqual(
168                    src.calculator.__getattribute__(operation)(
169                        self.random_first_number,
170                        self.random_second_number
171                    ),
172                    self.calculator_tests[operation]
173                )
174
175
176# Exceptions seen
177# AssertionError
178# NameError
179# AttributeError
180# TypeError
181# KeyError

how to make a calculator 9: solutions

the code from calculator/src/calculator.py from how to make a calculator 9

 1def check_input(function):
 2    def wrapper(first_input, second_input):
 3        if isinstance(
 4            first_input,
 5            (dict, set, list, tuple, str, bool)
 6        ) or first_input is None:
 7            return 'brmph?! Numbers only. Try again...'
 8        return function(first_input, second_input)
 9    return wrapper
10
11
12@check_input
13def add(first_input, second_input):
14    return first_input + second_input
15
16
17@check_input
18def divide(first_input, second_input):
19    try:
20        return first_input / second_input
21    except ZeroDivisionError:
22        return 'brmph?! I cannot divide by 0. Try again...'
23
24
25@check_input
26def multiply(first_input, second_input):
27    return first_input * second_input
28
29
30@check_input
31def subtract(first_input, second_input):
32    return first_input - second_input

the code from calculator/src/website.py from how to make a calculator 9

 1import pathlib
 2import sys
 3sys.path.insert(
 4    0, str(pathlib.Path(__file__).resolve().parent)
 5)
 6
 7import calculator
 8import flask
 9
10
11app = flask.Flask(__name__)
12
13
14@app.route('/')
15def home():
16    return flask.render_template('index.html')
17
18
19@app.route('/calculate', methods=['POST'])
20def calculate():
21    first_input = flask.request.form.get('first_input')
22    first_input = float(first_input)
23    second_input = flask.request.form.get('second_input')
24    second_input = float(second_input)
25    operation = flask.request.form.get('operation')
26
27    operations = {
28        'add': '+',
29        'subtract': '-',
30        'divide': '/',
31        'multiply': '*',
32    }
33
34    result = calculator.__getattribute__(operation)(
35        first_input, second_input
36    )
37    return (
38        f'<h2>{first_input} {operations[operation]} {second_input} '
39        f'= {result}</h2>'
40    )

how to make a calculator 10: part 1: tests

the code from calculator/tests/test_streamlit_calculator.py from how to make a calculator 10: part 1

  1import random
  2import streamlit.testing.v1
  3import tests.test_calculator
  4import unittest
  5
  6
  7class TestStreamlitCalculator(unittest.TestCase):
  8
  9    def setUp(self):
 10        self.tester = streamlit.testing.v1.AppTest.from_file(
 11            'src/streamlit_calculator.py'
 12        )
 13        self.tester.run()
 14
 15    def test_streamlit_calculator_title(self):
 16        self.assertEqual(self.tester.title[0].value, 'Calculator')
 17
 18    def test_streamlit_calculator_display(self):
 19        self.assertEqual(
 20            self.tester.main.children[1].type,
 21            'flex_container'
 22        )
 23
 24    def test_streamlit_calculator_columns(self):
 25        self.assertEqual(len(self.tester.columns), 4)
 26        self.assertEqual(
 27            self.tester.columns[0].button('<-').label,
 28            '<-'
 29        )
 30        self.assertEqual(
 31            self.tester.columns[0].button('7').label,
 32            '7'
 33        )
 34        self.assertEqual(
 35            self.tester.columns[0].button('4').label,
 36            '4'
 37        )
 38        self.assertEqual(
 39            self.tester.columns[0].button('1').label,
 40            '1'
 41        )
 42        self.assertEqual(
 43            self.tester.columns[0].button('+/-').label,
 44            '+/-'
 45        )
 46        self.assertEqual(
 47            self.tester.columns[1].button('C').label,
 48            'C'
 49        )
 50        self.assertEqual(
 51            self.tester.columns[1].button('8').label,
 52            '8'
 53        )
 54        self.assertEqual(
 55            self.tester.columns[1].button('5').label,
 56            '5'
 57        )
 58        self.assertEqual(
 59            self.tester.columns[1].button('2').label,
 60            '2'
 61        )
 62        self.assertEqual(
 63            self.tester.columns[1].button('0').label,
 64            '0'
 65        )
 66
 67        self.assertEqual(
 68            self.tester.columns[2].button('AC').label,
 69            'AC'
 70        )
 71        self.assertEqual(
 72            self.tester.columns[2].button('9').label,
 73            '9'
 74        )
 75        self.assertEqual(
 76            self.tester.columns[2].button('6').label,
 77            '6'
 78        )
 79        self.assertEqual(
 80            self.tester.columns[2].button('3').label,
 81            '3'
 82        )
 83        self.assertEqual(
 84            self.tester.columns[2].button('.').label,
 85            '.'
 86        )
 87
 88        self.assertEqual(
 89            self.tester.columns[3].button('/').label,
 90            '/'
 91        )
 92        self.assertEqual(
 93            self.tester.columns[3].button('X').label,
 94            'X'
 95        )
 96        self.assertEqual(
 97            self.tester.columns[3].button('-').label,
 98            r'\-'
 99        )
100        self.assertEqual(
101            self.tester.columns[3].button('+').label,
102            r'\+'
103        )
104        self.assertEqual(
105            self.tester.columns[3].button('=').label,
106            '='
107        )
108
109    def test_streamlit_session_state(self):
110        expectation = '0'
111        for _ in range(0, 10):
112            number = random.choice('0123456789')
113            (
114                self.tester.button(number)
115                .click().run()
116            )
117            if expectation == '0':
118                expectation = number
119            else:
120                expectation += number
121        self.assertEqual(
122            self.tester.session_state['number'],
123            expectation
124        )
125
126    def test_streamlit_calculator_w_decimals(self):
127        for button in ('0.23.5.6.7.8.9'):
128            (
129                self.tester.button(button)
130                .click().run()
131            )
132        self.assertEqual(
133            self.tester.session_state['number'],
134            '.2356789'
135        )
136
137    def test_streamlit_calculator_w_plus_minus(self):
138        number = '963.0258741'
139        for button in number:
140            (
141                self.tester.button(button)
142                .click().run()
143            )
144        self.tester.button('+/-').click().run()
145        self.assertEqual(
146            self.tester.session_state['number'],
147            f'-{number}'
148        )
149
150        self.tester.session_state['number'] = '0'
151        number = '-963.0258741'
152        for button in number:
153            (
154                self.tester.button(button)
155                .click().run()
156            )
157
158        self.tester.button('+/-').click().run()
159        self.assertEqual(
160            self.tester.session_state['number'],
161            number[1:]
162        )
163
164    def test_streamlit_calculator_reset_state(self):
165        numbers = '123456789'
166        number = random.choice(numbers)
167        self.tester.button(number).click().run()
168        self.assertEqual(
169            self.tester.session_state['number'],
170            number
171        )
172        self.tester.button('C').click().run()
173        self.assertEqual(
174            self.tester.session_state['number'],
175            '0'
176        )
177
178        number = random.choice(numbers)
179        self.tester.button(number).click().run()
180        self.assertEqual(
181            self.tester.session_state['number'],
182            number
183        )
184        self.tester.button('AC').click().run()
185        self.assertEqual(
186            self.tester.session_state['number'],
187            '0'
188        )
189
190    @unittest.skip
191    def test_streamlit_calculator_operations(self):
192        # first_number = '1'
193        first_number = tests.test_calculator.a_random_number()
194        first_number = str(first_number)
195        second_number = '2'
196
197        for character in first_number:
198            if character == '-':
199                self.tester.button('+/-').click().run()
200            else:
201                self.tester.button(character).click().run()
202        self.tester.button('+').click().run()
203        self.assertEqual(
204            self.tester.session_state['first_number'],
205            first_number
206        )
207
208        self.tester.button(second_number).click().run()
209        self.tester.button('=').click().run()
210        self.assertEqual(
211            self.tester.session_state['second_number'],
212            second_number
213        )
214
215        self.assertEqual(
216            self.tester.session_state['number'],
217            str(float(first_number) + float(second_number))
218        )
219
220
221# Exceptions seen
222# NameError
223# AttributeError
224# AssertionError
225# SyntaxError
226# KeyError
227# streamlit.errors.StreamlitDuplicateElementKey
228# TypeError

how to make a calculator 10: part 1: solutions

the code from calculator/src/streamlit_calculator.py from how to make a calculator 10: part 1

  1import streamlit
  2
  3
  4def show_result(display):
  5    streamlit.session_state['second_number'] = \
  6        streamlit.session_state['number']
  7    streamlit.session_state['number'] = str(
  8        float(streamlit.session_state['first_number'])
  9      + float(streamlit.session_state['second_number'])
 10    )
 11    display.write(streamlit.session_state['number'])
 12
 13
 14def make_variable(display):
 15    streamlit.session_state['first_number'] = \
 16        streamlit.session_state['number']
 17    reset_state(display)
 18
 19
 20def reset_state(display):
 21    streamlit.session_state['number'] = '0'
 22    display.write(streamlit.session_state['number'])
 23
 24
 25def update_state(value):
 26    if value == '.':
 27        if (
 28            streamlit.session_state['number']
 29            .count('.') != 0
 30        ):
 31            return
 32    if streamlit.session_state['number'] == '0':
 33        streamlit.session_state['number'] = value
 34        return
 35    if value == '+/-':
 36        if not (
 37            streamlit.session_state['number']
 38            .startswith('-')
 39        ):
 40            negative = (
 41                '-'  +
 42                streamlit.session_state['number']
 43            )
 44            streamlit.session_state['number'] = \
 45                negative
 46            return
 47        else:
 48            streamlit.session_state['number'] = \
 49                streamlit.session_state['number'][1:]
 50            return
 51    streamlit.session_state['number'] += value
 52
 53
 54def show(display, value):
 55    update_state(value)
 56    display.write(streamlit.session_state['number'])
 57
 58
 59def add_buttons():
 60    display = streamlit.container(border=True)
 61    column_1, column_2, column_3, operations = streamlit.columns(4)
 62
 63    column_1.button('<-', key='<-', width='stretch')
 64    column_1.button(
 65        '7', key='7', width='stretch',
 66        on_click=show, args=[display, '7'],
 67    )
 68    column_1.button(
 69        '4', key='4', width='stretch',
 70        on_click=show, args=[display, '4'],
 71    )
 72    column_1.button(
 73        '1', key='1', width='stretch',
 74        on_click=show, args=[display, '1'],
 75    )
 76    column_1.button(
 77        '+/-', key='+/-', width='stretch',
 78        on_click=show, args=[display, '+/-'],
 79    )
 80
 81    column_2.button(
 82        'C', key='C', width='stretch', type='primary',
 83        on_click=reset_state, args=[display],
 84    )
 85    column_2.button(
 86        '8', key='8', width='stretch',
 87        on_click=show, args=[display, '8'],
 88    )
 89    column_2.button(
 90        '5', key='5', width='stretch',
 91        on_click=show, args=[display, '5'],
 92    )
 93    column_2.button(
 94        '2', key='2', width='stretch',
 95        on_click=show, args=[display, '2'],
 96    )
 97    column_2.button(
 98        '0', key='0', width='stretch',
 99        on_click=show, args=[display, '0'],
100    )
101
102    column_3.button(
103        'AC', key='AC', width='stretch', type='primary',
104        on_click=reset_state, args=[display],
105    )
106    column_3.button(
107        '9', key='9', width='stretch',
108        on_click=show, args=[display, '9'],
109    )
110    column_3.button(
111        '6', key='6', width='stretch',
112        on_click=show, args=[display, '6'],
113    )
114    column_3.button(
115        '3', key='3', width='stretch',
116        on_click=show, args=[display, '3'],
117    )
118    column_3.button(
119        '.', key='.', width='stretch',
120        on_click=show, args=[display, '.'],
121    )
122
123    operations.button('/', key='/', width='stretch', type='primary')
124    operations.button('X', key='X', width='stretch', type='primary')
125    operations.button(r'\-', key='-', width='stretch', type='primary')
126    operations.button(
127        r'\+', key='+', width='stretch', type='primary',
128        on_click=make_variable, args=[display],
129    )
130    operations.button(
131        '=', key='=', width='stretch', type='primary',
132        on_click=show_result, args=[display],
133    )
134
135
136def main():
137    streamlit.title('Calculator')
138    streamlit.session_state.setdefault('number', '0')
139    add_buttons()
140
141
142if __name__ == '__main__':
143    main()

how to make a calculator 10: part 2: tests and solutions

the code from calculator/tests/test_streamlit_calculator.py from how to make a calculator 10: part 2

  1import random
  2import streamlit.testing.v1
  3import tests.test_calculator
  4import unittest
  5
  6
  7class TestStreamlitCalculator(unittest.TestCase):
  8
  9    def setUp(self):
 10        self.tester = streamlit.testing.v1.AppTest.from_file(
 11            'src/streamlit_calculator.py'
 12        )
 13        self.tester.run()
 14
 15    def test_streamlit_calculator_title(self):
 16        self.assertEqual(self.tester.title[0].value, 'Calculator')
 17
 18    def test_streamlit_calculator_display(self):
 19        self.assertEqual(
 20            self.tester.main.children[1].type,
 21            'flex_container'
 22        )
 23
 24    def test_streamlit_calculator_columns(self):
 25        self.assertEqual(len(self.tester.columns), 4)
 26        self.assertEqual(
 27            self.tester.columns[0].button('<-').label,
 28            '<-'
 29        )
 30        self.assertEqual(
 31            self.tester.columns[0].button('7').label,
 32            '7'
 33        )
 34        self.assertEqual(
 35            self.tester.columns[0].button('4').label,
 36            '4'
 37        )
 38        self.assertEqual(
 39            self.tester.columns[0].button('1').label,
 40            '1'
 41        )
 42        self.assertEqual(
 43            self.tester.columns[0].button('+/-').label,
 44            '+/-'
 45        )
 46        self.assertEqual(
 47            self.tester.columns[1].button('C').label,
 48            'C'
 49        )
 50        self.assertEqual(
 51            self.tester.columns[1].button('8').label,
 52            '8'
 53        )
 54        self.assertEqual(
 55            self.tester.columns[1].button('5').label,
 56            '5'
 57        )
 58        self.assertEqual(
 59            self.tester.columns[1].button('2').label,
 60            '2'
 61        )
 62        self.assertEqual(
 63            self.tester.columns[1].button('0').label,
 64            '0'
 65        )
 66
 67        self.assertEqual(
 68            self.tester.columns[2].button('AC').label,
 69            'AC'
 70        )
 71        self.assertEqual(
 72            self.tester.columns[2].button('9').label,
 73            '9'
 74        )
 75        self.assertEqual(
 76            self.tester.columns[2].button('6').label,
 77            '6'
 78        )
 79        self.assertEqual(
 80            self.tester.columns[2].button('3').label,
 81            '3'
 82        )
 83        self.assertEqual(
 84            self.tester.columns[2].button('.').label,
 85            '.'
 86        )
 87
 88        self.assertEqual(
 89            self.tester.columns[3].button('/').label,
 90            '/'
 91        )
 92        self.assertEqual(
 93            self.tester.columns[3].button('X').label,
 94            'X'
 95        )
 96        self.assertEqual(
 97            self.tester.columns[3].button('-').label,
 98            r'\-'
 99        )
100        self.assertEqual(
101            self.tester.columns[3].button('+').label,
102            r'\+'
103        )
104        self.assertEqual(
105            self.tester.columns[3].button('=').label,
106            '='
107        )
108
109    def test_streamlit_session_state(self):
110        expectation = '0'
111        for _ in range(0, 10):
112            number = random.choice('0123456789')
113            (
114                self.tester.button(number)
115                .click().run()
116            )
117            if expectation == '0':
118                expectation = number
119            else:
120                expectation += number
121        self.assertEqual(
122            self.tester.session_state['number'],
123            expectation
124        )
125
126    def test_streamlit_calculator_w_decimals(self):
127        for button in ('0.23.5.6.7.8.9'):
128            (
129                self.tester.button(button)
130                .click().run()
131            )
132        self.assertEqual(
133            self.tester.session_state['number'],
134            '.2356789'
135        )
136
137    def test_streamlit_calculator_w_plus_minus(self):
138        number = '963.0258741'
139        for button in number:
140            (
141                self.tester.button(button)
142                .click().run()
143            )
144        self.tester.button('+/-').click().run()
145        self.assertEqual(
146            self.tester.session_state['number'],
147            f'-{number}'
148        )
149
150        self.tester.session_state['number'] = '0'
151        number = '-963.0258741'
152        for button in number:
153            (
154                self.tester.button(button)
155                .click().run()
156            )
157
158        self.tester.button('+/-').click().run()
159        self.assertEqual(
160            self.tester.session_state['number'],
161            number[1:]
162        )
163
164    def test_streamlit_calculator_reset_state(self):
165        numbers = '123456789'
166        number = random.choice(numbers)
167        self.tester.button(number).click().run()
168        self.assertEqual(
169            self.tester.session_state['number'],
170            number
171        )
172        self.tester.button('C').click().run()
173        self.assertEqual(
174            self.tester.session_state['number'],
175            '0'
176        )
177
178        number = random.choice(numbers)
179        self.tester.button(number).click().run()
180        self.assertEqual(
181            self.tester.session_state['number'],
182            number
183        )
184        self.tester.button('AC').click().run()
185        self.assertEqual(
186            self.tester.session_state['number'],
187            '0'
188        )
189
190    @unittest.skip
191    def test_streamlit_calculator_operations(self):
192        # first_number = '1'
193        first_number = tests.test_calculator.a_random_number()
194        first_number = str(first_number)
195        second_number = '2'
196
197        for character in first_number:
198            if character == '-':
199                self.tester.button('+/-').click().run()
200            else:
201                self.tester.button(character).click().run()
202        self.tester.button('+').click().run()
203        self.assertEqual(
204            self.tester.session_state['first_number'],
205            first_number
206        )
207
208        self.tester.button(second_number).click().run()
209        self.tester.button('=').click().run()
210        self.assertEqual(
211            self.tester.session_state['second_number'],
212            second_number
213        )
214
215        self.assertEqual(
216            self.tester.session_state['number'],
217            str(float(first_number) + float(second_number))
218        )
219
220
221# Exceptions seen
222# NameError
223# AttributeError
224# AssertionError
225# SyntaxError
226# KeyError
227# streamlit.errors.StreamlitDuplicateElementKey
228# TypeError

how to make a calculator 10: part 2: solutions

the code from calculator/src/streamlit_calculator.py from how to make a calculator 10: part 2

  1import streamlit
  2
  3
  4def show_number(display):
  5    display.write(streamlit.session_state['number'])
  6
  7
  8def plus_minus():
  9    if streamlit.session_state['number'].startswith('-'):
 10        number = streamlit.session_state['number'][1:]
 11    else:
 12        number = '-' + streamlit.session_state['number']
 13    streamlit.session_state['number'] = number
 14
 15
 16def backspace():
 17    number = streamlit.session_state['number'][:-1]
 18    streamlit.session_state['number'] = number
 19
 20
 21def add_decimal():
 22    if streamlit.session_state['number'].count('.') == 0:
 23        streamlit.session_state['number'] += '.'
 24
 25
 26def add_number_to_state(number):
 27    if streamlit.session_state['number'] == '0':
 28        streamlit.session_state['number'] = number
 29    else:
 30        streamlit.session_state['number'] += number
 31
 32
 33def on_click(function, display, *value):
 34    function(*value)
 35    show_number(display)
 36
 37
 38def add_buttons_to_column_1(column_1, display):
 39    column_1.button(
 40        label='<-', key='<-', width='stretch', on_click=on_click,
 41        args=[backspace, display],
 42    )
 43    column_1.button(
 44        label='7', key='7', width='stretch', on_click=on_click,
 45        args=[add_number_to_state, display, '7'],
 46    )
 47    column_1.button(
 48        label='4', key='4', width='stretch', on_click=on_click,
 49        args=[add_number_to_state, display, '4'],
 50    )
 51    column_1.button(
 52        label='1', key='1', width='stretch', on_click=on_click,
 53        args=[add_number_to_state, display, '1'],
 54    )
 55    column_1.button(
 56        label='+/-', key='+/-', width='stretch', on_click=on_click,
 57        args=[plus_minus, display],
 58    )
 59
 60
 61def add_buttons_to_column_2(column_2, display):
 62    column_2.button(
 63        label='C', key='C', width='stretch', type='primary',
 64    )
 65    column_2.button(
 66        label='8', key='8', width='stretch', on_click=on_click,
 67        args=[add_number_to_state, display, '8'],
 68    )
 69    column_2.button(
 70        label='5', key='5', width='stretch', on_click=on_click,
 71        args=[add_number_to_state, display, '5'],
 72    )
 73    column_2.button(
 74        label='2', key='2', width='stretch', on_click=on_click,
 75        args=[add_number_to_state, display, '2'],
 76    )
 77    column_2.button(
 78        label='0', key='0', width='stretch', on_click=on_click,
 79        args=[add_number_to_state, display, '0'],
 80    )
 81
 82
 83def add_buttons_to_column_3(column_3, display):
 84    column_3.button(
 85        label='AC', key='AC', width='stretch', type='primary',
 86    )
 87    column_3.button(
 88        label='9', key='9', width='stretch', on_click=on_click,
 89        args=[add_number_to_state, display, '9'],
 90    )
 91    column_3.button(
 92        label='6', key='6', width='stretch', on_click=on_click,
 93        args=[add_number_to_state, display, '6'],
 94    )
 95    column_3.button(
 96        label='3', key='3', width='stretch', on_click=on_click,
 97        args=[add_number_to_state, display, '3'],
 98    )
 99    column_3.button(
100        label='.', key='.', width='stretch', on_click=on_click,
101        args=[add_decimal, display]
102    )
103
104
105def add_buttons_to_column_4(column_4):
106    column_4.button(
107        label='/', key='/', width='stretch', type='primary',
108    )
109    column_4.button(
110        label='X', key='X', width='stretch', type='primary',
111    )
112    column_4.button(
113        label=r'\-', key=r'\-', width='stretch', type='primary',
114    )
115    column_4.button(
116        label=r'\+', key=r'\+', width='stretch', type='primary',
117    )
118    column_4.button(
119        label='=', key='=', width='stretch', type='primary',
120    )
121
122
123def main():
124    streamlit.title('Calculator')
125    streamlit.session_state.setdefault('number', '0')
126    display = streamlit.container(border=True)
127
128    column_1, column_2, column_3, operations = streamlit.columns(4)
129    add_buttons_to_column_1(column_1, display)
130    add_buttons_to_column_2(column_2, display)
131    add_buttons_to_column_3(column_3, display)
132    add_buttons_to_column_4(operations)
133
134
135if __name__ == '__main__':
136    main()

how to make a calculator 10: part 3: tests

the code from calculator/tests/test_streamlit_calculator.py from how to make a calculator 10: part 3

  1import random
  2import streamlit.testing.v1
  3import tests.test_calculator
  4import unittest
  5
  6
  7class TestStreamlitCalculator(unittest.TestCase):
  8
  9    def press_button(self, key):
 10        self.tester.button(key).click().run()
 11
 12    def setUp(self):
 13        self.tester = streamlit.testing.v1.AppTest.from_file(
 14            'src/streamlit_calculator.py'
 15        )
 16        self.tester.run()
 17
 18    def test_streamlit_calculator_title(self):
 19        self.assertEqual(self.tester.title[0].value, 'Calculator')
 20
 21    def test_streamlit_calculator_display(self):
 22        display = (
 23            self.tester.main.children[1].proto
 24                .flex_container
 25        )
 26        self.assertEqual(display.gap_config.gap_size, 1)
 27        self.assertEqual(display.direction, 1)
 28        self.assertEqual(display.justify, 1)
 29        self.assertEqual(display.align, 1)
 30        self.assertTrue(display.border)
 31
 32    def test_streamlit_calculator_columns_and_buttons(self):
 33        self.assertEqual(len(self.tester.columns), 4)
 34
 35        for column, keys in (
 36            (0, ('<-', '7', '4', '1', '+/-')),
 37            (1, ('C', '8', '5', '2', '0')),
 38            (2, ('AC', '9', '6', '3', '.')),
 39            (3, ('/', 'X', r'\-', r'\+', '=')),
 40        ):
 41            for key in keys:
 42                with self.subTest(key=key):
 43                    self.assertEqual(
 44                        (
 45                            self.tester.columns[column]
 46                                .button(key)
 47                                .label
 48                        ),
 49                        key
 50                    )
 51
 52    def test_streamlit_calculator_operations_buttons(self):
 53        for key in ('/', 'X', r'\-', r'\+', '=', 'C', 'AC'):
 54            with self.subTest(key=key):
 55                self.assertEqual(
 56                    self.tester.button(key).proto.type,
 57                    'primary'
 58                )
 59
 60    def test_streamlit_session_state(self):
 61        numbers = '0123456789'
 62        self.assertEqual(self.tester.session_state['number'], '0')
 63
 64        expectation = random.choice(numbers)
 65        while expectation == '0':
 66            expectation = random.choice(numbers)
 67        else:
 68            self.press_button(expectation)
 69
 70        self.assertEqual(
 71            self.tester.session_state['number'], expectation
 72        )
 73
 74        for _ in range(0, len(numbers)):
 75            a_random_number = random.choice(numbers)
 76            self.press_button(a_random_number)
 77            expectation += a_random_number
 78
 79        self.assertEqual(
 80            self.tester.session_state['number'],
 81            expectation
 82        )
 83
 84    def test_streamlit_calculator_w_decimals(self):
 85        for key in ('0.23.5.6.7.89'):
 86            self.press_button(key)
 87
 88        self.assertEqual(
 89            self.tester.session_state['number'],
 90            '0.2356789'
 91        )
 92
 93    def test_streamlit_calculator_backspace(self):
 94        a_random_number = tests.test_calculator.a_random_number()
 95        while a_random_number < 0:
 96            a_random_number = tests.test_calculator.a_random_number()
 97        a_random_number = str(a_random_number)
 98
 99        for key in a_random_number:
100            self.press_button(key)
101        self.press_button('<-')
102
103        self.assertEqual(
104            self.tester.session_state['number'],
105            a_random_number[:-1]
106        )
107
108        self.press_button('<-')
109        self.assertEqual(
110            self.tester.session_state['number'],
111            a_random_number[:-2]
112        )
113
114    def test_streamlit_calculator_w_plus_minus(self):
115        a_number = '963.0258741'
116        for key in a_number:
117            self.press_button(key)
118        self.assertEqual(
119            self.tester.session_state['number'], a_number
120        )
121
122        self.press_button('+/-')
123        self.assertEqual(
124            self.tester.session_state['number'], f'-{a_number}'
125        )
126
127        self.press_button('+/-')
128        self.assertEqual(
129            self.tester.session_state['number'], a_number
130        )
131
132
133# Exceptions seen
134# NameError
135# AttributeError
136# AssertionError
137# SyntaxError
138# KeyError
139# streamlit.errors.StreamlitDuplicateElementKey

how to make a calculator 10: part 3: solutions

the code from calculator/src/streamlit_calculator.py from how to make a calculator 10: part 3

  1import streamlit
  2
  3
  4def show_number(display):
  5    display.write(streamlit.session_state['number'])
  6
  7
  8def plus_minus():
  9    if streamlit.session_state['number'].startswith('-'):
 10        number = streamlit.session_state['number'][1:]
 11    else:
 12        number = '-' + streamlit.session_state['number']
 13    streamlit.session_state['number'] = number
 14
 15
 16def backspace():
 17    number = streamlit.session_state['number'][:-1]
 18    streamlit.session_state['number'] = number
 19
 20
 21def add_decimal():
 22    if streamlit.session_state['number'].count('.') == 0:
 23        streamlit.session_state['number'] += '.'
 24
 25
 26def add_number_to_state(number):
 27    if streamlit.session_state['number'] == '0':
 28        streamlit.session_state['number'] = number
 29    else:
 30        streamlit.session_state['number'] += number
 31
 32
 33def on_click(function, display, *value):
 34    function(*value)
 35    show_number(display)
 36
 37
 38def add_buttons_to_column_1(column_1, display):
 39    column_1.button(
 40        label='<-', key='<-', width='stretch', on_click=on_click,
 41        args=[backspace, display],
 42    )
 43    column_1.button(
 44        label='7', key='7', width='stretch', on_click=on_click,
 45        args=[add_number_to_state, display, '7'],
 46    )
 47    column_1.button(
 48        label='4', key='4', width='stretch', on_click=on_click,
 49        args=[add_number_to_state, display, '4'],
 50    )
 51    column_1.button(
 52        label='1', key='1', width='stretch', on_click=on_click,
 53        args=[add_number_to_state, display, '1'],
 54    )
 55    column_1.button(
 56        label='+/-', key='+/-', width='stretch', on_click=on_click,
 57        args=[plus_minus, display],
 58    )
 59
 60
 61def add_buttons_to_column_2(column_2, display):
 62    column_2.button(
 63        label='C', key='C', width='stretch', type='primary',
 64    )
 65    column_2.button(
 66        label='8', key='8', width='stretch', on_click=on_click,
 67        args=[add_number_to_state, display, '8'],
 68    )
 69    column_2.button(
 70        label='5', key='5', width='stretch', on_click=on_click,
 71        args=[add_number_to_state, display, '5'],
 72    )
 73    column_2.button(
 74        label='2', key='2', width='stretch', on_click=on_click,
 75        args=[add_number_to_state, display, '2'],
 76    )
 77    column_2.button(
 78        label='0', key='0', width='stretch', on_click=on_click,
 79        args=[add_number_to_state, display, '0'],
 80    )
 81
 82
 83def add_buttons_to_column_3(column_3, display):
 84    column_3.button(
 85        label='AC', key='AC', width='stretch', type='primary',
 86    )
 87    column_3.button(
 88        label='9', key='9', width='stretch', on_click=on_click,
 89        args=[add_number_to_state, display, '9'],
 90    )
 91    column_3.button(
 92        label='6', key='6', width='stretch', on_click=on_click,
 93        args=[add_number_to_state, display, '6'],
 94    )
 95    column_3.button(
 96        label='3', key='3', width='stretch', on_click=on_click,
 97        args=[add_number_to_state, display, '3'],
 98    )
 99    column_3.button(
100        label='.', key='.', width='stretch', on_click=on_click,
101        args=[add_decimal, display]
102    )
103
104
105def add_buttons_to_column_4(column_4):
106    column_4.button(
107        label='/', key='/', width='stretch', type='primary',
108    )
109    column_4.button(
110        label='X', key='X', width='stretch', type='primary',
111    )
112    column_4.button(
113        label=r'\-', key=r'\-', width='stretch', type='primary',
114    )
115    column_4.button(
116        label=r'\+', key=r'\+', width='stretch', type='primary',
117    )
118    column_4.button(
119        label='=', key='=', width='stretch', type='primary',
120    )
121
122
123def main():
124    streamlit.title('Calculator')
125    streamlit.session_state.setdefault('number', '0')
126    display = streamlit.container(border=True)
127
128    column_1, column_2, column_3, operations = streamlit.columns(4)
129    add_buttons_to_column_1(column_1, display)
130    add_buttons_to_column_2(column_2, display)
131    add_buttons_to_column_3(column_3, display)
132    add_buttons_to_column_4(operations)
133
134
135if __name__ == '__main__':
136    main()

how to make a calculator 10: part 4: tests

the code from calculator/tests/test_streamlit_calculator.py from how to make a calculator 10: part 4

  1import random
  2import streamlit.testing.v1
  3import tests.test_calculator
  4import unittest
  5
  6
  7class TestStreamlitCalculator(unittest.TestCase):
  8
  9    def setUp(self):
 10        self.tester = streamlit.testing.v1.AppTest.from_file(
 11            'src/streamlit_calculator.py'
 12        )
 13        self.tester.run()
 14
 15    def test_streamlit_calculator_title(self):
 16        self.assertEqual(self.tester.title[0].value, 'Calculator')
 17
 18    def test_streamlit_calculator_display(self):
 19        self.assertEqual(
 20            self.tester.main.children[1].type,
 21            'flex_container'
 22        )
 23
 24    def test_streamlit_calculator_columns(self):
 25        self.assertEqual(len(self.tester.columns), 4)
 26        self.assertEqual(
 27            self.tester.columns[0].button('<-').label,
 28            '<-'
 29        )
 30        self.assertEqual(
 31            self.tester.columns[0].button('7').label,
 32            '7'
 33        )
 34        self.assertEqual(
 35            self.tester.columns[0].button('4').label,
 36            '4'
 37        )
 38        self.assertEqual(
 39            self.tester.columns[0].button('1').label,
 40            '1'
 41        )
 42        self.assertEqual(
 43            self.tester.columns[0].button('+/-').label,
 44            '+/-'
 45        )
 46        self.assertEqual(
 47            self.tester.columns[1].button('C').label,
 48            'C'
 49        )
 50        self.assertEqual(
 51            self.tester.columns[1].button('8').label,
 52            '8'
 53        )
 54        self.assertEqual(
 55            self.tester.columns[1].button('5').label,
 56            '5'
 57        )
 58        self.assertEqual(
 59            self.tester.columns[1].button('2').label,
 60            '2'
 61        )
 62        self.assertEqual(
 63            self.tester.columns[1].button('0').label,
 64            '0'
 65        )
 66
 67        self.assertEqual(
 68            self.tester.columns[2].button('AC').label,
 69            'AC'
 70        )
 71        self.assertEqual(
 72            self.tester.columns[2].button('9').label,
 73            '9'
 74        )
 75        self.assertEqual(
 76            self.tester.columns[2].button('6').label,
 77            '6'
 78        )
 79        self.assertEqual(
 80            self.tester.columns[2].button('3').label,
 81            '3'
 82        )
 83        self.assertEqual(
 84            self.tester.columns[2].button('.').label,
 85            '.'
 86        )
 87
 88        self.assertEqual(
 89            self.tester.columns[3].button('/').label,
 90            '/'
 91        )
 92        self.assertEqual(
 93            self.tester.columns[3].button('X').label,
 94            'X'
 95        )
 96        self.assertEqual(
 97            self.tester.columns[3].button('-').label,
 98            r'\-'
 99        )
100        self.assertEqual(
101            self.tester.columns[3].button('+').label,
102            r'\+'
103        )
104        self.assertEqual(
105            self.tester.columns[3].button('=').label,
106            '='
107        )
108
109    def test_streamlit_session_state(self):
110        expectation = '0'
111        for _ in range(0, 10):
112            number = random.choice('0123456789')
113            (
114                self.tester.button(number)
115                .click().run()
116            )
117            if expectation == '0':
118                expectation = number
119            else:
120                expectation += number
121        self.assertEqual(
122            self.tester.session_state['number'],
123            expectation
124        )
125
126    def test_streamlit_calculator_w_decimals(self):
127        for button in ('0.23.5.6.7.8.9'):
128            (
129                self.tester.button(button)
130                .click().run()
131            )
132        self.assertEqual(
133            self.tester.session_state['number'],
134            '.2356789'
135        )
136
137    def test_streamlit_calculator_w_plus_minus(self):
138        number = '963.0258741'
139        for button in number:
140            (
141                self.tester.button(button)
142                .click().run()
143            )
144        self.tester.button('+/-').click().run()
145        self.assertEqual(
146            self.tester.session_state['number'],
147            f'-{number}'
148        )
149
150        self.tester.session_state['number'] = '0'
151        number = '-963.0258741'
152        for button in number:
153            (
154                self.tester.button(button)
155                .click().run()
156            )
157
158        self.tester.button('+/-').click().run()
159        self.assertEqual(
160            self.tester.session_state['number'],
161            number[1:]
162        )
163
164    def test_streamlit_calculator_reset_state(self):
165        numbers = '123456789'
166        number = random.choice(numbers)
167        self.tester.button(number).click().run()
168        self.assertEqual(
169            self.tester.session_state['number'],
170            number
171        )
172        self.tester.button('C').click().run()
173        self.assertEqual(
174            self.tester.session_state['number'],
175            '0'
176        )
177
178        number = random.choice(numbers)
179        self.tester.button(number).click().run()
180        self.assertEqual(
181            self.tester.session_state['number'],
182            number
183        )
184        self.tester.button('AC').click().run()
185        self.assertEqual(
186            self.tester.session_state['number'],
187            '0'
188        )
189
190    @unittest.skip
191    def test_streamlit_calculator_operations(self):
192        # first_number = '1'
193        first_number = tests.test_calculator.a_random_number()
194        first_number = str(first_number)
195        second_number = '2'
196
197        for character in first_number:
198            if character == '-':
199                self.tester.button('+/-').click().run()
200            else:
201                self.tester.button(character).click().run()
202        self.tester.button('+').click().run()
203        self.assertEqual(
204            self.tester.session_state['first_number'],
205            first_number
206        )
207
208        self.tester.button(second_number).click().run()
209        self.tester.button('=').click().run()
210        self.assertEqual(
211            self.tester.session_state['second_number'],
212            second_number
213        )
214
215        self.assertEqual(
216            self.tester.session_state['number'],
217            str(float(first_number) + float(second_number))
218        )
219
220
221# Exceptions seen
222# NameError
223# AttributeError
224# AssertionError
225# SyntaxError
226# KeyError
227# streamlit.errors.StreamlitDuplicateElementKey
228# TypeError

how to make a calculator 10: part 4: solutions

the code from calculator/src/streamlit_calculator.py from how to make a calculator 10: part 4

  1import streamlit
  2
  3
  4def show_number(display):
  5    display.write(streamlit.session_state['number'])
  6
  7
  8def plus_minus():
  9    if streamlit.session_state['number'].startswith('-'):
 10        number = streamlit.session_state['number'][1:]
 11    else:
 12        number = '-' + streamlit.session_state['number']
 13    streamlit.session_state['number'] = number
 14
 15
 16def backspace():
 17    number = streamlit.session_state['number'][:-1]
 18    streamlit.session_state['number'] = number
 19
 20
 21def add_decimal():
 22    if streamlit.session_state['number'].count('.') == 0:
 23        streamlit.session_state['number'] += '.'
 24
 25
 26def add_number_to_state(number):
 27    if streamlit.session_state['number'] == '0':
 28        streamlit.session_state['number'] = number
 29    else:
 30        streamlit.session_state['number'] += number
 31
 32
 33def on_click(function, display, *value):
 34    function(*value)
 35    show_number(display)
 36
 37
 38def add_buttons_to_column_1(column_1, display):
 39    column_1.button(
 40        label='<-', key='<-', width='stretch', on_click=on_click,
 41        args=[backspace, display],
 42    )
 43    column_1.button(
 44        label='7', key='7', width='stretch', on_click=on_click,
 45        args=[add_number_to_state, display, '7'],
 46    )
 47    column_1.button(
 48        label='4', key='4', width='stretch', on_click=on_click,
 49        args=[add_number_to_state, display, '4'],
 50    )
 51    column_1.button(
 52        label='1', key='1', width='stretch', on_click=on_click,
 53        args=[add_number_to_state, display, '1'],
 54    )
 55    column_1.button(
 56        label='+/-', key='+/-', width='stretch', on_click=on_click,
 57        args=[plus_minus, display],
 58    )
 59
 60
 61def add_buttons_to_column_2(column_2, display):
 62    column_2.button(
 63        label='C', key='C', width='stretch', type='primary',
 64    )
 65    column_2.button(
 66        label='8', key='8', width='stretch', on_click=on_click,
 67        args=[add_number_to_state, display, '8'],
 68    )
 69    column_2.button(
 70        label='5', key='5', width='stretch', on_click=on_click,
 71        args=[add_number_to_state, display, '5'],
 72    )
 73    column_2.button(
 74        label='2', key='2', width='stretch', on_click=on_click,
 75        args=[add_number_to_state, display, '2'],
 76    )
 77    column_2.button(
 78        label='0', key='0', width='stretch', on_click=on_click,
 79        args=[add_number_to_state, display, '0'],
 80    )
 81
 82
 83def add_buttons_to_column_3(column_3, display):
 84    column_3.button(
 85        label='AC', key='AC', width='stretch', type='primary',
 86    )
 87    column_3.button(
 88        label='9', key='9', width='stretch', on_click=on_click,
 89        args=[add_number_to_state, display, '9'],
 90    )
 91    column_3.button(
 92        label='6', key='6', width='stretch', on_click=on_click,
 93        args=[add_number_to_state, display, '6'],
 94    )
 95    column_3.button(
 96        label='3', key='3', width='stretch', on_click=on_click,
 97        args=[add_number_to_state, display, '3'],
 98    )
 99    column_3.button(
100        label='.', key='.', width='stretch', on_click=on_click,
101        args=[add_decimal, display]
102    )
103
104
105def add_buttons_to_column_4(column_4):
106    column_4.button(
107        label='/', key='/', width='stretch', type='primary',
108    )
109    column_4.button(
110        label='X', key='X', width='stretch', type='primary',
111    )
112    column_4.button(
113        label=r'\-', key=r'\-', width='stretch', type='primary',
114    )
115    column_4.button(
116        label=r'\+', key=r'\+', width='stretch', type='primary',
117    )
118    column_4.button(
119        label='=', key='=', width='stretch', type='primary',
120    )
121
122
123def main():
124    streamlit.title('Calculator')
125    streamlit.session_state.setdefault('number', '0')
126    display = streamlit.container(border=True)
127
128    column_1, column_2, column_3, operations = streamlit.columns(4)
129    add_buttons_to_column_1(column_1, display)
130    add_buttons_to_column_2(column_2, display)
131    add_buttons_to_column_3(column_3, display)
132    add_buttons_to_column_4(operations)
133
134
135if __name__ == '__main__':
136    main()