how to make a calculator 10: part 3


open the project

  • I `change directory`_ to the calculator folder_

    cd calculator
    
  • I make a new test file_ for the Streamlit_ website

    touch tests/test_streamlit_calculator.py
    
  • I add streamlit_ to the requirements.txt file_

    echo "streamlit" >> requirements.txt
    

    Streamlit_ is a Python_ library that is used for making websites, it is not part of `The Python Standard Library`_

  • I install the `Python packages`_ that I wrote in the requirements file_

    uv add --requirement requirements.txt
    

    the terminal shows it installed the `Python packages`_

  • I use pytest-watcher to run the tests

    uv run pytest-watcher . --now
    

    the terminal_ shows

    rootdir: .../pumping_python/calculator
    configfile: pyproject.toml
    collected 8 items
    
    tests/test_calculator.py .....                                [ 62%]
    tests/test_calculator_website.py ...                          [100%]
    
    ======================== 5 passed in X.YZs =========================
    

test_streamlit_calculator_state

streamlit_ has a `session state object`_ that I can use to keep values in between button presses. They work the same as class attributes and they are dictionaries - I can add key-value pairs to them


RED: make it fail


I add a test for the `session state object`_, I want it to hold the number when I click the buttons, in test_streamlit_calculator.py

46    def test_streamlit_calculator_operations_buttons(self):
47        for key in ('/', 'X', r'\-', r'\+', '=', 'C', 'AC'):
48            with self.subTest(key=key):
49                self.assertEqual(
50                    self.tester.button(key).proto.type,
51                    'primary'
52                )
53
54    def test_streamlit_calculator_state(self):
55        self.assertIsNone(self.tester.session_state['number'])
56
57
58# Exceptions seen

the terminal_ shows KeyError

KeyError: 'st.session_state has no key "number".
Did you forget to initialize it?
More info: https://docs.streamlit.io/develop/concepts/architecture/session-state#initialization'

GREEN: make it pass


  • I use the setdefault method to add a key that will hold the numbers to show in the Calculator, in streamlit_calculator.py

    83def main():
    84    streamlit.title('Calculator')
    85    streamlit.session_state.setdefault('number', '0')
    86    add_buttons()
    87
    88
    89if __name__ == '__main__':
    90    main()
    

    the terminal_ shows AssertionError

    AssertionError: 0 is not None
    
  • I change the assertIsNone_ to assertEqual_ in test_streamlit_calculator.py

    107    def test_streamlit_calculator_state(self):
    108        self.assertEqual(self.tester.session_state['number'], '0')
    

    the test passes


REFACTOR: make it better


  • I add the `session state object`_ to the show function so that when a button is clicked, it will be added to the `session state object`_ then shown in the disply

    4def show(display, number):
    5    # display.write(number)
    6    streamlit.session_state['number'] += number
    7    display.write(streamlit.session_state['number'])
    

    the test passes

  • I remove the commented line

    4def show(display, number):
    5    streamlit.session_state['number'] += number
    6    display.write(streamlit.session_state['number'])
    
  • I add an assertion to test what happens to the `session state object`_ when I press a button for a number

    54    def test_streamlit_calculator_state(self):
    55        self.assertEqual(self.tester.session_state['number'], '0')
    56        self.tester.button('1').click().run()
    57        self.assertEqual(self.tester.session_state['number'], '0')
    
    • .click() presses the button

    • .run() runs the program - streamlit_calculator.py, which is what happens when a person uses the application in the browser

    the terminal_ shows AssertionError

    AssertionError: '01' != '0'
    

    this is a problem

  • I change the expectation to match

    57        self.assertEqual(self.tester.session_state['number'], '01')
    

    the test passes

  • I add more button clicks

    57        self.assertEqual(self.tester.session_state['number'], '01')
    58        self.tester.button('2').click().run()
    59        self.tester.button('3').click().run()
    60        self.tester.button('4').click().run()
    61        self.assertEqual(self.tester.session_state['number'], '01')
    

    the terminal_ shows AssertionError

    AssertionError: '01234' != '01'
    
  • I change the expectation to match

    61        self.assertEqual(
    62            self.tester.session_state['number'], '01234'
    63        )
    

    the test passes

  • I refresh the browser and click on all the numbers

    Many Numbers

    the calculator keeps numbers when I press the buttons and the number starts with 0 like the test

  • I want the calculator to remove 0 when it is the first number after I click on other numbers. I change the assertions

    54    def test_streamlit_calculator_state(self):
    55        self.assertEqual(self.tester.session_state['number'], '0')
    56        self.tester.button('1').click().run()
    57        self.assertEqual(self.tester.session_state['number'], '1')
    58        self.tester.button('2').click().run()
    59        self.tester.button('3').click().run()
    60        self.tester.button('4').click().run()
    61        self.assertEqual(
    62            self.tester.session_state['number'], '1234'
    63        )
    

    the terminal_ shows AssertionError

    AssertionError: '01' != '1'
    
  • I add a condition to the show function in streamlit_calculator.py

    4def show(display, number):
    5    if streamlit.session_state['number'] == '0':
    6        streamlit.session_state['number'] = number
    7    else:
    8        streamlit.session_state['number'] += number
    9    display.write(streamlit.session_state['number'])
    

    the test passes

  • I refresh the browser and try all the numbers

    Many Numbers Without 0 first

    the number no longer starts with 0

  • I import the `random module`_ to use random numbers for the test, in test_streamlit_calculator.py

    1import random
    2import streamlit.testing.v1
    3import unittest
    4
    5
    6class TestStreamlitCalculator(unittest.TestCase):
    
  • I use it in test_streamlit_calculator_state

    55    def test_streamlit_calculator_state(self):
    56        numbers = '0123456789'
    57        self.assertEqual(self.tester.session_state['number'], '0')
    58        # self.tester.button('1').click().run()
    59        x = random.choice(numbers)
    60        self.tester.button(x).click().run()
    61        self.assertEqual(self.tester.session_state['number'], '1')
    
    • numbers = '0123456789' makes a string_ with ten numbers

    • x = random.choice(numbers) picks a random number from the ten numbers and points x to it

    the terminal_ shows. AssertionError

    AssertionError: 'X' != '1'
    

    where X is a random number

  • I change the expectation

    55    def test_streamlit_calculator_state(self):
    56        numbers = '0123456789'
    57        self.assertEqual(self.tester.session_state['number'], '0')
    58        # self.tester.button('1').click().run()
    59        x = random.choice(numbers)
    60        self.tester.button(x).click().run()
    61        self.assertEqual(self.tester.session_state['number'], x)
    62        self.tester.button('2').click().run()
    63        self.tester.button('3').click().run()
    64        self.tester.button('4').click().run()
    65        self.assertEqual(
    66            self.tester.session_state['number'], '1234'
    67        )
    

    when x is not 1, the terminal_ shows AssertionError

    AssertionError: 'X234' != '1234'
    
  • I use random numbers for the other button presses

    61        self.assertEqual(self.tester.session_state['number'], x)
    62        # self.tester.button('2').click().run()
    63        # self.tester.button('3').click().run()
    64        # self.tester.button('4').click().run()
    65        y = random.choice(numbers)
    66        self.tester.button(y).click().run()
    67        z = random.choice(numbers)
    68        self.tester.button(z).click().run()
    69        a = random.choice(numbers)
    70        self.tester.button(a).click().run()
    71        self.assertEqual(
    72            self.tester.session_state['number'], '1234'
    73        )
    

    the terminal_ shows AssertionError

    AssertionError: 'XYZA' != '1234'
    
  • I use the random numbers in the expectation of the assertion

    124        self.assertEqual(
    125            self.tester.session_state['number'], f'{x}{y}{z}{a}'
    126        )
    

    when x is 0, the terminal_ shows AssertionError

    AssertionError: 'YZA' != '0YZA'
    

what is a while loop?

  • I can use a `while loop`_ to make sure that x is never 0, since the session_state['number'] is always 0 at the beginning. I add a `while statement`_ to test_streamlit_calculator_state

    55    def test_streamlit_calculator_state(self):
    56        numbers = '0123456789'
    57        self.assertEqual(self.tester.session_state['number'], '0')
    58        # self.tester.button('1').click().run()
    59        x = random.choice(numbers)
    60        while x == '0':
    61            x = random.choice(numbers)
    62        else:
    63            self.tester.button(x).click().run()
    64        self.tester.button(x).click().run()
    65        self.assertEqual(self.tester.session_state['number'], x)
    
    • x = random.choice(numbers) points x to a random number from the numbers variable

      • if x is not equal to '0' it goes to the next block else: self.tester.button(x).click().run()

      • if x is equal to '0' it goes to while x == '0': which makes a loop that will continue to run as long as x is equal to '0'

        • inside the loop x = random.choice(numbers) points x to a random number from the numbers string_ then checks again to see if x is equal to '0'

          • if x is equal to 0 the loop runs again

          • if x is not equal to 0 in the loop, it leaves the loop and continues to the next block else: self.tester.button(x).click().run()

    I use ctrl+s on the keyboard a few times and there are no more random failures

  • I add a for loop to test with a 10 digit number

    74        self.assertEqual(
    75            self.tester.session_state['number'], f'{x}{y}{z}{a}'
    76        )
    77        for _ in range(0, 10):
    78            a_random_number = random.choice(numbers)
    79            (
    80                self.tester.button(a_random_number)
    81                    .click().run()
    82            )
    83            x += a_random_number
    84        self.assertEqual(
    85            self.tester.session_state['number'],
    86            x
    87        )
    88
    89
    90# Exceptions seen
    

    for _ in range(0, 10): uses _ because I do not need a name for each number from the `range object`_ in the for loop since I do not use it

    the terminal_ shows AssertionError

    AssertionError: 'XYZABCDEFGHIJK' != 'XBCDEFGHIJK'
    

    the first number and the last 10 numbers are the same in the two strings_

  • I comment out the other lines

    108    def test_streamlit_calculator_state(self):
    109        numbers = '0123456789'
    110        # self.assertEqual(self.tester.session_state['number'], '0')
    111        # self.tester.button('1').click().run()
    112        x = random.choice(numbers)
    113        while x == '0':
    114            x = random.choice(numbers)
    115        else:
    116            self.tester.button(x).click().run()
    117        self.assertEqual(self.tester.session_state['number'], x)
    118        # self.tester.button('2').click().run()
    119        # self.tester.button('3').click().run()
    120        # self.tester.button('4').click().run()
    121        # y = random.choice(numbers)
    122        # self.tester.button(y).click().run()
    123        # z = random.choice(numbers)
    124        # self.tester.button(z).click().run()
    125        # a = random.choice(numbers)
    126        # self.tester.button(a).click().run()
    127        # self.assertEqual(
    128        #     self.tester.session_state['number'], f'{x}{y}{z}{a}'
    129        # )
    130        for _ in range(0, 10):
    

    I use ctrl+s on the keyboard to run the tests a few times and the test is still green

  • I remove the commented lines

    55    def test_streamlit_calculator_state(self):
    56        numbers = '0123456789'
    57        self.assertEqual(self.tester.session_state['number'], '0')
    58        x = random.choice(numbers)
    59        while x == '0':
    60            x = random.choice(numbers)
    61        else:
    62            self.tester.button(x).click().run()
    63        self.assertEqual(self.tester.session_state['number'], x)
    64        for _ in range(0, 10):
    65            a_random_number = random.choice(numbers)
    66            (
    67                self.tester.button(a_random_number)
    68                    .click().run()
    69            )
    70            x += a_random_number
    71        self.assertEqual(
    72            self.tester.session_state['number'],
    73            x
    74        )
    
  • I use the Rename Symbol feature of the `Integrated Development Environment (IDE)`_ to change x to expectation

    55    def test_streamlit_calculator_state(self):
    56        numbers = '0123456789'
    57        self.assertEqual(self.tester.session_state['number'], '0')
    58        expectation = random.choice(numbers)
    59        while expectation == '0':
    60            expectation = random.choice(numbers)
    61        else:
    62            self.tester.button(expectation).click().run()
    63        self.assertEqual(
    64            self.tester.session_state['number'],
    65            expectation
    66        )
    67        for _ in range(0, 10):
    68            a_random_number = random.choice(numbers)
    69            (
    70                self.tester.button(a_random_number)
    71                    .click().run()
    72            )
    73            expectation += a_random_number
    74        self.assertEqual(
    75            self.tester.session_state['number'],
    76            expectation
    77        )
    78
    79
    80# Exceptions seen
    

test_streamlit_calculator_w_decimals


RED: make it fail


  • I add a test for decimal numbers

    74        self.assertEqual(
    75            self.tester.session_state['number'],
    76            expectation
    77        )
    78
    79    def test_streamlit_calculator_w_decimals(self):
    80        self.tester.button('0').click().run()
    81        self.tester.button('.').click().run()
    82        self.tester.button('2').click().run()
    83        self.tester.button('3').click().run()
    84        self.tester.button('.').click().run()
    85        self.tester.button('5').click().run()
    86        self.tester.button('.').click().run()
    87        self.tester.button('6').click().run()
    88        self.tester.button('.').click().run()
    89        self.tester.button('7').click().run()
    90        self.tester.button('.').click().run()
    91        self.tester.button('8').click().run()
    92        self.tester.button('9').click().run()
    93        self.assertEqual(
    94            self.tester.session_state['number'],
    95            '0.2356789'
    96        )
    97
    98
    99# Exceptions seen
    

    the terminal_ shows AssertionError

    AssertionError: '.23.5.6.7.8.9' != '0.2356789'
    
  • I refresh the browser and do the same thing

    Many Decimals

    the Calculator allows me add many decimal points


GREEN: make it pass


  • I add a function for decimals

     1import streamlit
     2
     3
     4def handle_decimals(display, number):
     5    if streamlit.session_state['number'].count('.') >= 1:
     6        return None
     7    else:
     8        streamlit.session_state['number'] += number
     9    display.write(streamlit.session_state['number'])
    10
    11
    12def show(display, number):
    

    the terminal_ shows AssertionError

    AssertionError: '.23.5.6.7.89' != '0.2356789'
    
  • I change the on_click parameter for the . button to use the new function

    70    column_3.button(
    71        '3', key='3', width='stretch',
    72        on_click=show, args=[display, '3'],
    73    )
    74    column_3.button(
    75        '.', key='.', width='stretch',
    76        on_click=handle_decimals, args=[display, '.'],
    77    )
    78
    79    operations.button(
    80        '/', key='/', width='stretch', type='primary',
    81    )
    

    the test passes


REFACTOR: make it better


  • I add the logical negation of the if statement to make it simpler

     4def handle_decimals(display, number):
     5    if streamlit.session_state['number'].count('.') > 0:
     6        return None
     7    # else:
     8    if streamlit.session_state['number'].count('.') == 0:
     9        streamlit.session_state['number'] += number
    10    display.write(streamlit.session_state['number'])
    

    the test is still green

  • I remove the other if statement

    4def handle_decimals(display, number):
    5    if streamlit.session_state['number'].count('.') == 0:
    6        streamlit.session_state['number'] += number
    7    display.write(streamlit.session_state['number'])
    

    still green

    • if streamlit.session_state['number'].count('.') == 0: checks if . is in session_state['number']

      • streamlit.session_state['number'].count('.') counts how many times . shows up in session_state['number']

      • if . is NOT in session_state['number'], then . gets added to session_state['number'] and the program runs the next line display.write(streamlit.session_state['number'])

      • if . is in session_state['number'], then the program runs the next line display.write(streamlit.session_state['number'])

  • I refresh the browser and try many decimals again

    One Decimal

    Yes! I can only do one decimal in a number

  • I add a for loop to test_streamlit_calculator_w_decimals in test_streamlit_calculator.py

    79    def test_streamlit_calculator_w_decimals(self):
    80        for button in ('0.23.5.6.7.8.9'):
    81            (
    82                self.tester.button(button)
    83                    .click().run()
    84            )
    85
    86        self.tester.button('0').click().run()
    87        self.tester.button('.').click().run()
    88        self.tester.button('2').click().run()
    89        self.tester.button('3').click().run()
    

    the terminal_ shows AssertionError

    AssertionError: '0.235678902356789' != '0.2356789'
    
  • I remove the other button presses

    79    def test_streamlit_calculator_w_decimals(self):
    80        for button in ('0.23.5.6.7.8.9'):
    81            (
    82                self.tester.button(button)
    83                    .click().run()
    84            )
    85
    86        self.assertEqual(
    87            self.tester.session_state['number'],
    88            '0.2356789'
    89        )
    90
    91
    92# Exceptions seen
    

    the test passes


test_streamlit_calculator_backspace

I want to be able to remove the last digit of a number


RED: make it fail


  • I add a new test

     86          self.assertEqual(
     87              self.tester.session_state['number'],
     88              '0.2356789'
     89          )
     90
     91      def test_streamlit_calculator_backspace(self):
     92          self.tester.button('1').click().run()
     93          self.tester.button('2').click().run()
     94          self.tester.button('3').click().run()
     95          self.tester.button('4').click().run()
     96          self.tester.button('<-').click().run()
     97          self.assertEqual(
     98              self.tester.session_state['number'],
     99              '123'
    100          )
    101
    102
    103  # Exceptions seen
    

    the terminal_ shows AssertionError

    AssertionError: '1234' != '123'
    
  • I try the same thing in the browser and it clears the screen


GREEN: make it pass


  • I add a function to streamlit_calculator.py

     1import streamlit
     2
     3
     4def backspace(display):
     5    streamlit.session_state['number'] = \
     6        streamlit.session_state['number'][:-1]
     7    display.write(streamlit.session_state['number'])
     8
     9
    10def handle_decimals(display, number):
    

    the terminal_ still shows AssertionError

  • I add the on_click parameter to the <- button in the add_buttons function

    24def add_buttons():
    25    display = streamlit.container(border=True)
    26    column_1, column_2, column_3, operations = streamlit.columns(4)
    27
    28    column_1.button(
    29        '<-', key='<-', width='stretch',
    30        on_click=backspace, args=[display],
    31    )
    32    column_1.button(
    33        '7', key='7', width='stretch',
    34        on_click=show, args=[display, '7'],
    35    )
    

    the test passes


REFACTOR: make it better


  • I add an `import statement`_ for tests/test_calculator.py in test_streamlit_calculator.py

    1import random
    2import streamlit.testing.v1
    3import tests.test_calculator
    4import unittest
    5
    6
    7class TestStreamlitCalculator(unittest.TestCase):
    
  • I add a variable with a for loop for a random number in test_streamlit_calculator_backspace

     92    def test_streamlit_calculator_backspace(self):
     93        a_random_number = tests.test_calculator.a_random_number()
     94        a_random_number = str(a_random_number)
     95
     96        for number in a_random_number:
     97            self.tester.button(number).click().run()
     98        self.tester.button('<-').click().run()
     99        self.assertEqual(
    100            self.tester.session_state['number'],
    101            a_random_number
    102        )
    103
    104        self.tester.button('1').click().run()
    105        self.tester.button('2').click().run()
    106        self.tester.button('3').click().run()
    
    • a_random_number = tests.test_calculator.a_random_number() uses the a_random_number function test_calculator.py that I made in how to use random numbers

    • a_random_number = str(a_random_number) changes the random number to a string_ since all the operations of the calculator have been with strings_ so far

    I use ctrl+s on the keyboard to run the test a few times because I am using random numbers and the terminal_ sometimes shows AssertionError

    AssertionError: 'LMN.OPQRSTUVWXYZ' != 'LMN.OPQRSTUVWXYZA'
    

    the last number was not removed. When the random number is a negative number, the terminal_ shows KeyError

    KeyError: '-'
    

    I need a test for the ‘+/-’ button

  • I add a while loop to make sure a_random_number is never negative

    92    def test_streamlit_calculator_backspace(self):
    93        a_random_number = tests.test_calculator.a_random_number()
    94        while a_random_number < 0:
    95            a_random_number = tests.test_calculator.a_random_number()
    96        a_random_number = str(a_random_number)
    97
    98        for number in a_random_number:
    

    I use ctrl+s on the keyboard to run the tests a few times and the terminal_ shows AssertionError

    AssertionError: 'BCD.EFGHIJKLMNOP' != 'BCD.EFGHIJKLMNOPQ'
    

    I remove the while statement when I test the ‘+/-’ button

  • I change the expectation to remove the last digit from the random number

    101            self.assertEqual(
    102                self.tester.session_state['number'],
    103                a_random_number[:-1]
    104            )
    

    the terminal shows AssertionError

    AssertionError: 'RS.TUVWXYZABCDEF123' != '123'
    

    because I have button presses after the test. I need a way to reset the numbers back to 0

  • I remove the other numbers

     99        for number in a_random_number:
    100            self.tester.button(number).click().run()
    101            self.assertEqual(
    102                self.tester.session_state['number'],
    103                a_random_number[:-1]
    104            )
    105            self.tester.button('<-').click().run()
    106            self.assertEqual(
    107                self.tester.session_state['number'],
    108                '123'
    109            )
    

    the terminal_ shows AssertionError

    AssertionError: 'GHI.JKLMNOPQRS' != '123'
    
  • I change the expectation

     92    def test_streamlit_calculator_backspace(self):
     93        a_random_number = tests.test_calculator.a_random_number()
     94        while a_random_number < 0:
     95            a_random_number = tests.test_calculator.a_random_number()
     96        a_random_number = str(a_random_number)
     97
     98        for number in a_random_number:
     99            self.tester.button(number).click().run()
    100        self.tester.button('<-').click().run()
    101        self.assertEqual(
    102            self.tester.session_state['number'],
    103            a_random_number[:-1]
    104        )
    105        self.tester.button('<-').click().run()
    106        self.assertEqual(
    107            self.tester.session_state['number'],
    108            a_random_number[:-2]
    109        )
    110
    111
    112# Exceptions seen
    

    the test passes

  • I go to the browser and type a few numbers, then click on <- and it removes the last number. Fantastic!


test_streamlit_calculator_w_plus_minus

Nothing happens when I click +/- in the calculator. I want it to

  • change positive numbers to negative numbers

  • change negative numbers to positive numbers


RED: make it fail


I add a new test for the +/-

106        self.assertEqual(
107            self.tester.session_state['number'],
108            a_random_number[:-2]
109        )
110
111    def test_streamlit_calculator_w_plus_minus(self):
112        self.tester.button('9').click().run()
113        self.assertEqual(
114            self.tester.session_state['number'], '9'
115        )
116
117        self.tester.button('+/-').click().run()
118        self.assertEqual(
119            self.tester.session_state['number'], '-9'
120        )
121
122
123# Exceptions seen

the terminal_ shows AssertionError

AssertionError: '9' != '-9'

GREEN: make it pass


  • I add a function for the +/- button in streamlit_calculator.py

     1import streamlit
     2
     3
     4def plus_minus(display):
     5    if not streamlit.session_state['number'].startswith('-'):
     6        number = '-' + streamlit.session_state['number']
     7
     8    streamlit.session_state['number'] = number
     9    display.write(streamlit.session_state['number'])
    10
    11
    12def backspace(display):
    

    the terminal_ still shows AssertionError

  • I add the on_click parameter for the +/- button in the add_buttons function

    50    column_1.button(
    51        '1', key='1', width='stretch',
    52        on_click=show, args=[display, '1'],
    53    )
    54    column_1.button(
    55        '+/-', key='+/-', width='stretch',
    56        on_click=plus_minus, args=[display],
    57    )
    58
    59    column_2.button(
    60        'C', key='C', width='stretch', type='primary',
    61    )
    62    column_2.button(
    63        '8', key='8', width='stretch',
    64        on_click=show, args=[display, '8'],
    65    )
    

    the test passes. I can turn a positive number to a negative one with the +/- button.


REFACTOR: make it better


  • I add another button press and assertion to make sure I can turn a negative number to a positive one with the +/- button, in test_streamlit_calculator.py

    117        self.tester.button('+/-').click().run()
    118        self.assertEqual(
    119            self.tester.session_state['number'], '-9'
    120        )
    121
    122        self.tester.button('+/-').click().run()
    123        self.assertEqual(
    124            self.tester.session_state['number'], '9'
    125        )
    126
    127
    128# Exceptions seen
    

    the terminal_ shows AssertionError

    AssertionError: '-9' != '9'
    
  • I add an if statement to the plus_minus function in test_streamlit_calculator.py

     4def plus_minus(display):
     5    if streamlit.session_state['number'].startswith('-'):
     6        number = streamlit.session_state['number'][1:]
     7    if not streamlit.session_state['number'].startswith('-'):
     8        number = '-' + streamlit.session_state['number']
     9
    10    streamlit.session_state['number'] = number
    11    display.write(streamlit.session_state['number'])
    

    the test passes

  • I use else_ for the second if statement

    the test is still green

  • I remove the commented line

     4def plus_minus(display):
     5    if streamlit.session_state['number'].startswith('-'):
     6        number = streamlit.session_state['number'][1:]
     7    else:
     8        number = '-' + streamlit.session_state['number']
     9
    10    streamlit.session_state['number'] = number
    11    display.write(streamlit.session_state['number'])
    
  • I add a variable to remove duplication in test_streamlit_calculator.py

    111    def test_streamlit_calculator_w_plus_minus(self):
    112        a_number = '9'
    113        self.tester.button('1').click().run()
    
  • I use the number in the assertions and button presses

    111    def test_streamlit_calculator_w_plus_minus(self):
    112        a_number = '9'
    113        # self.tester.button('1').click().run()
    114        self.tester.button(a_number).click().run()
    115        self.assertEqual(
    116            # self.tester.session_state['number'], '9'
    117            self.tester.session_state['number'], a_number
    118        )
    119
    120        self.tester.button('+/-').click().run()
    121        self.assertEqual(
    122            # self.tester.session_state['number'], '-9'
    123            self.tester.session_state['number'], f'-{a_number}'
    124        )
    125
    126        self.tester.button('+/-').click().run()
    127        self.assertEqual(
    128            # self.tester.session_state['number'], '9'
    129            self.tester.session_state['number'], a_number
    130        )
    

    the test is still green

  • I remove the commented lines

    111    def test_streamlit_calculator_w_plus_minus(self):
    112        a_number = '9'
    113        self.tester.button(a_number).click().run()
    114        self.assertEqual(
    115            self.tester.session_state['number'], a_number
    116        )
    117
    118        self.tester.button('+/-').click().run()
    119        self.assertEqual(
    120            self.tester.session_state['number'], f'-{a_number}'
    121        )
    122
    123        self.tester.button('+/-').click().run()
    124        self.assertEqual(
    125            self.tester.session_state['number'], a_number
    126        )
    
  • I try a bigger number

    111    def test_streamlit_calculator_w_plus_minus(self):
    112        a_number = '96'
    113        self.tester.button(a_number).click().run()
    114        self.assertEqual(
    115            self.tester.session_state['number'], a_number
    116        )
    

    the terminal_ shows KeyError

    KeyError: '96'
    

    I need separate button presses for the two numbers

  • I add a for loop

    111    def test_streamlit_calculator_w_plus_minus(self):
    112        a_number = '96'
    113        for number in a_number:
    114            self.tester.button(number).click().run()
    115        self.assertEqual(
    116            self.tester.session_state['number'], a_number
    117        )
    

    the test passes

  • I try a number with 10 digits

    111    def test_streamlit_calculator_w_plus_minus(self):
    112        a_number = '963.0258741'
    

    the terminal_ still shows green

  • I refresh the browser and try to make a negative number

    Negative Number

    when I click on the +/- button it turns a positive number negative

  • I try the +/- button again

    Negative Number

    the - is removed from the number to make it a positive number. Progress!


  • The last 4 functions in streamlit_calculator.py - plus_minus, backspace, handle_decimals and show look the same

    def function_name(display):
        statements
        display.write(streamlit.session_state['number'])
    

    some of the functions have number in the function signature

  • I add number in the parentheses for the plus_minus function to make it have the same signature as the show and handle_decimals functions

    4def plus_minus(display, number):
    5    if streamlit.session_state['number'].startswith('-'):
    6        number = streamlit.session_state['number'][1:]
    7    else:
    8        number = '-' + streamlit.session_state['number']
    

    the terminal_ shows AssertionError

    AssertionError: '1234567890' != '-1234567890'
    

    and TypeError

    TypeError: plus_minus() missing 1 required positional argument: 'number'
    
  • I add a second argument to the args parameter for the +/- button in the add_buttons function

    50    column_1.button(
    51        '1', key='1', width='stretch',
    52        on_click=show, args=[display, '1'],
    53    )
    54    column_1.button(
    55        '+/-', key='+/-', width='stretch',
    56        on_click=plus_minus, args=[display, '+/-'],
    57    )
    58
    59    column_2.button(
    60        'C', key='C', width='stretch', type='primary',
    61    )
    

    the test is green again

  • I add number in the parentheses for the backspace function to make it have the same signature as the show, handle_decimals and plus_minus functions

    the terminal_ shows AssertionError

    AssertionError: 'RST.UVWXYZABCDEFGH' != 'RST.UVWXYZABCDEFG'
    

    it also shows TypeError

    TypeError: backspace() missing 1 required positional argument: 'number'
    
  • I add a second argument to the args parameter for the <- button in the add_buttons function

    the test is green again

  • I add a function to show the state

     4import streamlit
     5
     6
     7def show_state(display):
     8    display.write(streamlit.session_state['number'])
     9
    10
    11def plus_minus(display, number):
    
  • I use the new function in the plus_minus function

     8def plus_minus(display, number):
     9    if streamlit.session_state['number'].startswith('-'):
    10        number = streamlit.session_state['number'][1:]
    11    else:
    12        number = '-' + streamlit.session_state['number']
    13
    14    streamlit.session_state['number'] = number
    15    # display.write(streamlit.session_state['number'])
    16    show_state(display)
    

    the test is still green

  • I remove the commented line

     8def plus_minus(display, number):
     9    if streamlit.session_state['number'].startswith('-'):
    10        number = streamlit.session_state['number'][1:]
    11    else:
    12        number = '-' + streamlit.session_state['number']
    13
    14    streamlit.session_state['number'] = number
    15    show_state(display)
    
  • I use the new function in the backspace function

    18def backspace(display, number):
    19    streamlit.session_state['number'] = \
    20        streamlit.session_state['number'][:-1]
    21    # display.write(streamlit.session_state['number'])
    22    show_state(display)
    

    the test is still green

  • I remove the commented line

    18def backspace(display, number):
    19    streamlit.session_state['number'] = \
    20        streamlit.session_state['number'][:-1]
    21    show_state(display)
    
  • I use the new function in the handle_decimals function

    24def handle_decimals(display, number):
    25    if streamlit.session_state['number'].count('.') == 0:
    26        streamlit.session_state['number'] += number
    27    # display.write(streamlit.session_state['number'])
    28    show_state(display)
    

    the test is still green

  • I remove the commented line

    24def handle_decimals(display, number):
    25    if streamlit.session_state['number'].count('.') == 0:
    26        streamlit.session_state['number'] += number
    27    show_state(display)
    
  • I use the new function in the show function

    30def show(display, number):
    31    if streamlit.session_state['number'] == '0':
    32        streamlit.session_state['number'] = number
    33    else:
    34        streamlit.session_state['number'] += number
    35    # display.write(streamlit.session_state['number'])
    36    show_state(display)
    

    the test is still green

  • I remove the commented line

    30def show(display, number):
    31    if streamlit.session_state['number'] == '0':
    32        streamlit.session_state['number'] = number
    33    else:
    34        streamlit.session_state['number'] += number
    35    show_state(display)
    
  • I add a new function for adding the number to the `session state object`_

    24def handle_decimals(display, number):
    25    if streamlit.session_state['number'].count('.') == 0:
    26        streamlit.session_state['number'] += number
    27    show_state(display)
    28
    29
    30def add_number_to_state(number):
    31    if streamlit.session_state['number'] == '0':
    32        streamlit.session_state['number'] = number
    33    else:
    34        streamlit.session_state['number'] += number
    35
    36
    37def show(display, number):
    
  • I add a function to handle all the button clicks

    37def show(display, number):
    38    if streamlit.session_state['number'] == '0':
    39        streamlit.session_state['number'] = number
    40    else:
    41        streamlit.session_state['number'] += number
    42    show_state(display)
    43
    44
    45def on_click(function, display, value):
    46    function(value)
    47    show_state(display)
    48
    49
    50def add_buttons():
    
  • I try the on_click function with the 7 button in the add_buttons function

    49def add_buttons():
    50    display = streamlit.container(border=True)
    51    column_1, column_2, column_3, operations = streamlit.columns(4)
    52
    53    column_1.button(
    54        '<-', key='<-', width='stretch',
    55        on_click=backspace, args=[display, '<-'],
    56    )
    57    column_1.button(
    58        '7', key='7', width='stretch', on_click=on_click,
    59        # on_click=show, args=[display, '7'],
    60        args=[add_number_to_state, display, '7'],
    61    )
    62    column_1.button(
    63        '4', key='4', width='stretch',
    64        on_click=show, args=[display, '4'],
    65    )
    

    the test is still green! Yes!!

  • I remove the commented line and use the on_click function with all the other number buttons in the add_buttons function

     50def add_buttons():
     51    display = streamlit.container(border=True)
     52    column_1, column_2, column_3, operations = streamlit.columns(4)
     53
     54    column_1.button(
     55        '<-', key='<-', width='stretch',
     56        on_click=backspace, args=[display, '<-'],
     57    )
     58    column_1.button(
     59        '7', key='7', width='stretch', on_click=on_click,
     60        args=[add_number_to_state, display, '7'],
     61    )
     62    column_1.button(
     63        '4', key='4', width='stretch', on_click=on_click,
     64        args=[add_number_to_state, display, '4'],
     65    )
     66    column_1.button(
     67        '1', key='1', width='stretch', on_click=on_click,
     68        args=[add_number_to_state, display, '1'],
     69    )
     70    column_1.button(
     71        '+/-', key='+/-', width='stretch',
     72        on_click=plus_minus, args=[display, '+/-'],
     73    )
     74
     75    column_2.button(
     76        'C', key='C', width='stretch', type='primary',
     77    )
     78    column_2.button(
     79        '8', key='8', width='stretch', on_click=on_click,
     80        args=[add_number_to_state, display, '8'],
     81    )
     82    column_2.button(
     83        '5', key='5', width='stretch', on_click=on_click,
     84        args=[add_number_to_state, display, '5'],
     85    )
     86    column_2.button(
     87        '2', key='2', width='stretch', on_click=on_click,
     88        args=[add_number_to_state, display, '2'],
     89    )
     90    column_2.button(
     91        '0', key='0', width='stretch', on_click=on_click,
     92        args=[add_number_to_state, display, '0'],
     93    )
     94
     95    column_3.button(
     96        'AC', key='AC', width='stretch', type='primary',
     97    )
     98    column_3.button(
     99        '9', key='9', width='stretch', on_click=on_click,
    100        args=[add_number_to_state, display, '9'],
    101    )
    102    column_3.button(
    103        '6', key='6', width='stretch', on_click=on_click,
    104        args=[add_number_to_state, display, '6'],
    105    )
    106    column_3.button(
    107        '3', key='3', width='stretch', on_click=on_click,
    108        args=[add_number_to_state, display, '3'],
    109    )
    110    column_3.button(
    111        '.', key='.', width='stretch',
    112        on_click=handle_decimals, args=[display, '.'],
    113    )
    114
    115    operations.button(
    116        '/', key='/', width='stretch', type='primary',
    117    )
    118    operations.button(
    119        'X', key='X', width='stretch', type='primary',
    120    )
    121    operations.button(
    122        r'\-', key=r'\-', width='stretch', type='primary',
    123    )
    124    operations.button(
    125        r'\+', key=r'\+', width='stretch', type='primary',
    126    )
    127    operations.button(
    128        '=', key='=', width='stretch', type='primary',
    129    )
    130
    131def main():
    

    still green

  • I remove the show function

    30def add_number_to_state(number):
    31    if streamlit.session_state['number'] == '0':
    32        streamlit.session_state['number'] = number
    33    else:
    34        streamlit.session_state['number'] += number
    35
    36
    37def on_click(function, display, number):
    

  • I add a new function for decimals

    24def handle_decimals(display, number):
    25    if streamlit.session_state['number'].count('.') == 0:
    26        streamlit.session_state['number'] += number
    27    show_state(display)
    28
    29
    30def add_decimal():
    31    if streamlit.session_state['number'].count('.') == 0:
    32        streamlit.session_state['number'] += '.'
    33
    34
    35def add_number_to_state(number):
    
  • I try the new function with the . button in the add_buttons function

    103    column_3.button(
    104        '3', key='3', width='stretch', on_click=on_click,
    105        args=[add_number_to_state, display, '3'],
    106    )
    107    column_3.button(
    108        '.', key='.', width='stretch', on_click=on_click,
    109        # on_click=handle_decimals, args=[display, '.'],
    110        args=[add_decimal, display],
    111    )
    112
    113    operations.button(
    114        '/', key='/', width='stretch', type='primary',
    115    )
    

    the terminal_ shows KeyError

    FAILED test_streamlit_calculator_backspace - KeyError: 'I'
    FAILED test_streamlit_calculator_w_decimals - KeyError: '2'
    

    and shows TypeError

    TypeError: on_click() missing 1 required positional argument: 'value'
    
  • I make the value parameter a choice in the on_click function

    42def on_click(function, display, *value):
    43    function(*value)
    44    show_state(display)
    

    the test passes

  • I remove the commented line

    107    column_3.button(
    108        '.', key='.', width='stretch', on_click=on_click,
    109        args=[add_decimal, display],
    110    )
    
  • I remove the handle_decimals function

    18def backspace(display, number):
    19    streamlit.session_state['number'] = \
    20        streamlit.session_state['number'][:-1]
    21    show_state(display)
    22
    23
    24def add_decimal():
    25    if streamlit.session_state['number'].count('.') == 0:
    26        streamlit.session_state['number'] += '.'
    

  • I change the on_click and args parameters for the <- button in the add_buttons function

    41def add_buttons():
    42    display = streamlit.container(border=True)
    43    column_1, column_2, column_3, operations = streamlit.columns(4)
    44
    45    column_1.button(
    46        '<-', key='<-', width='stretch', on_click=on_click,
    47        # on_click=backspace, args=[display, '<-'],
    48        args=[backspace, display],
    49    )
    50    column_1.button(
    51        '7', key='7', width='stretch', on_click=on_click,
    52        args=[add_number_to_state, display, '7'],
    53    )
    

    the terminal_ shows AssertionError

    AssertionError: 'MN.OPQRSTUVWXYZABC' != 'MN.OPQRSTUVWXYZAB'
    

    and TypeError

    TypeError: backspace() missing 2 required positional arguments: 'display' and 'number'
    
  • I change the backspace function

    18# def backspace(display, number):
    19def backspace():
    20    streamlit.session_state['number'] = \
    21        streamlit.session_state['number'][:-1]
    22    # show_state(display)
    

    the test passes

  • I remove the commented lines in the backspace function

    18def backspace():
    19    streamlit.session_state['number'] = \
    20        streamlit.session_state['number'][:-1]
    21
    22
    23def add_decimal():
    

  • I change the on_click and args parameters for the +/- button in the add_buttons function

    57    column_1.button(
    58        '1', key='1', width='stretch', on_click=on_click,
    59        args=[add_number_to_state, display, '1'],
    60    )
    61    column_1.button(
    62        '+/-', key='+/-', width='stretch', on_click=on_click,
    63        # on_click=plus_minus, args=[display, '+/-'],
    64        args=[plus_minus, display]
    65    )
    66
    67    column_2.button(
    68        'C', key='C', width='stretch', type='primary',
    69    )
    

    the terminal_ shows AssertionError

    AssertionError: '1234567890' != '-1234567890'
    

    and shows TypeError

    TypeError: plus_minus() missing 2 required positional arguments: 'display' and 'number'
    
  • I change the plus_minus function

     8# def plus_minus(display, number):
     9def plus_minus():
    10    if streamlit.session_state['number'].startswith('-'):
    11        number = streamlit.session_state['number'][1:]
    12    if not streamlit.session_state['number'].startswith('-'):
    13        number = '-' + streamlit.session_state['number']
    14
    15    streamlit.session_state['number'] = number
    16    # show_state(display)
    

    the test passes

  • I remove the commented lines in the plus_minus function

     8def plus_minus():
     9    if streamlit.session_state['number'].startswith('-'):
    10        number = streamlit.session_state['number'][1:]
    11    if not streamlit.session_state['number'].startswith('-'):
    12        number = '-' + streamlit.session_state['number']
    13    streamlit.session_state['number'] = number
    14
    15
    16def backspace():
    
  • I remove the commented line from the +/- button in the add_buttons function

    42    column_1.button(
    43        '<-', key='<-', width='stretch', on_click=on_click,
    44        args=[backspace, display],
    45    )
    

  • That was a lot especially when all the numbers have to change. I add a function for the buttons in the first column

    33def on_click(function, display, *value):
    34    function(*value)
    35    show_state(display)
    36
    37
    38def add_buttons_to_column_1(column_1, display):
    39    column_1.button(
    40        '<-', key='<-', width='stretch', on_click=on_click,
    41        args=[backspace, display],
    42    )
    43    column_1.button(
    44        '7', key='7', width='stretch', on_click=on_click,
    45        args=[add_number_to_state, display, '7'],
    46    )
    47    column_1.button(
    48        '4', key='4', width='stretch', on_click=on_click,
    49        args=[add_number_to_state, display, '4'],
    50    )
    51    column_1.button(
    52        '1', key='1', width='stretch', on_click=on_click,
    53        args=[add_number_to_state, display, '1'],
    54    )
    55    column_1.button(
    56        '+/-', key='+/-', width='stretch', on_click=on_click,
    57        args=[plus_minus, display]
    58    )
    59
    60
    61def add_buttons():
    
  • I call the new function in the add_buttons function

    61def add_buttons():
    62    display = streamlit.container(border=True)
    63    column_1, column_2, column_3, operations = streamlit.columns(4)
    64
    65    add_buttons_to_column_1(column_1, display)
    66    # column_1.button(
    67    #     '<-', key='<-', width='stretch', on_click=on_click,
    68    #     args=[backspace, display],
    69    # )
    70    # column_1.button(
    71    #     '7', key='7', width='stretch', on_click=on_click,
    72    #     args=[add_number_to_state, display, '7'],
    73    # )
    74    # column_1.button(
    75    #     '4', key='4', width='stretch', on_click=on_click,
    76    #     args=[add_number_to_state, display, '4'],
    77    # )
    78    # column_1.button(
    79    #     '1', key='1', width='stretch', on_click=on_click,
    80    #     args=[add_number_to_state, display, '1'],
    81    # )
    82    # column_1.button(
    83    #     '+/-', key='+/-', width='stretch', on_click=on_click,
    84    #     args=[plus_minus, display]
    85    # )
    86
    87    column_2.button(
    88        'C', key='C', width='stretch', type='primary',
    89    )
    

    the tests are still green

  • I remove the commented lines

    61def add_buttons():
    62    display = streamlit.container(border=True)
    63    column_1, column_2, column_3, operations = streamlit.columns(4)
    64
    65    add_buttons_to_column_1(column_1, display)
    66
    67    column_2.button(
    68        'C', key='C', width='stretch', type='primary',
    69    )
    
  • I add a function for adding buttons to the second column

    38def add_buttons_to_column_1(column_1, display):
    39    column_1.button(
    40        '<-', key='<-', width='stretch', on_click=on_click,
    41        args=[backspace, display],
    42    )
    43    column_1.button(
    44        '7', key='7', width='stretch', on_click=on_click,
    45        args=[add_number_to_state, display, '7'],
    46    )
    47    column_1.button(
    48        '4', key='4', width='stretch', on_click=on_click,
    49        args=[add_number_to_state, display, '4'],
    50    )
    51    column_1.button(
    52        '1', key='1', width='stretch', on_click=on_click,
    53        args=[add_number_to_state, display, '1'],
    54    )
    55    column_1.button(
    56        '+/-', 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        'C', key='C', width='stretch', type='primary',
    64    )
    65    column_2.button(
    66        '8', key='8', width='stretch', on_click=on_click,
    67        args=[add_number_to_state, display, '8'],
    68    )
    69    column_2.button(
    70        '5', key='5', width='stretch', on_click=on_click,
    71        args=[add_number_to_state, display, '5'],
    72    )
    73    column_2.button(
    74        '2', key='2', width='stretch', on_click=on_click,
    75        args=[add_number_to_state, display, '2'],
    76    )
    77    column_2.button(
    78        '0', key='0', width='stretch', on_click=on_click,
    79        args=[add_number_to_state, display, '0'],
    80    )
    81
    82
    83def add_buttons():
    
  • I add a call to add_buttons_to_column_2 in the add_buttons function

     83def add_buttons():
     84    display = streamlit.container(border=True)
     85    column_1, column_2, column_3, operations = streamlit.columns(4)
     86
     87    add_buttons_to_column_1(column_1, display)
     88    add_buttons_to_column_2(column_2, display)
     89
     90    # column_2.button(
     91    #     'C', key='C', width='stretch', type='primary',
     92    # )
     93    # column_2.button(
     94    #     '8', key='8', width='stretch', on_click=on_click,
     95    #     args=[add_number_to_state, display, '8'],
     96    # )
     97    # column_2.button(
     98    #     '5', key='5', width='stretch', on_click=on_click,
     99    #     args=[add_number_to_state, display, '5'],
    100    # )
    101    # column_2.button(
    102    #     '2', key='2', width='stretch', on_click=on_click,
    103    #     args=[add_number_to_state, display, '2'],
    104    # )
    105    # column_2.button(
    106    #     '0', key='0', width='stretch', on_click=on_click,
    107    #     args=[add_number_to_state, display, '0'],
    108    # )
    109
    110    column_3.button(
    111        'AC', key='AC', width='stretch', type='primary',
    112    )
    

    still green

  • I remove the commented lines

    83def add_buttons():
    84    display = streamlit.container(border=True)
    85    column_1, column_2, column_3, operations = streamlit.columns(4)
    86
    87    add_buttons_to_column_1(column_1, display)
    88    add_buttons_to_column_2(column_2, display)
    89
    90    column_3.button(
    91        'AC', key='AC', width='stretch', type='primary',
    92    )
    
  • I add a function to add buttons to the third column

     83def add_buttons_to_column_3(column_3, display):
     84    column_3.button(
     85        'AC', key='AC', width='stretch', type='primary',
     86    )
     87    column_3.button(
     88        '9', key='9', width='stretch', on_click=on_click,
     89        args=[add_number_to_state, display, '9'],
     90    )
     91    column_3.button(
     92        '6', key='6', width='stretch', on_click=on_click,
     93        args=[add_number_to_state, display, '6'],
     94    )
     95    column_3.button(
     96        '3', key='3', width='stretch', on_click=on_click,
     97        args=[add_number_to_state, display, '3'],
     98    )
     99    column_3.button(
    100        '.', key='.', width='stretch', on_click=on_click,
    101        args=[add_decimal, display],
    102    )
    103
    104
    105def add_buttons():
    
  • I use the add_buttons_to_column_3 function in the add_columns function

    105def add_buttons():
    106    display = streamlit.container(border=True)
    107    column_1, column_2, column_3, operations = streamlit.columns(4)
    108
    109    add_buttons_to_column_1(column_1, display)
    110    add_buttons_to_column_2(column_2, display)
    111    add_buttons_to_column_3(column_3, display)
    112
    113    # column_3.button(
    114    #     'AC', key='AC', width='stretch', type='primary',
    115    # )
    116    # column_3.button(
    117    #     '9', key='9', width='stretch', on_click=on_click,
    118    #     args=[add_number_to_state, display, '9'],
    119    # )
    120    # column_3.button(
    121    #     '6', key='6', width='stretch', on_click=on_click,
    122    #     args=[add_number_to_state, display, '6'],
    123    # )
    124    # column_3.button(
    125    #     '3', key='3', width='stretch', on_click=on_click,
    126    #     args=[add_number_to_state, display, '3'],
    127    # )
    128    # column_3.button(
    129    #     '.', key='.', width='stretch', on_click=on_click,
    130    #     args=[add_decimal, display],
    131    # )
    132
    133    operations.button(
    134        '/', key='/', width='stretch', type='primary',
    135    )
    

    green

  • I remove the commented lines

    105def add_buttons():
    106    display = streamlit.container(border=True)
    107    column_1, column_2, column_3, operations = streamlit.columns(4)
    108
    109    add_buttons_to_column_1(column_1, display)
    110    add_buttons_to_column_2(column_2, display)
    111    add_buttons_to_column_3(column_3, display)
    112
    113    operations.button(
    114        '/', key='/', width='stretch', type='primary',
    115    )
    
  • I add a function for the operations column

    105def add_buttons_to_column_4(column_4):
    106    column_4.button(
    107        '/', key='/', width='stretch', type='primary',
    108    )
    109    column_4.button(
    110        'X', key='X', width='stretch', type='primary',
    111    )
    112    column_4.button(
    113        r'\-', key=r'\-', width='stretch', type='primary',
    114    )
    115    column_4.button(
    116        r'\+', key=r'\+', width='stretch', type='primary',
    117    )
    118    column_4.button(
    119        '=', key='=', width='stretch', type='primary',
    120    )
    121
    122
    123def add_buttons():
    
  • I use the add_buttons_to_column_4 function in the add_buttons functions

    123def add_buttons():
    124    display = streamlit.container(border=True)
    125    column_1, column_2, column_3, operations = streamlit.columns(4)
    126
    127    add_buttons_to_column_1(column_1, display)
    128    add_buttons_to_column_2(column_2, display)
    129    add_buttons_to_column_3(column_3, display)
    130    add_buttons_to_column_4(operations)
    131
    132    # operations.button(
    133    #     '/', key='/', width='stretch', type='primary',
    134    # )
    135    # operations.button(
    136    #     'X', key='X', width='stretch', type='primary',
    137    # )
    138    # operations.button(
    139    #     r'\-', key=r'\-', width='stretch', type='primary',
    140    # )
    141    # operations.button(
    142    #     r'\+', key=r'\+', width='stretch', type='primary',
    143    # )
    144    # operations.button(
    145    #     '=', key='=', width='stretch', type='primary',
    146    # )
    147
    148def main():
    

    still green

  • I remove the commented lines

    123def add_buttons():
    124    display = streamlit.container(border=True)
    125    column_1, column_2, column_3, operations = streamlit.columns(4)
    126
    127    add_buttons_to_column_1(column_1, display)
    128    add_buttons_to_column_2(column_2, display)
    129    add_buttons_to_column_3(column_3, display)
    130    add_buttons_to_column_4(operations)
    131
    132
    133def main():
    

    okay! Enough playing, time to do the operations.


close the project


review

I now have three different versions of the same calculator:

  • Pure Python (chapters 1–8)

  • Flask website (chapter 9)

  • Streamlit web app (chapter 10) — the fastest and most beautiful version

The core calculator code never changed. All my tests still protect it. This is the real power of Test-Driven Development.

code from the chapter

Do you want to see all the CODE I typed in this chapter?


what is next?

You have completed an amazing journey from pure functions to real web applications!

You now know how to:

  • Build programs with Test-Driven Development

  • Turn them into Flask websites

  • Turn them into beautiful Streamlit apps


rate pumping python

If this has been a 7 star experience for you, please `CLICK HERE to leave a 5 star review of pumping python`_. It helps other people get into the book too