how to make a calculator 10: part 2


open the project

  • I `change directory`_ to the calculator folder_

    cd calculator
    
  • I use pytest-watcher to run the tests

    uv run pytest-watcher . --now
    

    the terminal_ shows

    rootdir: .../pumping_python/calculator
    configfile: pyproject.toml
    collected 10 items
    
    tests/test_calculator.py .....                                [ 50%]
    tests/test_calculator_website.py ...                          [ 80%]
    tests/test_streamlit_calculator.py ..                         [100%]
    
    ======================== 10 passed in X.YZs =========================
    
  • I open another terminal_ then use uv_ in the calculator folder_

    uv run streamlit run src/streamlit_calculator.py
    

    the terminal_ shows

    Collecting usage statistics.
    To deactivate, set browser.gatherUsageStats to false.
    
    
      You can now view your Streamlit app in your browser.
    
      Local URL: http://localhost:8501
      Network URL: http://ABC.DEF.GHI.JKL:8501
      External URL: http://MNO.PQR.STU.VWX:8501
    

    I use ctrl/option on the keyboard and click on http://localhost:8501 with the mouse to open the browser and it shows

    Calculator Streamlit Display

test_streamlit_calculator_buttons

I want to add buttons for the numbers and operations.


RED: make it fail


  • I add a new test with an assertion for the first button, in test_streamlit_calculator.py

    Note

    <- is <+- on the keyboard

    14    def test_streamlit_calculator_display(self):
    15        tester = streamlit.testing.v1.AppTest.from_file(
    16            'src/streamlit_calculator.py'
    17        )
    18        tester.run()
    19
    20        display = (
    21            tester.main.children[1].proto
    22                  .flex_container
    23        )
    24        self.assertEqual(display.gap_config.gap_size, 1)
    25        self.assertEqual(display.direction, 1)
    26        self.assertEqual(display.justify, 1)
    27        self.assertEqual(display.align, 1)
    28        self.assertTrue(display.border)
    29
    30    def test_streamlit_calculator_buttons(self):
    31        tester = streamlit.testing.v1.AppTest.from_file(
    32            'src/streamlit_calculator.py'
    33        )
    34        tester.run()
    35
    36        self.assertIsNone(tester.button('<-'))
    37
    38
    39# Exceptions seen
    

    the terminal_ shows KeyError

    KeyError: '<-'
    
  • I add KeyError to the list of Exceptions seen

    39# Exceptions seen
    40# NameError
    41# AttributeError
    42# AssertionError
    43# SyntaxError
    44# KeyError
    

GREEN: make it pass


  • I add a button in streamlit_calculator.py

    4def main():
    5    streamlit.title('Calculator')
    6    streamlit.container(border=True)
    7
    8    streamlit.button('<-')
    

    the terminal_ still shows KeyError

  • `streamlit buttons`_ have a key parameter. I add it

    8    streamlit.button('<-', key='<-')
    

    the terminal_ shows AssertionError

    AssertionError: Button(key='<-', label='<-') is not None
    

    the Button object has two keys - key and label

  • I change the assertion in test_streamlit_calculator.py

    36        self.assertIsNone(tester.button('<-').label)
    

    the terminal_ shows AssertionError

    AssertionError: '<-' is not None
    
  • I add <- as the expectation

    36        self.assertIsNone(tester.button('<-').label, '<-')
    

    the terminal_ shows AssertionError

    AssertionError: '<-' is not None : <-
    
  • I change assertIsNone_ to assertEqual_

    36        self.assertEqual(tester.button('<-').label, '<-')
    

    the test passes

  • I go to the browser, click refresh

    Calculator Streamlit First Button no column

    I see the button I just added


REFACTOR: make it better


  • I add the label parameter to the button in streamlit_calculator.py

    4def main():
    5    streamlit.title('Calculator')
    6    streamlit.container(border=True)
    7
    8    streamlit.button(label='<-', key='<-')
    

    the test is still green

  • I add an assertion for the next button, in test_streamlit_calculator.py

    30    def test_streamlit_calculator_buttons(self):
    31        tester = streamlit.testing.v1.AppTest.from_file(
    32            'src/streamlit_calculator.py'
    33        )
    34        tester.run()
    35
    36        self.assertEqual(tester.button('<-').label, '<-')
    37        self.assertEqual(tester.button('7').label, '7')
    

    the terminal_ shows KeyError

    KeyError: '7'
    
  • I add the button to streamlit_calculator.py

    8    streamlit.button(label='<-', key='<-')
    9    streamlit.button(label='7', key='7')
    

    the test passes

  • I click refresh in the browser

    Calculator Streamlit Second Button no column

    the second button is there

  • I add a for loop for all the buttons, in test_streamlit_calculator.py

    34    def test_streamlit_calculator_buttons(self):
    35        tester = streamlit.testing.v1.AppTest.from_file(
    36            'src/streamlit_calculator.py'
    37        )
    38        tester.run()
    39
    40        for key in (
    41            '<-', '7', '4', '1', '+/-',
    42            'C', '8', '5', '2', '0',
    43            'AC', '9', '6', '3', '.',
    44            '/', 'X', '-', '+', '=',
    45        ):
    46            with self.subTest(key=key):
    47                self.assertEqual(tester.button(key).label, key)
    48
    49        self.assertEqual(tester.button('<-').label, '<-')
    50        self.assertEqual(tester.button('7').label, '7')
    

    the terminal_ shows KeyError for 18 sub tests

    ================== 18 failed, 11 passed in X.YZs ===================
    
  • I add the buttons to streamlit_calculator.py

     4def main():
     5    streamlit.title('Calculator')
     6    streamlit.container(border=True)
     7
     8    streamlit.button(label='<-', key='<-')
     9    streamlit.button(label='7', key='7')
    10    streamlit.button(label='4', key='4')
    11    streamlit.button(label='1', key='1')
    12    streamlit.button(label='+/-', key='+/-')
    13
    14    streamlit.button(label='C', key='C')
    15    streamlit.button(label='8', key='8')
    16    streamlit.button(label='5', key='5')
    17    streamlit.button(label='2', key='2')
    18    streamlit.button(label='0', key='0')
    19
    20    streamlit.button(label='AC', key='AC')
    21    streamlit.button(label='9', key='9')
    22    streamlit.button(label='6', key='6')
    23    streamlit.button(label='3', key='3')
    24    streamlit.button(label='.', key='.')
    25
    26    streamlit.button(label='/', key='/')
    27    streamlit.button(label='X', key='X')
    28    streamlit.button(label='-', key='-')
    29    streamlit.button(label='+', key='+')
    30    streamlit.button(label='=', key='=')
    31
    32
    33if __name__ == '__main__':
    

    the test passes

  • I make a function to make the buttons

     1import streamlit
     2
     3
     4def add_buttons():
     5    streamlit.button(label='<-', key='<-')
     6    streamlit.button(label='7', key='7')
     7    streamlit.button(label='4', key='4')
     8    streamlit.button(label='1', key='1')
     9    streamlit.button(label='+/-', key='+/-')
    10
    11    streamlit.button(label='C', key='C')
    12    streamlit.button(label='8', key='8')
    13    streamlit.button(label='5', key='5')
    14    streamlit.button(label='2', key='2')
    15    streamlit.button(label='0', key='0')
    16
    17    streamlit.button(label='AC', key='AC')
    18    streamlit.button(label='9', key='9')
    19    streamlit.button(label='6', key='6')
    20    streamlit.button(label='3', key='3')
    21    streamlit.button(label='.', key='.')
    22
    23    streamlit.button(label='/', key='/')
    24    streamlit.button(label='X', key='X')
    25    streamlit.button(label='-', key='-')
    26    streamlit.button(label='+', key='+')
    27    streamlit.button(label='=', key='=')
    28
    29def main():
    
  • I call the new function in main

    30def main():
    31    streamlit.title('Calculator')
    32    streamlit.container(border=True)
    33    add_buttons()
    34
    35    streamlit.button('<-', key='<-')
    

    the test is still passing

  • the second terminal_ (for the streamlit_ application) shows streamlit.errors.StreamlitDuplicateElementKey

    streamlit.errors.StreamlitDuplicateElementKey:
    There are multiple elements with the same key='<-'.
    To fix this, please make sure that the key argument is unique for each element you create.
    
  • I add streamlit.errors.StreamlitDuplicateElementKey to the list of Exceptions seen, in test_streamlit_calculator.py

    49# Exceptions seen
    50# NameError
    51# AttributeError
    52# AssertionError
    53# SyntaxError
    54# KeyError
    55# streamlit.errors.StreamlitDuplicateElementKey
    
  • I check the browser and see all the buttons and the Exception

  • I remove the buttons from main in streamlit_calculator.py

    30def main():
    31    streamlit.title('Calculator')
    32    streamlit.container(border=True)
    33    add_buttons()
    34
    35
    36if __name__ == '__main__':
    37    main()
    

    the test is still green

  • The second terminal_ still shows streamlit.errors.StreamlitDuplicateElementKey

  • I check the browser

    Calculator Streamlit No Column All Buttons

    I see all the buttons, and problems

    • all the buttons are in one column, simple calculators have 4 columns - 3 for numbers and 1 for operations

    • 2 buttons are missing - addition and subtraction

    • the buttons have different sizes

  • I remove the other two assertions from test_streamlit_calculator_buttons in test_streamlit_calculator.py

    30    def test_streamlit_calculator_buttons(self):
    31        tester = streamlit.testing.v1.AppTest.from_file(
    32            'src/streamlit_calculator.py'
    33        )
    34        tester.run()
    35
    36        for key in (
    37            '<-', '7', '4', '1', '+/-',
    38            'C', '8', '5', '2', '0',
    39            'AC', '9', '6', '3', '.',
    40            '/', 'X', '-', '+', '=',
    41        ):
    42            with self.subTest(key=key):
    43                self.assertEqual(tester.button(key).label, key)
    44
    45
    46# Exceptions seen
    
  • before I continue, there is some repetition to remove. Each test makes the same `streamlit tester object`_. I add it to the `setUp method`_

     5class TestStreamlitCalculator(unittest.TestCase):
     6
     7    def setUp(self):
     8        self.tester = streamlit.testing.v1.AppTest.from_file(
     9            'src/streamlit_calculator.py'
    10        )
    11        self.tester.run()
    
  • I use the new class attribute in test_streamlit_calculator_title

    13    def test_streamlit_calculator_title(self):
    14        tester = streamlit.testing.v1.AppTest.from_file(
    15            'src/streamlit_calculator.py'
    16        )
    17        tester.run()
    18        # self.assertEqual(tester.title[0].value, 'Calculator')
    19        self.assertEqual(self.tester.title[0].value, 'Calculator')
    

    the test is still green

  • I remove the other lines from test_streamlit_calculator_title

    13    def test_streamlit_calculator_title(self):
    14        self.assertEqual(self.tester.title[0].value, 'Calculator')
    15
    16    def test_streamlit_calculator_display(self):
    
  • I use the new class attribute in test_streamlit_calculator_display

    16    def test_streamlit_calculator_display(self):
    17        tester = streamlit.testing.v1.AppTest.from_file(
    18            'src/streamlit_calculator.py'
    19        )
    20        tester.run()
    21
    22        display = (
    23            # tester.main.children[1].proto
    24            self.tester.main.children[1].proto
    25                .flex_container
    26        )
    27        self.assertEqual(display.gap_config.gap_size, 1)
    

    still green

  • I remove the commented line, tester variable and call to tester.run() from test_streamlit_calculator_display

    16    def test_streamlit_calculator_display(self):
    17        display = (
    18            self.tester.main.children[1].proto
    19                .flex_container
    20        )
    21        self.assertEqual(display.gap_config.gap_size, 1)
    22        self.assertEqual(display.direction, 1)
    23        self.assertEqual(display.justify, 1)
    24        self.assertEqual(display.align, 1)
    25        self.assertTrue(display.border)
    26
    27    def test_streamlit_calculator_buttons(self):
    
  • I do the same thing in test_streamlit_calculator_buttons

    27    def test_streamlit_calculator_buttons(self):
    28        tester = streamlit.testing.v1.AppTest.from_file(
    29            'src/streamlit_calculator.py'
    30        )
    31        tester.run()
    32
    33        for key in (
    34            '<-', '7', '4', '1', '+/-',
    35            'C', '8', '5', '2', '0',
    36            'AC', '9', '6', '3', '.',
    37            '/', 'X', '-', '+', '=',
    38        ):
    39            with self.subTest(key=key):
    40                # self.assertEqual(tester.button(key).label, key)
    41                self.assertEqual(self.tester.button(key).label, key)
    

    green

  • I remove the commented line, tester variable and call to tester.run() from test_streamlit_calculator_buttons

    27    def test_streamlit_calculator_buttons(self):
    28        for key in (
    29            '<-', '7', '4', '1', '+/-',
    30            'C', '8', '5', '2', '0',
    31            'AC', '9', '6', '3', '.',
    32            '/', 'X', '-', '+', '=',
    33        ):
    34            with self.subTest(key=key):
    35                self.assertEqual(self.tester.button(key).label, key)
    36
    37
    38# Exceptions seen
    

    on to columns


test_streamlit_calculator_columns_and_buttons

Calculator buttons are arranged in Columns and Rows. I can use `streamlit columns`_ to arrange the buttons.


RED: make it fail


I add a new test to make sure the Calculator has 4 columns

34            with self.subTest(key=key):
35                self.assertEqual(self.tester.button(key).label, key)
36
37    def test_streamlit_calculator_columns(self):
38        self.assertEqual(len(self.tester.columns), 4)
39
40
41# Exceptions seen

the terminal_ shows AssertionError

AssertionError: 0 != 4
  • len(self.tester.columns) returns the length of the columns object

  • `streamlit columns`_ are lists and I know how to work with lists


GREEN: make it pass


I add columns to the main function in streamlit_calculator.py

30def main():
31    streamlit.title('Calculator')
32    streamlit.container(border=True)
33    add_buttons()
34
35    streamlit.columns(4)
36
37
38if __name__ == '__main__':
39    main()

the test passes


REFACTOR: make it better


  • I add an assertion to check what is in the first column, in test_streamlit_calculator.py

    37    def test_streamlit_calculator_columns(self):
    38        self.assertEqual(len(self.tester.columns), 4)
    39
    40        self.assertIsNone(self.tester.columns[0].children)
    

    the terminal_ shows AssertionError

    AssertionError: {} is not None
    
  • I add the expectation

    40        self.assertIsNone(self.tester.columns[0].children, {})
    
  • I change assertIsNone_ to assertEqual_

    37    def test_streamlit_calculator_columns(self):
    38        self.assertEqual(len(self.tester.columns), 4)
    39
    40        self.assertEqual(
    41            self.tester.columns[0].children, {}
    42        )
    

    the test passes

  • I name the columns because I want to put buttons in them, in streamlit_calculator.py

    29def main():
    30    streamlit.title('Calculator')
    31    streamlit.container(border=True)
    32    add_buttons()
    33
    34    column_1, column_2, column_3, operations = streamlit.columns(4)
    

    the test is still green

  • I move the columns to the add_buttons function

     4def add_buttons():
     5    column_1, column_2, column_3, operations = streamlit.columns(4)
     6
     7    streamlit.button('<-', key='<-')
     8    streamlit.button('7', key='7')
     9    streamlit.button('4', key='4')
    10    streamlit.button('1', key='1')
    11    streamlit.button('+/-', key='+/-')
    

    still green

  • I add the button for <- to the first column

     4def add_buttons():
     5    column_1, column_2, column_3, operations = streamlit.columns(4)
     6
     7    # streamlit.button('<-', key='<-')
     8    column_1.button('<-', key='<-')
     9    streamlit.button('7', key='7')
    10    streamlit.button('4', key='4')
    11    streamlit.button('1', key='1')
    12    streamlit.button('+/-', key='+/-')
    

    the terminal_ shows AssertionError

    AssertionError: {0: Button(key='<-', label='<-')} != {}
    

    good, the button is in the column and I know how to test buttons

  • I change the assertion in test_streamlit_calculator_columns in test_streamlit_calculator.py

    37    def test_streamlit_calculator_columns(self):
    38        self.assertEqual(len(self.tester.columns), 4)
    39
    40        self.assertEqual(
    41            self.tester.columns[0].button('<-').label,
    42            ''
    43        )
    

    the terminal_ shows AssertionError

    AssertionError: '<-' != ''
    
  • I change the expectation of the assertion

    40        self.assertEqual(
    41            self.tester.columns[0].button('<-').label,
    42            '<-'
    43        )
    

    the test passes


  • I add a for loop for all the buttons in the first column, in test_streamlit_calculator.py

    37    def test_streamlit_calculator_columns(self):
    38        self.assertEqual(len(self.tester.columns), 4)
    39
    40        for key in ('<-', '7', '4', '1', '+/-'):
    41            with self.subTest(key=key):
    42                self.assertEqual(
    43                    self.tester.columns[0].button(key).label,
    44                    'BOOM!!!'
    45                )
    46
    47        self.assertEqual(
    48            self.tester.columns[0].button('<-').label,
    49            '<-'
    50        )
    

    the terminal_ shows AssertionError for the <- button

    SUBFAILED(key='<-') ... - AssertionError: '<-' != 'BOOM!!!'
    

    and KeyError

    SUBFAILED(key='7') ... - KeyError: '7'
    SUBFAILED(key='4') ... - KeyError: '4'
    SUBFAILED(key='1') ... - KeyError: '1'
    SUBFAILED(key='+/-') ... - KeyError: '+/-'
    
  • I change the expectation of the assertion

    42                self.assertEqual(
    43                    self.tester.columns[0].button(key).label,
    44                    key
    45                )
    

    the terminal_ still shows KeyError

  • I remove the commented line in the add_buttons function then move buttons to the first column in streamlit_calculator.py

     4def add_buttons():
     5    column_1, column_2, column_3, operations = streamlit.columns(4)
     6
     7    column_1.button('<-', key='<-')
     8    column_1.button('7', key='7')
     9    column_1.button('4', key='4')
    10    column_1.button('1', key='1')
    11    column_1.button('+/-', key='+/-')
    12
    13    streamlit.button('C', key='C')
    

    the test passes

  • I add a function for the buttons in the first column

     1import streamlit
     2
     3
     4def add_buttons_to_column_1(column_1):
     5    column_1.button(label='<-', key='<-')
     6    column_1.button(label='7', key='7')
     7    column_1.button(label='4', key='4')
     8    column_1.button(label='1', key='1')
     9    column_1.button(label='+/-', key='+/-')
    10
    11
    12def add_buttons():
    
  • I call the new function in the add_buttons function

    12def add_buttons():
    13    column_1, column_2, column_3, operations = streamlit.columns(4)
    14
    15    add_buttons_to_column_1(column_1)
    16    column_1.button(label='<-', key='<-')
    

    the terminal_ shows KeyError and streamlit.errors.StreamlitDuplicateElementKey

  • I remove the buttons for column 1 from the add_buttons function since add_buttons_to_column_1 now makes them

    12def add_buttons():
    13    column_1, column_2, column_3, operations = streamlit.columns(4)
    14
    15    add_buttons_to_column_1(column_1)
    16
    17    streamlit.button(label='C', key='C')
    

    the test is green again


  • I check the browser and the buttons still look the same

  • I remove the assertion after the for loop, then add another for loop for the second column, in test_streamlit_calculator.py

    37    def test_streamlit_calculator_columns(self):
    38        self.assertEqual(len(self.tester.columns), 4)
    39
    40        for key in ('<-', '7', '4', '1', '+/-'):
    41            with self.subTest(key=key):
    42                self.assertEqual(
    43                    self.tester.columns[0].button(key).label,
    44                    key
    45                )
    46
    47        for key in ('<-', '7', '4', '1', '+/-'):
    48            with self.subTest(key=key):
    49                self.assertEqual(
    50                    self.tester.columns[1].button(key).label,
    51                    key
    52                )
    53
    54
    55# Exceptions seen
    

    the terminal_ shows KeyError

    SUBFAILED(key='<-') ... - KeyError: '<-'
    SUBFAILED(key='7') ... - KeyError: '7'
    SUBFAILED(key='4') ... - KeyError: '4'
    SUBFAILED(key='1') ... - KeyError: '1'
    SUBFAILED(key='+/-') ... - KeyError: '+/-'
    
  • I move buttons to the second column in streamlit_calculator,py

     2def add_buttons():
     3    column_1, column_2, column_3, operations = streamlit.columns(4)
     4
     5    add_buttons_to_column_1(column_1)
     6
     7    column_2.button(label='C', key='C')
     8    column_2.button(label='8', key='8')
     9    column_2.button(label='5', key='5')
    10    column_2.button(label='2', key='2')
    11    column_2.button(label='0', key='0')
    12
    13    streamlit.button(label='AC', key='AC')
    

    the terminal_ shows KeyError

    SUBFAILED(key='C') ... - KeyError: 'C'
    SUBFAILED(key='8') ... - KeyError: '8'
    SUBFAILED(key='5') ... - KeyError: '5'
    SUBFAILED(key='2') ... - KeyError: '2'
    SUBFAILED(key='0') ... - KeyError: '0'
    
  • I use the keys for the second column, in test_streamlit_calculator.py

    47        for key in ('C', '8', '5', '2', '0'):
    48            with self.subTest(key=key):
    49                self.assertEqual(
    50                    self.tester.columns[1].button(key).label,
    51                    key
    52                )
    

    the test passes

  • I check the browser

    Calculator 2 Columns

    there is a second column of buttons. Progress! It does not look right yet, baby steps.

  • I add a function for adding buttons to the second column in streamlit_calculator.py

     4def add_buttons_to_column_1(column_1):
     5    column_1.button(label='<-', key='<-')
     6    column_1.button(label='7', key='7')
     7    column_1.button(label='4', key='4')
     8    column_1.button(label='1', key='1')
     9    column_1.button(label='+/-', key='+/-')
    10
    11
    12def add_buttons_to_column_2(column_2):
    13    column_2.button(label='C', key='C')
    14    column_2.button(label='8', key='8')
    15    column_2.button(label='5', key='5')
    16    column_2.button(label='2', key='2')
    17    column_2.button(label='0', key='0')
    18
    19
    20def add_buttons():
    
  • I call add_buttons_to_column_2 in the add_buttons function

    20def add_buttons():
    21    column_1, column_2, column_3, operations = streamlit.columns(4)
    22
    23    add_buttons_to_column_1(column_1)
    24    add_buttons_to_column_2(column_2)
    25
    26    column_2.button(label='C', key='C')
    

    the terminal_ shows KeyError and streamlit.errors.StreamlitDuplicateElementKey

  • I remove the buttons for the second column from the add_buttons function

    20def add_buttons():
    21    column_1, column_2, column_3, operations = streamlit.columns(4)
    22
    23    add_buttons_to_column_1(column_1)
    24    add_buttons_to_column_2(column_2)
    25
    26    streamlit.button(label='AC', key='AC')
    

    the test is green again


  • I add a for loop for the third column in test_streamlit_calculator.py

    47        for key in ('C', '8', '5', '2', '0'):
    48            with self.subTest(key=key):
    49                self.assertEqual(
    50                    self.tester.columns[1].button(key).label,
    51                    key
    52                )
    53
    54        for key in ('AC', '9', '6', '3', '.'):
    55            with self.subTest(key=key):
    56                self.assertEqual(
    57                    self.tester.columns[1].button(key).label,
    58                    key
    59                )
    60
    61
    62# Exceptions seen
    

    the terminal_ shows KeyError

    SUBFAILED(key='AC') ... - KeyError: 'AC'
    SUBFAILED(key='9') ... - KeyError: '9'
    SUBFAILED(key='6') ... - KeyError: '6'
    SUBFAILED(key='3') ... - KeyError: '3'
    SUBFAILED(key='.') ... - KeyError: '.'
    
  • I change the column in the assertion

    54        for key in ('AC', '9', '6', '3', '.'):
    55            with self.subTest(key=key):
    56                self.assertEqual(
    57                    self.tester.columns[2].button(key).label,
    58                    key
    59                )
    

    the terminal_ still shows KeyError

  • I move buttons to the third column in the add_buttons function in streamlit_calculator.py

    20def add_buttons():
    21    column_1, column_2, column_3, operations = streamlit.columns(4)
    22
    23    add_buttons_to_column_1(column_1)
    24    add_buttons_to_column_2(column_2)
    25
    26    column_3.button(label='AC', key='AC')
    27    column_3.button(label='9', key='9')
    28    column_3.button(label='6', key='6')
    29    column_3.button(label='3', key='3')
    30    column_3.button(label='.', key='.')
    31
    32    streamlit.button(label='/', key='/')
    

    the test passes

  • I check the browser

    Calculator 3 Columns

    3 columns

  • I add a function for adding buttons to the third column

    12def add_buttons_to_column_2(column_2):
    13    column_2.button(label='C', key='C')
    14    column_2.button(label='8', key='8')
    15    column_2.button(label='5', key='5')
    16    column_2.button(label='2', key='2')
    17    column_2.button(label='0', key='0')
    18
    19
    20def add_buttons_to_column_3(column_3):
    21    column_3.button(label='AC', key='AC')
    22    column_3.button(label='9', key='9')
    23    column_3.button(label='6', key='6')
    24    column_3.button(label='3', key='3')
    25    column_3.button(label='.', key='.')
    26
    27
    28def add_buttons():
    
  • I call the new function in the add_buttons function

    28def add_buttons():
    29    column_1, column_2, column_3, operations = streamlit.columns(4)
    30
    31    add_buttons_to_column_1(column_1)
    32    add_buttons_to_column_2(column_2)
    33    add_buttons_to_column_3(column_3)
    34
    35    column_3.button(label='AC', key='AC')
    

    the terminal_ shows KeyError and streamlit.errors.StreamlitDuplicateElementKey

  • I remove the buttons for column 3 from the add_buttons function

    28def add_buttons():
    29    column_1, column_2, column_3, operations = streamlit.columns(4)
    30
    31    add_buttons_to_column_1(column_1)
    32    add_buttons_to_column_2(column_2)
    33    add_buttons_to_column_3(column_3)
    34
    35    streamlit.button(label='/', key='/')
    

    the test is green again. On to the next one.


  • I add a for loop with an assertion for the operations in the operations column, in test_streamlit_calculator.py

    54        for key in ('AC', '9', '6', '3', '.'):
    55            with self.subTest(key=key):
    56                self.assertEqual(
    57                    self.tester.columns[2].button(key).label,
    58                    key
    59                )
    60
    61        for key in ('/', 'X', '-', '+', '='):
    62            with self.subTest(key=key):
    63                self.assertEqual(
    64                    self.tester.columns[3].button(key).label,
    65                    key
    66                )
    67
    68
    69# Exceptions seen
    

    the terminal_ shows KeyError

    SUBFAILED(key='/') ... - KeyError: '/'
    SUBFAILED(key='X') ... - KeyError: 'X'
    SUBFAILED(key='-') ... - KeyError: '-'
    SUBFAILED(key='+') ... - KeyError: '+'
    SUBFAILED(key='=') ... - KeyError: '='
    
  • I move the buttons to the fourth column, in streamlit_calculator.py

    23    column_3.button('.', key='.')
    24
    25    operations.button('/', key='/')
    26    operations.button('X', key='X')
    27    operations.button('-', key='-')
    28    operations.button('+', key='+')
    29    operations.button('=', key='=')
    30
    31
    32def main():
    

    the test passes

  • I look in the browser

    Calculator 4 Columns

    there are 4 columns

  • I add a function to add buttons to the fourth column, in streamlit_calculator.py

    20def add_buttons_to_column_3(column_3):
    21    column_3.button(label='AC', key='AC')
    22    column_3.button(label='9', key='9')
    23    column_3.button(label='6', key='6')
    24    column_3.button(label='3', key='3')
    25    column_3.button(label='.', key='.')
    26
    27
    28def add_buttons_to_column_4(column_4):
    29    column_4.button(label='/', key='/')
    30    column_4.button(label='X', key='X')
    31    column_4.button(label='-', key='-')
    32    column_4.button(label='+', key='+')
    33    column_4.button(label='=', key='=')
    34
    35
    36def add_buttons():
    
  • I use add_buttons_to_column_4 in the add_buttons function

    36def add_buttons():
    37    column_1, column_2, column_3, operations = streamlit.columns(4)
    38
    39    add_buttons_to_column_1(column_1)
    40    add_buttons_to_column_2(column_2)
    41    add_buttons_to_column_3(column_3)
    42    add_buttons_to_column_4(operations)
    43
    44    operations.button(label='/', key='/')
    

    the terminal_ shows KeyError and streamlit.errors.StreamlitDuplicateElementKey

  • I remove the buttons for the fourth column from the add_buttons function

    36def add_buttons():
    37    column_1, column_2, column_3, operations = streamlit.columns(4)
    38
    39    add_buttons_to_column_1(column_1)
    40    add_buttons_to_column_2(column_2)
    41    add_buttons_to_column_3(column_3)
    42    add_buttons_to_column_4(operations)
    43
    44
    45def main():
    
  • I write the statements in the add_buttons function in the main function

    45def main():
    46    streamlit.title('Calculator')
    47    streamlit.container(border=True)
    48    # add_buttons()
    49
    50    column_1, column_2, column_3, operations = streamlit.columns(4)
    51    add_buttons_to_column_1(column_1)
    52    add_buttons_to_column_2(column_2)
    53    add_buttons_to_column_3(column_3)
    54    add_buttons_to_column_4(operations)
    

    the test is still green

  • I remove the commented line

    365def main():
    366    streamlit.title('Calculator')
    367    streamlit.container(border=True)
    368
    369    column_1, column_2, column_3, operations = streamlit.columns(4)
    370    add_buttons_to_column_1(column_1)
    371    add_buttons_to_column_2(column_2)
    372    add_buttons_to_column_3(column_3)
    373    add_buttons_to_column_4(operations)
    374
    375
    376if __name__ == '__main__':
    377    main()
    
  • I remove the add_buttons function because I no longer need it

    28def add_buttons_to_column_4(column_4):
    29    column_4.button(label='/', key='/')
    30    column_4.button(label='X', key='X')
    31    column_4.button(label='-', key='-')
    32    column_4.button(label='+', key='+')
    33    column_4.button(label='=', key='=')
    34
    35
    36def main():
    
  • I remove test_streamlit_calculator_buttons because test_streamlit_calculator_columns tests the same things - the button labels, in test_streamlit_calculator.py

    16    def test_streamlit_calculator_display(self):
    17        display = (
    18            self.tester.main.children[1].proto
    19                .flex_container
    20        )
    21        self.assertEqual(display.gap_config.gap_size, 1)
    22        self.assertEqual(display.direction, 1)
    23        self.assertEqual(display.justify, 1)
    24        self.assertEqual(display.align, 1)
    25        self.assertTrue(display.border)
    26
    27    def test_streamlit_calculator_columns(self):
    
  • I change the name of test_streamlit_calculator_columns to test_streamlit_calculator_columns_and_buttons

    16    def test_streamlit_calculator_display(self):
    17        display = (
    18            self.tester.main.children[1].proto
    19                .flex_container
    20        )
    21        self.assertEqual(display.gap_config.gap_size, 1)
    22        self.assertEqual(display.direction, 1)
    23        self.assertEqual(display.justify, 1)
    24        self.assertEqual(display.align, 1)
    25        self.assertTrue(display.border)
    26
    27    def test_streamlit_calculator_columns_and_buttons(self):
    28        self.assertEqual(len(self.tester.columns), 4)
    29
    30        for key in ('<-', '7', '4', '1', '+/-'):
    31            with self.subTest(key=key):
    32                self.assertEqual(
    33                    self.tester.columns[0].button(key).label,
    34                    key
    35                )
    36
    37        for key in ('C', '8', '5', '2', '0'):
    38            with self.subTest(key=key):
    39                self.assertEqual(
    40                    self.tester.columns[1].button(key).label,
    41                    key
    42                )
    43
    44        for key in ('AC', '9', '6', '3', '.'):
    45            with self.subTest(key=key):
    46                self.assertEqual(
    47                    self.tester.columns[2].button(key).label,
    48                    key
    49                )
    50
    51        for key in ('/', 'X', '-', '+', '='):
    52            with self.subTest(key=key):
    53                self.assertEqual(
    54                    self.tester.columns[3].button(key).label,
    55                    key
    56                )
    57
    58
    59# Exceptions seen
    
  • All the for loops in test_streamlit_calculator_columns_and_buttons look like this

    for key in (A, B, C, D, E):
        with self.subTest(key=key):
            self.assertEqual(
                self.tester.columns[index].button(key).label,
                key
            )
    

    I add 2 new for loops that put all the for loops for testing the buttons together

    27    def test_streamlit_calculator_columns_and_buttons(self):
    28        self.assertEqual(len(self.tester.columns), 4)
    29
    30        for column, keys in (
    31            (0, ('<-', '7', '4', '1', '+/-')),
    32            (1, ('C', '8', '5', '2', '0')),
    33            (2, ('AC', '9', '6', '3', '.')),
    34            (3, ('/', 'X', '-', '+', '=')),
    35        ):
    36            for key in keys:
    37                with self.subTest(key=key):
    38                    self.assertEqual(
    39                        (
    40                            self.tester.columns[column]
    41                                .button(key)
    42                                .label
    43                        ),
    44                        'BOOM!!!'
    45                    )
    46
    47        for key in ('<-', '7', '4', '1', '+/-'):
    

    the terminal_ shows AssertionError for 20 sub tests

    ================== 20 failed, 11 passed in X.YZs ===================
    
  • I change the expectation

    38                    self.assertEqual(
    39                        (
    40                            self.tester.columns[column]
    41                                .button(key)
    42                                .label
    43                        ),
    44                        key
    45                    )
    

    the test passes

  • I remove the other for loops

    27    def test_streamlit_calculator_columns_and_buttons(self):
    28        self.assertEqual(len(self.tester.columns), 4)
    29
    30        for column, keys in (
    31            (0, ('<-', '7', '4', '1', '+/-')),
    32            (1, ('C', '8', '5', '2', '0')),
    33            (2, ('AC', '9', '6', '3', '.')),
    34            (3, ('/', 'X', '-', '+', '=')),
    35        ):
    36            for key in keys:
    37                with self.subTest(key=key):
    38                    self.assertEqual(
    39                        (
    40                            self.tester.columns[column]
    41                                .button(key)
    42                                .label
    43                        ),
    44                        key
    45                    )
    46
    47
    48# Exceptions seen
    

how to change the size of streamlit buttons

I want all the buttons to be the same size.

  • `streamlit buttons`_ have a width option. I set it to stretch for the <- button in the add_buttons_to_column_1 function in streamlit_calculator.py

    4def add_buttons_to_column_1(column_1):
    5    column_1.button(label='<-', key='<-', width='stretch')
    6    column_1.button(label='7', key='7')
    7    column_1.button(label='4', key='4')
    8    column_1.button(label='1', key='1')
    9    column_1.button(label='+/-', key='+/-')
    

    the test is still green

  • I refresh the browser

    Calculator Stretched First

    better

  • I stretch the rest of the buttons in the first column

     4def add_buttons_to_column_1(column_1):
     5    column_1.button(label='<-', key='<-', width='stretch')
     6    column_1.button(label='7', key='7')
     7    column_1.button(label='4', key='4')
     8    column_1.button(label='1', key='1')
     9    column_1.button(label='+/-', key='+/-')
    10
    11
    12def add_buttons_to_column_2(column_2):
    

    still green

  • I check the browser

    Calculator Stretched First Column

    all the buttons in the first column have the same size

  • I stretch the buttons in the second column

    12def add_buttons_to_column_2(column_2):
    13    column_2.button(label='C', key='C', width='stretch')
    14    column_2.button(label='8', key='8', width='stretch')
    15    column_2.button(label='5', key='5', width='stretch')
    16    column_2.button(label='2', key='2', width='stretch')
    17    column_2.button(label='0', key='0', width='stretch')
    18
    19
    20def add_buttons_to_column_3(column_3):
    

    green

  • I stretch the buttons in the third column

    20def add_buttons_to_column_3(column_3):
    21    column_3.button(label='AC', key='AC', width='stretch')
    22    column_3.button(label='9', key='9', width='stretch')
    23    column_3.button(label='6', key='6', width='stretch')
    24    column_3.button(label='3', key='3', width='stretch')
    25    column_3.button(label='.', key='.', width='stretch')
    26
    27
    28def add_buttons_to_column_4(column_4):
    

    still green

  • I stretch the buttons in the fourth column

    28def add_buttons_to_column_4(column_4):
    29    column_4.button(label='/', key='/', width='stretch')
    30    column_4.button(label='X', key='X', width='stretch')
    31    column_4.button(label='-', key='-', width='stretch')
    32    column_4.button(label='+', key='+', width='stretch')
    33    column_4.button(label='=', key='=', width='stretch')
    34
    35
    36def main():
    

    the tests are still green

  • I refresh the browser

    Calculator Streamlit Stretch 4 Columns

    Good! The buttons all have the same size. I am still missing - and + that the tests says are there


how to use raw strings and escape characters

  • I add r and \ to escape the - character because it means something to streamlit_, which is why it does not show on the button in the add_buttons_to_column_4 function

    28def add_buttons_to_column_4(column_4):
    29    column_4.button(label='/', key='/', width='stretch')
    30    column_4.button(label='X', key='X', width='stretch')
    31    column_4.button(label=r'\-', key=r'\-', width='stretch')
    32    column_4.button(label='+', key='+', width='stretch')
    33    column_4.button(label='=', key='=', width='stretch')
    

    the terminal_ shows KeyError

    KeyError: '-'
    
  • I change the - to r'\-' in test_streamlit_calculator_columns_and_buttons in test_streamlit_calculator.py

    27    def test_streamlit_calculator_columns_and_buttons(self):
    28        self.assertEqual(len(self.tester.columns), 4)
    29
    30        for column, keys in (
    31            (0, ('<-', '7', '4', '1', '+/-')),
    32            (1, ('C', '8', '5', '2', '0')),
    33            (2, ('AC', '9', '6', '3', '.')),
    34            # (3, ('/', 'X', '-', '+', '=')),
    35            (3, ('/', 'X', r'\-', '+', '=')),
    36        ):
    37            for key in keys:
    

    the test passes

    • r makes this a raw string_ which means the characters are taken as they are, not what they stand for

    • \ escapes the character so that - is taken exactly as it is to show the character on the button. '-' means something different in streamlit_ so it does not show up as a label for the button

  • I add r and \ to escape the + character because it means something to streamlit_, which is why it does not show on the button in the add_buttons_to_column_4 function, in streamlit_calculator.py

    28def add_buttons_to_column_4(column_4):
    29    column_4.button(label='/', key='/', width='stretch')
    30    column_4.button(label='X', key='X', width='stretch')
    31    column_4.button(label=r'\-', key=r'\-', width='stretch')
    32    column_4.button(label=r'\+', key=r'\+', width='stretch')
    33    column_4.button(label='=', key='=', width='stretch')
    

    the terminal_ shows KeyError

    KeyError: '+'
    
  • I change the + to r'\+' in test_streamlit_calculator_columns_and_buttons in test_streamlit_calculator.py

    27    def test_streamlit_calculator_columns_and_buttons(self):
    28        self.assertEqual(len(self.tester.columns), 4)
    29
    30        for column, keys in (
    31            (0, ('<-', '7', '4', '1', '+/-')),
    32            (1, ('C', '8', '5', '2', '0')),
    33            (2, ('AC', '9', '6', '3', '.')),
    34            # (3, ('/', 'X', '-', '+', '=')),
    35            # (3, ('/', 'X', r'\-', '+', '=')),
    36            (3, ('/', 'X', r'\-', r'\+', '=')),
    37        ):
    38            for key in keys:
    

    the test passes

    • r makes this a raw string_ which means the characters are taken as they are, not what they stand for

    • \ escapes the character so that + is taken exactly as it is to show the character on the button. '+' means something different in streamlit_ so it does not show up as a label for the button

  • I remove the commented lines

    27    def test_streamlit_calculator_columns_and_buttons(self):
    28        self.assertEqual(len(self.tester.columns), 4)
    29
    30        for column, keys in (
    31            (0, ('<-', '7', '4', '1', '+/-')),
    32            (1, ('C', '8', '5', '2', '0')),
    33            (2, ('AC', '9', '6', '3', '.')),
    34            (3, ('/', 'X', r'\-', r'\+', '=')),
    35        ):
    36            for key in keys:
    37                with self.subTest(key=key):
    38                    self.assertEqual(
    39                        (
    40                            self.tester.columns[column]
    41                                .button(key)
    42                                .label
    43                        ),
    44                        key
    45                    )
    46
    47
    48# Exceptions seen
    
  • I check the browser

    Calculator Streamlit All labels

    yes! The calculator shows all the labels.


test_streamlit_calculator_operations_buttons

I want to change the colors of the buttons in the operations column


RED: make it fail


I add a test for the type parameter of `streamlit buttons`_

27      def test_streamlit_calculator_columns_and_buttons(self):
28          self.assertEqual(len(self.tester.columns), 4)
29
30          for column, keys in (
31              (0, ('<-', '7', '4', '1', '+/-')),
32              (1, ('C', '8', '5', '2', '0')),
33              (2, ('AC', '9', '6', '3', '.')),
34              (3, ('/', 'X', r'\-', r'\+', '=')),
35          ):
36              for key in keys:
37                  with self.subTest(key=key):
38                      self.assertEqual(
39                          (
40                              self.tester.columns[column]
41                                  .button(key)
42                                  .label
43                          ),
44                          key
45                      )
46
47      def test_streamlit_calculator_operations_buttons(self):
48          self.assertEqual(self.tester.button('/').proto.type, '')
49
50
51  # Exceptions seen

the terminal_ shows AssertionError

AssertionError: 'secondary' != ''

GREEN: make it pass


I change the expectation

47      def test_streamlit_calculator_operations_buttons(self):
48          self.assertEqual(
49              self.tester.button('/').proto.type,
50              'secondary'
51          )

the test passes


REFACTOR: make it better


  • I change the expectation from secondary to primary

    48        self.assertEqual(
    49            self.tester.button('/').proto.type,
    50            'primary'
    51        )
    

    the terminal_ shows AssertionError

    AssertionError: 'secondary' != 'primary'
    
  • I use the type parameter of `streamlit buttons`_ to change the colors of the buttons for the / button in the add_buttons_to_column_4 function, in streamlit_calculator.py

    28def add_buttons_to_column_4(column_4):
    29    column_4.button(
    30        label='/', key='/', width='stretch', type='primary'
    31    )
    32    column_4.button(label='X', key='X', width='stretch')
    33    column_4.button(label=r'\-', key=r'\-', width='stretch')
    34    column_4.button(label=r'\+', key=r'\+', width='stretch')
    35    column_4.button(label='=', key='=', width='stretch')
    

    the test passes

  • I refresh the browser

    Calculator Streamlit First Primary Button

    good

  • I add a for loop for all the buttons in the operations column

    46    def test_streamlit_calculator_operations_buttons(self):
    47        for key in ('/', 'X', r'\-', r'\+', '='):
    48            with self.subTest(key=key):
    49                self.assertEqual(
    50                    self.tester.button(key).proto.type,
    51                    'primary'
    52                )
    

    the terminal_ shows AssertionError

    SUBFAILED(key='X') ... - AssertionError: 'secondary' != 'primary'
    SUBFAILED(key='\\-') ... - AssertionError: 'secondary' != 'primary'
    SUBFAILED(key='\\+') ... - AssertionError: 'secondary' != 'primary'
    SUBFAILED(key='=') ... - AssertionError: 'secondary' != 'primary'
    
  • I add the type parameter to the buttons in the add_buttons_to_column_4 function in streamlit_calculator.py

    28def add_buttons_to_column_4(column_4):
    29    column_4.button(
    30        label='/', key='/', width='stretch', type='primary'
    31    )
    32    column_4.button(
    33        label='X', key='X', width='stretch', type='primary',
    34    )
    35    column_4.button(
    36        label=r'\-', key=r'\-', width='stretch', type='primary',
    37    )
    38    column_4.button(
    39        label=r'\+', key=r'\+', width='stretch', type='primary',
    40    )
    41    column_4.button(
    42        label='=', key='=', width='stretch', type='primary',
    43    )
    44
    45
    46def main():
    

    the test passes

  • I want the C and AC buttons to have the same colors as the buttons for the operations. I add them to the tuple_ in test_streamlit_calculator.py

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

    the terminal_ shows AssertionError

    SUBFAILED(key='C') ... - AssertionError: 'secondary' != 'primary'
    SUBFAILED(key='AC') ... - AssertionError: 'secondary' != 'primary'
    
  • I change the type for the AC button in the add_buttons_to_column_3 function in streamlit_calculator.py

    20def add_buttons_to_column_3(column_3):
    21    column_3.button(
    22        label='AC', key='AC', width='stretch', type='primary',
    23    )
    24    column_3.button(label='9', key='9', width='stretch')
    25    column_3.button(label='6', key='6', width='stretch')
    26    column_3.button(label='3', key='3', width='stretch')
    27    column_3.button(label='.', key='.', width='stretch')
    

    the terminal_ shows AssertionError

    SUBFAILED(key='C') ... - AssertionError: 'secondary' != 'primary'
    
  • I change the type for the C button in the add_buttons_to_column_2 function

    12def add_buttons_to_column_2(column_2):
    13    column_2.button(
    14        label='C', key='C', width='stretch', type='primary',
    15    )
    16    column_2.button(label='8', key='8', width='stretch')
    17    column_2.button(label='5', key='5', width='stretch')
    18    column_2.button(label='2', key='2', width='stretch')
    19    column_2.button(label='0', key='0', width='stretch')
    

    the test passes

  • I check the browser

    Calculator Streamlit Primary Buttons

    much better


how to show the numbers when I click on them

I want the calculator to show the number when I press a button

  • I add a variable to name the container so I can use it to show the numbers

    50def main():
    51    streamlit.title('Calculator')
    52    display = streamlit.container(border=True)
    53
    54    column_1, column_2, column_3, operations = streamlit.columns(4)
    55    add_buttons_to_column_1(column_1)
    56    add_buttons_to_column_2(column_2)
    57    add_buttons_to_column_3(column_3)
    58    add_buttons_to_column_4(operations)
    
  • I add a function to show the text of the button when it is clicked

    1import streamlit
    2
    3
    4def show(display, number):
    5    display.write(number)
    6
    7
    8def add_buttons_to_column_1(column_1):
    
  • `streamlit buttons`_ have an on_click parameter that lets me call a function when a button is pressed. It also takes an argument named args where I can pass in the positional arguments that the function I give for the on_click parameter takes. I pass the function and the display variable with a value as the arguments for the 7 button in the add_buttons_to_column_1 function

     8def add_buttons_to_column_1(column_1):
     9    column_1.button(label='<-', key='<-', width='stretch')
    10    column_1.button(
    11        label='7', key='7', width='stretch',
    12        on_click=show, args=[display, '7'],
    13    )
    14    column_1.button(label='4', key='4', width='stretch')
    15    column_1.button(label='1', key='1', width='stretch')
    16    column_1.button(label='+/-', key='+/-', width='stretch')
    

    the terminal_ shows KeyError

    ================== 26 failed, 12 passed in A.BCs ===================
    

    the terminal_ for the application shows NameError

    NameError: name 'display' is not defined
    
  • I add a parameter for the display variable in the function signature for add_buttons_to_column_1

    8def add_buttons_to_column_1(column_1, display):
    

    the terminal_ shows KeyError for 27 sub tests and the terminal_ for the streamlit_ application shows TypeError

    TypeError: add_buttons_to_column_1() missing 1 required positional argument: 'display'
    
  • I add the display variable to the call to add_buttons_to_column_1 in the main function

    57def main():
    58    streamlit.title('Calculator')
    59    display = streamlit.container(border=True)
    60
    61    column_1, column_2, column_3, operations = streamlit.columns(4)
    62    add_buttons_to_column_1(column_1, display)
    63    add_buttons_to_column_2(column_2)
    64    add_buttons_to_column_3(column_3)
    65    add_buttons_to_column_4(operations)
    

    the test passes

  • I go to the browser and click on the 7 button

    Calculator Streamlit Display 7 on click

    7 shows on the display. Yes!

  • I make the same change to the other numbers in the first column

     8def add_buttons_to_column_1(column_1, display):
     9    column_1.button(label='<-', key='<-', width='stretch')
    10    column_1.button(
    11        label='7', key='7', width='stretch',
    12        on_click=show, args=[display, '7'],
    13    )
    14    column_1.button(
    15        label='4', key='4', width='stretch',
    16        on_click=show, args=[display, '4']
    17    )
    18    column_1.button(
    19        label='1', key='1', width='stretch',
    20        on_click=show, args=[display, '1']
    21    )
    22    column_1.button(
    23        label='+/-', key='+/-', width='stretch',
    24        on_click=show, args=[display, '+/-']
    25    )
    

    the test is still green

  • I do the same thing for the numbers in the second column

    28def add_buttons_to_column_2(column_2):
    29    column_2.button(
    30        label='C', key='C', width='stretch', type='primary',
    31    )
    32    column_2.button(
    33        label='8', key='8', width='stretch',
    34        on_click=show, args=[display, '8']
    35    )
    36    column_2.button(
    37        label='5', key='5', width='stretch',
    38        on_click=show, args=[display, '5']
    39    )
    40    column_2.button(
    41        label='2', key='2', width='stretch',
    42        on_click=show, args=[display, '2']
    43    )
    44    column_2.button(
    45        label='0', key='0', width='stretch',
    46        on_click=show, args=[display, '0']
    47    )
    

    the terminal_ shows KeyError for 20 sub tests and the terminal_ for the application shows NameError

  • I add display to the function signature of the add_buttons_to_column_2 function

    28def add_buttons_to_column_2(column_2, display):
    

    the terminal_ shows KeyError for 22 sub tests and the terminal_ for the application shows TypeError

    TypeError: add_buttons_to_column_2() missing 1 required positional argument: 'display'
    
  • I add display to the call to the add_buttons_to_column_2 function in the main function

    78def main():
    79    streamlit.title('Calculator')
    80    display = streamlit.container(border=True)
    81
    82    column_1, column_2, column_3, operations = streamlit.columns(4)
    83    add_buttons_to_column_1(column_1, display)
    84    add_buttons_to_column_2(column_2, display)
    85    add_buttons_to_column_3(column_3)
    86    add_buttons_to_column_4(operations)
    

    the test passes

  • I make the same change for the numbers in the third column

    47def add_buttons_to_column_3(column_3):
    48    column_3.button(
    49        label='AC', key='AC', width='stretch', type='primary',
    50    )
    51    column_3.button(
    52        label='9', key='9', width='stretch',
    53        on_click=show, args=[display, '9'],
    54    )
    55    column_3.button(
    56        label='6', key='6', width='stretch',
    57        on_click=show, args=[display, '6'],
    58    )
    59    column_3.button(
    60        label='3', key='3', width='stretch',
    61        on_click=show, args=[display, '3'],
    62    )
    63    column_3.button(
    64        label='.', key='.', width='stretch',
    65        on_click=show, args=[display, '.'],
    66    )
    

    green

  • I go to the browser to test the numbers and they show up in the box, with one problem - every time I press a button it shows a new number. I want the numbers to stay so that I can make numbers that have more than one digit


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