separate and equal functions

All the functions in the functions project were written in test_functions.py. I want to move them to functions.py in the src folder so that I can keep the tests and solutions separate like I did with the other projects.


preview

I have these tests by the end of the chapter

  1import src.functions
  2
  3
  4def test_making_a_function_w_pass():
  5    assert src.functions.w_pass() is None
  6
  7
  8def test_making_a_function_w_return():
  9    assert src.functions.w_return() is None
 10
 11
 12def test_making_a_function_w_return_none():
 13    assert src.functions.w_return_none() is None
 14
 15
 16def test_what_happens_after_functions_return():
 17    assert (
 18        src.functions
 19           .return_leaves_the_function()
 20    ) is None
 21
 22
 23def test_constant_function():
 24    assert src.functions.constant() == 'the same thing'
 25
 26
 27def test_identity_function():
 28    assert src.functions.identity(None) == None
 29    assert src.functions.identity(object) == object
 30
 31
 32def test_why_use_a_function():
 33    def add_x(number):
 34        return 3 + number
 35
 36    assert add_x(0) == 3
 37    assert add_x(1) == 4
 38    assert add_x(2) == 5
 39    assert add_x(3) == 6
 40    assert add_x(4) == 7
 41    assert add_x(5) == 8
 42    assert add_x(6) == 9
 43    assert add_x(7) == 10
 44    assert add_x(8) == 11
 45    assert add_x(9) == 12
 46
 47
 48def test_positional_arguments():
 49    positional_arguments = (
 50        src.functions.positional_arguments
 51    )
 52    first, last = 'first', 'last'
 53
 54    assert (
 55        positional_arguments(first, last)
 56     == (first, last)
 57    )
 58    assert (
 59        positional_arguments(last, first)
 60     == (last, first)
 61    )
 62    assert (
 63        positional_arguments(0, 1)
 64     == (0, 1)
 65    )
 66
 67    a_tuple = (1, 2, 3, 'n')
 68    a_list = [1, 2, 3, 'n']
 69    assert (
 70        positional_arguments(a_tuple, a_list)
 71     == (a_tuple, a_list)
 72    )
 73
 74    keyword_arguments = (
 75        src.functions.keyword_arguments
 76    )
 77    a_set = {1, 2, 3, 'n'}
 78    a_dictionary = {'key': 'value'}
 79    assert (
 80        keyword_arguments(
 81            a_set, a_dictionary,
 82        )
 83     == (a_set, a_dictionary)
 84    )
 85
 86
 87def test_keyword_arguments():
 88    keyword_arguments = (
 89        src.functions.keyword_arguments
 90    )
 91    first, last = 'first', 'last'
 92
 93    assert (
 94        keyword_arguments(
 95            first_input=first, last_input=last,
 96        )
 97     == (first, last)
 98    )
 99    assert (
100        keyword_arguments(
101            last_input=last, first_input=first,
102        )
103     == (first, last)
104    )
105    assert (
106        keyword_arguments(
107            last_input=0, first_input=1,
108        )
109     == (1, 0)
110    )
111
112    a_tuple = (1, 2, 3, 'n')
113    a_list = [1, 2, 3, 'n']
114    assert (
115        keyword_arguments(
116            first_input=a_tuple,
117            last_input=a_list,
118        )
119     == (a_tuple, a_list)
120    )
121
122    positional_arguments = (
123        src.functions.positional_arguments
124    )
125    a_set = {1, 2, 3, 'n'}
126    a_dictionary = {'key': 'value'}
127    assert (
128        positional_arguments(
129            last_input=a_dictionary,
130            first_input=a_set,
131        )
132     == (a_set, a_dictionary)
133    )
134
135
136def test_args_and_kwargs():
137    first, last = 'first', 'last'
138
139    assert (
140        src.functions.args_and_kwargs(
141            first, last_input=last,
142        )
143     == (first, last)
144    )
145
146
147def test_optional_arguments():
148    optional_arguments = (
149        src.functions.optional_arguments
150    )
151
152    first_name, last_name = 'jane', 'doe'
153
154    assert (
155        optional_arguments(
156            first_name,
157        )
158     == (first_name, last_name)
159    )
160
161    first_name, blow = 'joe', 'blow'
162    assert (
163        optional_arguments(
164            first_name, blow
165        )
166     == (first_name, blow)
167    )
168
169    first_name = 'john'
170    assert (
171        optional_arguments(
172            first_input=first_name
173        )
174     == (first_name, last_name)
175    )
176
177    last_name = 'smith'
178    assert (
179        optional_arguments(
180            last_input=last_name,
181            first_input=first_name,
182        )
183     == (first_name, last_name)
184    )
185
186
187def test_unknown_number_of_arguments():
188    unknown_number_of_arguments = (
189        src.functions.unknown_number_of_arguments
190    )
191
192    a_tuple = (0, 1)
193    a_dictionary = {'a': 2, 'b': 3}
194    assert (
195        unknown_number_of_arguments(
196            *a_tuple, **a_dictionary
197        )
198     == (a_tuple, a_dictionary)
199    )
200
201    a_tuple = (0, 1)
202    a_dictionary = {'a': 2, 'b': 3, 'c': 4}
203    assert (
204        unknown_number_of_arguments(
205            *a_tuple, **a_dictionary
206        )
207     == (a_tuple, a_dictionary)
208    )
209
210    a_tuple = (0, 1, 2)
211    a_dictionary = {'a': 3, 'b': 4, 'c': 5}
212    assert (
213        unknown_number_of_arguments(
214            *a_tuple, **a_dictionary
215        )
216     == (a_tuple, a_dictionary)
217    )
218
219    a_tuple = (1, 2, 3, 'n')
220    assert (
221        unknown_number_of_arguments(*a_tuple)
222     == (a_tuple, {})
223    )
224
225    a_dictionary = {'a': 1, 'b': 2, 'c': 3, 'd': 'n'}
226    assert (
227        unknown_number_of_arguments(**a_dictionary)
228     == ((), a_dictionary)
229    )
230
231    assert (
232        unknown_number_of_arguments()
233     == ((), {})
234    )
235
236
237# Exceptions seen
238# AssertionError
239# NameError
240# TypeError
241# SyntaxError
242# ModuleNotFoundError
243# AttributeError

open the project

  • I open a terminal

  • I change directory to the project

    cd functions
    

    the terminal shows I am in the functions folder

    .../pumping_python/functions
    
  • I open test_functions.py

  • I use pytest-watcher to run the tests automatically

    uv run pytest-watcher . --now
    

    the terminal shows

    test_functions.py ............                      [100%]
    
    
    
    =================== 12 passed in X.YZs ===================
    

move w_pass


RED: make it fail


I change the call in the assertion of test_making_a_function_w_pass to use the result of a call to the w_pass function of the functions module in the src folder instead of a call to the w_pass function in test_functions.py

1def test_making_a_function_w_pass():
2    def w_pass():
3        pass
4
5    # assert w_pass() is None
6    assert src.functions.w_pass() is None
7
8
9def test_making_a_function_w_return():

the terminal is my friend, and shows NameError

NameError: name 'src' is not defined

because src is not defined in test_functions.py.


GREEN: make it pass


  • I add an import statement at the top of test_functions.py

    1import src.functions
    2
    3
    4def test_making_a_function_w_pass():
    

    the terminal is my friend, and shows ModuleNotFoundError

    ModuleNotFoundError: No module named 'src'
    

    because there is nothing named src in this project.

  • I add ModuleNotFoundError to the list of Exceptions seen

    230# Exceptions seen
    231# AssertionError
    232# NameError
    233# TypeError
    234# SyntaxError
    235# ModuleNotFoundError
    
  • I open another terminal then make sure I am in the functions folder

  • I use mkdir to make a folder named src

    mkdir src
    
  • I go back to the terminal where the tests are running.

  • I use ctrl/command+s (Windows & Linux/MacOS) on the keyboard in test_functions.py to run the test again and it shows ModuleNotFoundError

    ModuleNotFoundError: No module named 'src.functions'
    

    because there is nothing in the src folder named functions.

  • I go to the second terminal I opened, then use touch to make functions.py in the src folder

    touch src/functions.py
    
  • I go back to the terminal where the tests are running and it shows AttributeError

    AttributeError: module 'src.functions' has no attribute 'w_pass'
    

    because there is nothing in functions.py in the src folder with the name w_pass.

  • I add AttributeError to the list of Exceptions seen, in test_functions.py

    230# Exceptions seen
    231# AssertionError
    232# NameError
    233# TypeError
    234# SyntaxError
    235# ModuleNotFoundError
    236# AttributeError
    
  • I open functions.py from the src folder.

  • I add a copy of the w_pass function to functions.py

    1def w_pass():
    2    pass
    

    the test passes.


REFACTOR: make it better


  • I remove the commented line and the w_pass function from test_making_a_function_w_pass in test_functions.py

    1import src.functions
    2
    3
    4def test_making_a_function_w_pass():
    5    assert src.functions.w_pass() is None
    6
    7
    8def test_making_a_function_w_return():
    

    the test is still green because the call that was made to the w_pass function that was in test_functions.py is now to the w_pass function in functions.py in the src folder. When src.functions.w_pass is called, Python follows this path

    src
    └── functions.py
        └── def w_pass():
                pass
    
  • I add a git commit message in the other terminal

    git commit -am \
    'move w_pass to functions.py'
    

I can write solutions in a different module from the tests.


move w_return


RED: make it fail


  • I go back to the terminal where the tests are running.

  • I change the call in the assertion of test_making_a_function_w_return to use the result of a call to the w_return function of the functions module in the src folder instead of a call to the w_return function in test_functions.py

     8def test_making_a_function_w_return():
     9    def w_return():
    10        return
    11
    12    # assert w_return() is None
    13    assert src.functions.w_return() is None
    14
    15
    16def test_making_a_function_w_return_none():
    

    the terminal is my friend, and shows AttributeError

    AttributeError: module 'src.functions'
                    has no attribute 'w_return'
    

    because functions.py in the src folder does not have anything named w_return in it.


GREEN: make it pass


I add a copy of the w_return function to functions.py

1def w_pass():
2    pass
3
4
5def w_return():
6    return

the test passes.


REFACTOR: make it better


  • I remove the commented line and the w_return function from test_making_a_function_w_return in test_functions.py

     8def test_making_a_function_w_return():
     9    assert src.functions.w_return() is None
    10
    11
    12def test_making_a_function_w_return_none():
    

    the test is still green because the call that was made to the w_return function that was in test_functions.py is now to the w_return function in functions.py in the src folder. When src.functions.w_return is called, Python follows this path

    src
    └── functions.py
        └── def w_return():
                return
    
  • I add a git commit message in the other terminal

    git commit -am \
    'move w_return to functions.py'
    

move w_return_none


RED: make it fail


  • I go back to the terminal where the tests are running.

  • I change the call in the assertion of test_making_a_function_w_return_none to use the result of a call to the w_return_none function of the functions module in the src folder instead of a call to the w_return_none function in test_functions.py

    12def test_making_a_function_w_return_none():
    13    def w_return_none():
    14        return None
    15
    16    # assert w_return_none() is None
    17    assert src.functions.w_return_none() is None
    18
    19
    20def test_what_happens_after_functions_return():
    

    the terminal is my friend, and shows AttributeError

    AttributeError: module 'src.functions'
                    has no attribute 'w_return_none'
    

    because functions.py in the src folder does not have anything named w_return_none in it.


GREEN: make it pass


I add a copy of the w_return_none function to functions.py

 5def w_return():
 6    return
 7
 8
 9def w_return_none():
10    return None

the test passes.


REFACTOR: make it better


  • I remove the commented line and the w_return_none function from test_making_a_function_w_return_none in test_functions.py

    12def test_making_a_function_w_return_none():
    13    assert src.functions.w_return_none() is None
    14
    15
    16def test_what_happens_after_functions_return():
    

    the test is still green because the call that was made to the w_return_none function that was in test_functions.py is now to the w_return_none function in functions.py in the src folder. When src.functions.w_return_none is called, Python follows this path

    src
    └── functions.py
        └── def w_return_none():
                return None
    
  • I add a git commit message in the other terminal

    git commit -am \
    'move w_return_none to functions.py'
    

move return_leaves_the_function


RED: make it fail


  • I go back to the terminal where the tests are running.

  • I change the call in the assertion of test_what_happens_after_functions_return to use the result of a call to return_leaves_the_function of the functions module in the src folder instead of a call to return_leaves_the_function in test_functions.py

    16def test_what_happens_after_functions_return():
    17    def return_leaves_the_function():
    18        return None
    19        return 'only one way for this line to run'
    20
    21    # assert return_leaves_the_function() is None
    22    assert (
    23        src.functions
    24           .return_leaves_the_function()
    25    ) is None
    26
    27
    28def test_constant_function():
    

    the terminal is my friend, and shows AttributeError

    AttributeError: module 'src.functions'
                    has no attribute 'return_leaves_the_function'
    

    because I have not added return_leaves_the_function to functions.py in the src folder.


GREEN: make it pass


I add a copy of return_leaves_the_function to functions.py

 9def w_return_none():
10    return None
11
12
13def return_leaves_the_function():
14    return None
15    return 'only one way for this line to run'

the test passes.


REFACTOR: make it better


  • I remove the commented line and return_leaves_the_function from test_what_happens_after_functions_return in test_functions.py

    16def test_what_happens_after_functions_return():
    17    assert (
    18        src.functions
    19           .return_leaves_the_function()
    20    ) is None
    21
    22
    23def test_constant_function():
    

    the test is still green because the call that was made to return_leaves_the_function that was in test_functions.py is now to return_leaves_the_function in functions.py in the src folder. When src.functions.return_leaves_the_function is called, Python follows this path

    src
    └── functions.py
        └── def return_leaves_the_function():
                return None
    
  • I add a git commit message in the other terminal

    git commit -am \
    'move return_leaves_the_function to functions.py'
    

move constant function


RED: make it fail


  • I go back to the terminal where the tests are running.

  • I change the call in the assertion of test_constant_function to use the result of a call to the constant function of the functions module in the src folder instead of a call to the constant function in test_functions.py

    23def test_constant_function():
    24    def constant():
    25        return 'the same thing'
    26
    27    # assert constant() == 'the same thing'
    28    assert src.functions.constant() == 'the same thing'
    29
    30
    31def test_identity_function():
    

    the terminal is my friend, and shows AttributeError

    AttributeError: module 'src.functions'
                    has no attribute 'constant'
    

    because there is nothing named constant in functions.py in the src folder.


GREEN: make it pass


I add a copy of the constant function to functions.py

13def return_leaves_the_function():
14    return None
15    return 'only one way for this line to run'
16
17
18def constant():
19    return 'the same thing'

the test passes.


REFACTOR: make it better


  • I remove the commented line and the constant function from test_constant_function in test_functions.py

    23def test_constant_function():
    24    assert src.functions.constant() == 'the same thing'
    25
    26
    27def test_identity_function():
    

    the test is still green because the call that was made to the constant function that was in test_functions.py is now to the constant function in functions.py in the src folder. When src.functions.constant is called, Python follows this path

    src
    └── functions.py
        └── def constant():
                return 'the same thing'
    
  • I add a git commit message in the other terminal

    git commit -am \
    'move constant function to functions.py'
    

move identity function


RED: make it fail


  • I go back to the terminal where the tests are running.

  • I change the calls in the assertions of test_identity_function to use the results of calls to the identity function of the functions module in the src folder instead of calls to the identity function in test_functions.py

    27def test_identity_function():
    28    def identity(the_input):
    29        return the_input
    30
    31    # assert identity(None) == None
    32    # assert identity(object) == object
    33    assert src.functions.identity(None) == None
    34    assert src.functions.identity(object) == object
    35
    36
    37def test_why_use_a_function():
    

    the terminal is my friend, and shows AttributeError

    AttributeError: module 'src.functions'
                    has no attribute 'identity'
    

    because identity is not a name in functions.py in the src folder.


GREEN: make it pass


I add a copy of the identity function to functions.py

18def constant():
19    return 'the same thing'
20
21
22def identity(the_input):
23    return the_input

the test passes.


REFACTOR: make it better


  • I remove the commented line and the identity function from test_identity_function in test_functions.py

    23def test_identity_function():
    24    assert src.functions.identity(None) == None
    25    assert src.functions.identity(object) == object
    26
    27
    28def test_why_use_a_function():
    

    the test is still green because the call that was made to the identity function that was in test_functions.py is now to the identity function in functions.py in the src folder. When src.functions.identity is called, Python follows this path

    src
    └── functions.py
        └── def identity(the_input):
                return the_input
    
  • I add a git commit message in the other terminal

    git commit -am \
    'move identity function to functions.py'
    

move positional_arguments function


RED: make it fail


  • I go back to the terminal where the tests are running.

  • I comment out the positional_arguments function in test_functions.py

    32def test_why_use_a_function():
    33    def add_x(number):
    34        return 3 + number
    35
    36    assert add_x(0) == 3
    37    assert add_x(1) == 4
    38    assert add_x(2) == 5
    39    assert add_x(3) == 6
    40    assert add_x(4) == 7
    41    assert add_x(5) == 8
    42    assert add_x(6) == 9
    43    assert add_x(7) == 10
    44    assert add_x(8) == 11
    45    assert add_x(9) == 12
    46
    47
    48# def positional_arguments(first_input, last_input):
    49#     return first_input, last_input
    50
    51
    52def test_positional_arguments():
    

    the terminal is my friend, and shows NameError

    NameError: name 'positional_arguments'
               is not defined
    

GREEN: make it pass



REFACTOR: make it better


  • I remove the commented positional_arguments function from test_functions.py

    32def test_why_use_a_function():
    33    def add_x(number):
    34        return 3 + number
    35
    36    assert add_x(0) == 3
    37    assert add_x(1) == 4
    38    assert add_x(2) == 5
    39    assert add_x(3) == 6
    40    assert add_x(4) == 7
    41    assert add_x(5) == 8
    42    assert add_x(6) == 9
    43    assert add_x(7) == 10
    44    assert add_x(8) == 11
    45    assert add_x(9) == 12
    46
    47
    48def test_positional_arguments():
    
  • I add a git commit message in the other terminal

    git commit -am \
    'move positional_arguments to functions.py'
    

move keyword_arguments function


RED: make it fail


  • I go back to the terminal where the tests are running.

  • I comment out the keyword_arguments function in test_functions.py

    74    a_set = {1, 2, 3, 'n'}
    75    a_dictionary = {'key': 'value'}
    76    assert (
    77        keyword_arguments(
    78            a_set, a_dictionary,
    79        )
    80     == (a_set, a_dictionary)
    81    )
    82
    83
    84# def keyword_arguments(first_input, last_input):
    85#     return first_input, last_input
    86
    87
    88def test_keyword_arguments():
    

    the terminal is my friend, and shows NameError

    NameError: name 'keyword_arguments' is not defined
    

GREEN: make it pass


  • I use a variable to reroute the calls to the keyword_arguments function from test_keyword_arguments to the keyword_arguments function of the functions module in the src folder

     88def test_keyword_arguments():
     89    keyword_arguments = (
     90        src.functions.keyword_arguments
     91    )
     92    first, last = 'first', 'last'
     93
     94    assert (
     95        keyword_arguments(
     96            first_input=first, last_input=last,
     97        )
     98     == (first, last)
     99    )
    100    assert (
    101        keyword_arguments(
    102            last_input=last, first_input=first,
    103        )
    104     == (first, last)
    105    )
    106    assert (
    107        keyword_arguments(
    108            last_input=0, first_input=1,
    109        )
    110     == (1, 0)
    111    )
    112
    113    a_tuple = (1, 2, 3, 'n')
    114    a_list = [1, 2, 3, 'n']
    115    assert (
    116        keyword_arguments(
    117            first_input=a_tuple,
    118            last_input=a_list,
    119        )
    120     == (a_tuple, a_list)
    121    )
    122
    123    positional_arguments = (
    124        src.functions.positional_arguments
    125    )
    126    a_set = {1, 2, 3, 'n'}
    127    a_dictionary = {'key': 'value'}
    128    assert (
    129        positional_arguments(
    130            last_input=a_dictionary,
    131            first_input=a_set,
    132        )
    133     == (a_set, a_dictionary)
    134    )
    135
    136
    137def test_args_and_kwargs():
    

    the terminal is my friend, and shows AttributeError

    AttributeError: module 'src.functions'
                    has no attribute 'keyword_arguments'
    

    because I have not added keyword_arguments to functions.py, yet.

  • I add a copy of the keyword_arguments function to functions.py

    26def positional_arguments(first_input, last_input):
    27    return first_input, last_input
    28
    29
    30def keyword_arguments(first_input, last_input):
    31    return first_input, last_input
    

    the terminal is my friend, and shows NameError

    NameError: name 'keyword_arguments' is not defined
    

    because test_positional_arguments also calls the keyword_arguments function of test_functions.py. Risky business.

  • I use a variable to reroute the calls to the keyword_arguments function from test_positional_arguments to the keyword_arguments function of the functions module in the src folder

    67    a_tuple = (1, 2, 3, 'n')
    68    a_list = [1, 2, 3, 'n']
    69    assert (
    70        positional_arguments(a_tuple, a_list)
    71     == (a_tuple, a_list)
    72    )
    73
    74    keyword_arguments = (
    75        src.functions.keyword_arguments
    76    )
    77    a_set = {1, 2, 3, 'n'}
    78    a_dictionary = {'key': 'value'}
    79    assert (
    80        keyword_arguments(
    81            a_set, a_dictionary,
    82        )
    83     == (a_set, a_dictionary)
    84    )
    85
    86
    87# def keyword_arguments(first_input, last_input):
    88#     return first_input, last_input
    89
    90
    91def test_keyword_arguments():
    

    the test passes because the calls that were made to the keyword_arguments function that was in test_functions.py are now to the keyword_arguments function in functions.py in the src folder.

    When keyword_arguments is called in test_keyword_arguments and test_positional_arguments, Python follows this path

    keyword_arguments = src.functions.keyword_arguments
    
    src
    └── functions.py
        └── def keyword_arguments(first_input, last_input):
                return first_input, last_input
    

REFACTOR: make it better


  • I remove the commented keyword_arguments function from test_functions.py

    48def test_positional_arguments():
    49    positional_arguments = (
    50        src.functions.positional_arguments
    51    )
    52    first, last = 'first', 'last'
    53
    54    assert (
    55        positional_arguments(first, last)
    56     == (first, last)
    57    )
    58    assert (
    59        positional_arguments(last, first)
    60     == (last, first)
    61    )
    62    assert (
    63        positional_arguments(0, 1)
    64     == (0, 1)
    65    )
    66
    67    a_tuple = (1, 2, 3, 'n')
    68    a_list = [1, 2, 3, 'n']
    69    assert (
    70        positional_arguments(a_tuple, a_list)
    71     == (a_tuple, a_list)
    72    )
    73
    74    keyword_arguments = (
    75        src.functions.keyword_arguments
    76    )
    77    a_set = {1, 2, 3, 'n'}
    78    a_dictionary = {'key': 'value'}
    79    assert (
    80        keyword_arguments(
    81            a_set, a_dictionary,
    82        )
    83     == (a_set, a_dictionary)
    84    )
    85
    86
    87def test_keyword_arguments():
    
  • I add a git commit message in the other terminal

    git commit -am \
    'move keyword_arguments to functions.py'
    

move args_and_kwargs function


RED: make it fail


  • I go back to the terminal where the tests are running.

  • I change the call in the assertion of test_args_and_kwargs to use the result of a call to the args_and_kwargs function of the functions module in the src folder instead of the result of a call to the args_and_kwargs function in test_functions.py

    136def test_args_and_kwargs():
    137    def args_and_kwargs(first_input, last_input):
    138        return first_input, last_input
    139
    140    first, last = 'first', 'last'
    141
    142    assert (
    143        # args_and_kwargs(
    144        src.functions.args_and_kwargs(
    145            first, last_input=last,
    146        )
    147     == (first, last)
    148    )
    149
    150
    151def test_optional_arguments():
    

    the terminal is my friend, and shows AttributeError

    AttributeError: module 'src.functions'
                    has no attribute 'args_and_kwargs'
    

    because args_and_kwargs is not a name in functions.py in the src folder.


GREEN: make it pass


I add a copy of the args_and_kwargs function to functions.py

30def keyword_arguments(first_input, last_input):
31    return first_input, last_input
32
33
34def args_and_kwargs(first_input, last_input):
35    return first_input, last_input

the test passes.


REFACTOR: make it better


  • I remove the commented line and the args_and_kwargs function from test_args_and_kwargs in test_functions.py

    136def test_args_and_kwargs():
    137    first, last = 'first', 'last'
    138
    139    assert (
    140        src.functions.args_and_kwargs(
    141            first, last_input=last,
    142        )
    143     == (first, last)
    144    )
    145
    146
    147def test_optional_arguments():
    

    the test is still green because the call that was made to the args_and_kwargs function that was in test_functions.py is now to the args_and_kwargs function in functions.py in the src folder. When src.functions.args_and_kwargs is called, Python follows this path

    src
    └── functions.py
        └── def args_and_kwargs(first_input, last_input):
                return first_input, last_input
    
  • I add a git commit message in the other terminal

    git commit -am \
    'move args_and_kwargs to functions.py'
    

move optional_arguments function


RED: make it fail


  • I go back to the terminal where the tests are running.

  • I comment out the optional_arguments function in test_optional_arguments in test_functions.py

    147def test_optional_arguments():
    148    # def optional_arguments(
    149    #     first_input, last_input='doe',
    150    # ):
    151    #     return first_input, last_input
    152
    153    first_name, last_name = 'jane', 'doe'
    

    the terminal is my friend, and shows NameError

    NameError: name 'optional_arguments'
               is not defined
    

GREEN: make it pass


  • I use a variable to reroute the calls to the optional_arguments function from test_optional_arguments to the optional_arguments function of the functions module in the src folder

    147def test_optional_arguments():
    148    # def optional_arguments(
    149    #     first_input, last_input='doe',
    150    # ):
    151    #     return first_input, last_input
    152    optional_arguments = (
    153        src.functions.optional_arguments
    154    )
    155
    156    first_name, last_name = 'jane', 'doe'
    

    the terminal is my friend, and shows AttributeError

    AttributeError: module 'src.functions'
                    has no attribute 'optional_arguments'.
                    Did you mean: 'positional_arguments'?
    

    because optional_arguments does not exist in functions.py.

  • I add a copy of the optional_arguments function to functions.py

    34def args_and_kwargs(first_input, last_input):
    35    return first_input, last_input
    36
    37
    38def optional_arguments(
    39    first_input, last_input='doe',
    40):
    41    return first_input, last_input
    

    the test passes because the calls that were made to the optional_arguments function that was in test_functions.py are now to the optional_arguments function in functions.py in the src folder.

    When optional_arguments is called in test_optional_arguments, Python follows this path

    optional_arguments = src.functions.optional_arguments
    
    src
    └── functions.py
        └── def optional_arguments(
                first_input, last_input='doe',
            ):
                return first_input, last_input
    

REFACTOR: make it better


  • I remove the commented optional_arguments function from test_optional_arguments in test_functions.py

    147def test_optional_arguments():
    148    optional_arguments = (
    149        src.functions.optional_arguments
    150    )
    151
    152    first_name, last_name = 'jane', 'doe'
    153
    154    assert (
    155        optional_arguments(
    156            first_name,
    157        )
    158     == (first_name, last_name)
    159    )
    160
    161    first_name, blow = 'joe', 'blow'
    162    assert (
    163        optional_arguments(
    164            first_name, blow
    165        )
    166     == (first_name, blow)
    167    )
    168
    169    first_name = 'john'
    170    assert (
    171        optional_arguments(
    172            first_input=first_name
    173        )
    174     == (first_name, last_name)
    175    )
    176
    177    last_name = 'smith'
    178    assert (
    179        optional_arguments(
    180            last_input=last_name,
    181            first_input=first_name,
    182        )
    183     == (first_name, last_name)
    184    )
    185
    186
    187def test_unknown_number_of_arguments():
    
  • I add a git commit message in the other terminal

    git commit -am \
    'move optional_arguments to functions.py'
    

move unknown_number_of_arguments function


RED: make it fail


  • I go back to the terminal where the tests are running.

  • I comment out the unknown_number_of_arguments function in test_unknown_number_of_arguments in test_functions.py

    187def test_unknown_number_of_arguments():
    188    # def unknown_number_of_arguments(
    189    #     *positional_arguments, **keyword_arguments
    190    # ):
    191    #     return positional_arguments, keyword_arguments
    192
    193    a_tuple = (0, 1)
    

    the terminal is my friend, and shows NameError

    NameError: name 'unknown_number_of_arguments'
              is not defined
    

GREEN: make it pass


  • I use a variable to reroute the calls to the unknown_number_of_arguments function from test_unknown_number_of_arguments to the unknown_number_of_arguments function of the functions module in the src folder

    187def test_unknown_number_of_arguments():
    188    # def unknown_number_of_arguments(
    189    #     *positional_arguments, **keyword_arguments
    190    # ):
    191    #     return positional_arguments, keyword_arguments
    192    unknown_number_of_arguments = (
    193        src.functions.unknown_number_of_arguments
    194    )
    195
    196    a_tuple = (0, 1)
    

    the terminal is my friend, and shows AttributeError

    AttributeError: module 'src.functions'
                    has no attribute 'unknown_number_of_arguments'
    

    because I have not yet added unknown_number_of_arguments to functions.py.

  • I add a copy of the unknown_number_of_arguments function to functions.py

    38def optional_arguments(
    39    first_input, last_input='doe',
    40):
    41    return first_input, last_input
    42
    43
    44def unknown_number_of_arguments(
    45    *positional_arguments, **keyword_arguments
    46):
    47    return positional_arguments, keyword_arguments
    

    the test passes because the calls that were made to the unknown_number_of_arguments function that was in test_functions.py are now to the unknown_number_of_arguments function in functions.py in the src folder.

    When unknown_number_of_arguments is called in test_unknown_number_of_arguments, Python follows this path

    unknown_number_of_arguments = (
        src.functions.unknown_number_of_arguments
    )
    
    src
    └── functions.py
        └── def unknown_number_of_arguments(
                *positional_arguments, **keyword_arguments
            ):
                return positional_arguments, keyword_arguments
    

REFACTOR: make it better


  • I remove the commented unknown_number_of_arguments function from test_unknown_number_of_arguments in test_functions.py

    187def test_unknown_number_of_arguments():
    188    unknown_number_of_arguments = (
    189        src.functions.unknown_number_of_arguments
    190    )
    191
    192    a_tuple = (0, 1)
    193    a_dictionary = {'a': 2, 'b': 3}
    194    assert (
    195        unknown_number_of_arguments(
    196            *a_tuple, **a_dictionary
    197        )
    198     == (a_tuple, a_dictionary)
    199    )
    200
    201    a_tuple = (0, 1)
    202    a_dictionary = {'a': 2, 'b': 3, 'c': 4}
    203    assert (
    204        unknown_number_of_arguments(
    205            *a_tuple, **a_dictionary
    206        )
    207     == (a_tuple, a_dictionary)
    208    )
    209
    210    a_tuple = (0, 1, 2)
    211    a_dictionary = {'a': 3, 'b': 4, 'c': 5}
    212    assert (
    213        unknown_number_of_arguments(
    214            *a_tuple, **a_dictionary
    215        )
    216     == (a_tuple, a_dictionary)
    217    )
    218
    219    a_tuple = (1, 2, 3, 'n')
    220    assert (
    221        unknown_number_of_arguments(*a_tuple)
    222     == (a_tuple, {})
    223    )
    224
    225    a_dictionary = {'a': 1, 'b': 2, 'c': 3, 'd': 'n'}
    226    assert (
    227        unknown_number_of_arguments(**a_dictionary)
    228     == ((), a_dictionary)
    229    )
    230
    231    assert (
    232        unknown_number_of_arguments()
    233     == ((), {})
    234    )
    235
    236
    237# Exceptions seen
    238# AssertionError
    239# NameError
    240# TypeError
    241# SyntaxError
    242# ModuleNotFoundError
    243# AttributeError
    
  • I add a git commit message in the other terminal

    git commit -am \
    'move unknown_number_of_arguments to functions.py'
    

test_functions

I can write the functions that make the tests pass (except for test_why_use_a_function which I left alone) without looking at test_functions.py since the solutions are now separate from the tests.


RED: make it fail


  • I go back to the terminal where the tests are running

  • I close test_functions.py

  • I delete all the text in functions.py and the terminal shows 11 failures. I start with the last AttributeError

    FAILED ...::test_making_a_function_w_pass -
        AttributeError: module 'src.functions'
                        has no attribute 'w_pass'
    FAILED ...::test_making_a_function_w_return -
        AttributeError: module 'src.functions'
                        has no attribute 'w_return'
    FAILED ...::test_making_a_function_w_return_none -
        AttributeError: module 'src.functions'
                        has no attribute 'w_return_none'
    FAILED ...::test_what_happens_after_functions_return -
        AttributeError: module 'src.functions'
                        has no attribute 'return_lea...
    FAILED ...::test_constant_function -
        AttributeError: module 'src.functions'
                        has no attribute 'constant'
    FAILED ...::test_identity_function -
        AttributeError: module 'src.functions'
                        has no attribute 'identity'
    FAILED ...::test_positional_arguments -
        AttributeError: module 'src.functions'
                        has no attribute 'positional_arguments'
    FAILED ...::test_keyword_arguments -
        AttributeError: module 'src.functions'
                        has no attribute 'keyword_arguments'
    FAILED ...::test_args_and_kwargs -
        AttributeError: module 'src.functions'
                        has no attribute 'args_and_kwargs'
    FAILED ...::test_optional_arguments -
        AttributeError: module 'src.functions'
                        has no attribute 'optional_arguments'
    FAILED ...::test_unknown_number_of_arguments -
        AttributeError: module 'src.functions'
                        has no attribute 'unknown_nu...
    ============= 11 failed, 1 passed in A.BCs ==============
    

    Can you make the tests pass without looking at how I solve it below? You can come back to compare solutions when you are done or if you get stuck.


GREEN: make it pass


  • Starting with the last Exception, I add a function definition for unknown_number_of_arguments to functions.py in the src folder

    1def unknown_number_of_arguments():
    2    return None
    

    the terminal is my friend, and shows TypeError

    TypeError: unknown_number_of_arguments() got an
               unexpected keyword argument 'a'
    
  • I add a to the parentheses

    1# def unknown_number_of_arguments():
    2def unknown_number_of_arguments(a):
    3    return None
    

    the terminal is my friend, and shows TypeError

    TypeError: unknown_number_of_arguments() got
               multiple values for argument 'a'
    

    which means it got called with a as a keyword argument and since I have it as the first position, Python reads it as two values for the same argument.

  • I try another argument in the parentheses before a

    1# def unknown_number_of_arguments():
    2# def unknown_number_of_arguments(a):
    3def unknown_number_of_arguments(z, a):
    4    return None
    

    the terminal still shows TypeError

  • I use a starred expression to make the function any number of positional_arguments

    1# def unknown_number_of_arguments():
    2# def unknown_number_of_arguments(a):
    3# def unknown_number_of_arguments(z, a):
    4def unknown_number_of_arguments(
    5    *positional, a
    6):
    7    return None
    

    the terminal is my friend, and shows TypeError

    TypeError: unknown_number_of_arguments() got
               an unexpected keyword argument 'b'
    
  • I add b to the parentheses

    1# def unknown_number_of_arguments():
    2# def unknown_number_of_arguments(a):
    3# def unknown_number_of_arguments(z, a):
    4def unknown_number_of_arguments(
    5    # *positional, a
    6    *positional, a, b
    7):
    8    return None
    

    the terminal is my friend, and shows AssertionError

    AssertionError: assert None == ((0, 1), {'a': 2, 'b': 3})
    
  • I copy (ctrl/command+c) the tuple from the right side in the terminal then paste it (ctrl/command+v) to replace the return statement

    1# def unknown_number_of_arguments():
    2# def unknown_number_of_arguments(a):
    3# def unknown_number_of_arguments(z, a):
    4def unknown_number_of_arguments(
    5    # *positional, a
    6    *positional, a, b
    7):
    8    # return None
    9    return ((0, 1), {'a': 2, 'b': 3})
    

    the terminal is my friend, and shows TypeError

    TypeError: unknown_number_of_arguments() got
               an unexpected keyword argument 'c'
    
  • I add c to the parentheses

     1# def unknown_number_of_arguments():
     2# def unknown_number_of_arguments(a):
     3# def unknown_number_of_arguments(z, a):
     4def unknown_number_of_arguments(
     5    # *positional, a
     6    # *positional, a, b
     7    *positional, a, b, c
     8):
     9    # return None
    10    return ((0, 1), {'a': 2, 'b': 3})
    

    the terminal shows TypeError

    TypeError: unknown_number_of_arguments() missing
               1 required keyword-only argument: 'c'.
    

    because something called the function without a value for c

  • I make c an optional argument

     1# def unknown_number_of_arguments():
     2# def unknown_number_of_arguments(a):
     3# def unknown_number_of_arguments(z, a):
     4def unknown_number_of_arguments(
     5    # *positional, a
     6    # *positional, a, b
     7    # *positional, a, b, c
     8    *positional, a, b, c=None
     9):
    10    # return None
    11    return ((0, 1), {'a': 2, 'b': 3})
    

    the terminal shows AssertionError

    AssertionError: assert ((0, 1), {'a': 2, 'b': 3})
                        == ((0, 1), {'a'...': 3, 'c': 4})
    
  • I change the return statement to see the difference between the input and the expected output

     1# def unknown_number_of_arguments():
     2# def unknown_number_of_arguments(a):
     3# def unknown_number_of_arguments(z, a):
     4def unknown_number_of_arguments(
     5    # *positional, a
     6    # *positional, a, b
     7    # *positional, a, b, c
     8    *positional, a, b, c=None
     9):
    10    # return None
    11    # return ((0, 1), {'a': 2, 'b': 3})
    12    return positional, a, b, c
    

    the terminal is my friend, and shows AssertionError

    AssertionError: assert ((0, 1), 2, 3, None)
                        == ((0, 1), {'a': 2, 'b': 3})
    
  • I change the return statement back

     1# def unknown_number_of_arguments():
     2# def unknown_number_of_arguments(a):
     3# def unknown_number_of_arguments(z, a):
     4def unknown_number_of_arguments(
     5    # *positional, a
     6    # *positional, a, b
     7    # *positional, a, b, c
     8    *positional, a, b, c=None
     9):
    10    # return None
    11    return ((0, 1), {'a': 2, 'b': 3})
    12    # return positional, a, b, c
    

    the terminal is my friend, and shows AssertionError

    AssertionError: assert ((0, 1), {'a': 2, 'b': 3})
                        == ((0, 1), {'a'...': 3, 'c': 4})
    

    the dictionaries are different.

  • I use a double starred expression in the parentheses to make the function take any number of keyword_arguments

     1# def unknown_number_of_arguments():
     2# def unknown_number_of_arguments(a):
     3# def unknown_number_of_arguments(z, a):
     4def unknown_number_of_arguments(
     5    # *positional, a
     6    # *positional, a, b
     7    # *positional, a, b, c
     8    # *positional, a, b, c=None
     9    *positional, **keyword
    10):
    11    # return None
    12    return ((0, 1), {'a': 2, 'b': 3})
    13    # return positional, a, b, c
    

    the terminal still shows AssertionError

    AssertionError: assert ((0, 1), {'a': 2, 'b': 3})
                        == ((0, 1), {'a'...': 3, 'c': 4})
    
  • I return the inputs

     1# def unknown_number_of_arguments():
     2# def unknown_number_of_arguments(a):
     3# def unknown_number_of_arguments(z, a):
     4def unknown_number_of_arguments(
     5    # *positional, a
     6    # *positional, a, b
     7    # *positional, a, b, c
     8    # *positional, a, b, c=None
     9    *positional, **keyword
    10):
    11    # return None
    12    # return ((0, 1), {'a': 2, 'b': 3})
    13    # return positional, a, b, c
    14    return positional, keyword
    

    the terminal is my friend, and shows AttributeError

    AttributeError: module 'src.functions'
                    has no attribute 'optional_arguments'
    
  • I add a function definition for optional_arguments

    14    return positional, keyword
    15
    16
    17def optional_arguments():
    18    return None
    

    the terminal is my friend, and shows TypeError

    TypeError: optional_arguments() takes
               0 positional arguments but 1 was given
    
  • I add a name to allow the function take input

    17# def optional_arguments():
    18def optional_arguments(argument):
    19    return None
    

    the terminal is my friend, and shows AssertionError

    AssertionError: assert None == ('jane', 'doe')
    
  • I copy (ctrl/command+c) the value from the terminal and paste it (ctrl/command+v) to replace the return statement

    17# def optional_arguments():
    18def optional_arguments(argument):
    19    # return None
    20    return ('jane', 'doe')
    

    the terminal is my friend, and shows TypeError

    TypeError: optional_arguments() takes
               1 positional argument but 2 were given
    
  • I add another name to the parentheses

    17# def optional_arguments():
    18# def optional_arguments(argument):
    19def optional_arguments(argument, argument_b):
    20    # return None
    21    return ('jane', 'doe')
    

    the terminal shows TypeError

    TypeError: optional_arguments() missing
               1 required positional argument: 'argument_b'
    
  • I make the argument optional

    17# def optional_arguments():
    18# def optional_arguments(argument):
    19# def optional_arguments(argument, argument_b):
    20def optional_arguments(argument, argument_b=None):
    21    # return None
    22    return ('jane', 'doe')
    

    the terminal is my friend, and shows AssertionError

    AssertionError: assert ('jane', 'doe')
                        == ('joe', 'blow')
    
  • I change the return statement of optional_arguments to see the difference between the input and the expected output

    the terminal is my friend, and shows AssertionError

    AssertionError: assert ('jane', None) == ('jane', 'doe')
    

    the function returned a tuple with the first argument and the default value of argument_b.

  • I change the default value to 'doe'

    17# def optional_arguments():
    18# def optional_arguments(argument):
    19# def optional_arguments(argument, argument_b):
    20# def optional_arguments(argument, argument_b=None):
    21def optional_arguments(argument, argument_b='doe'):
    22    # return None
    23    # return ('jane', 'doe')
    24    return argument, argument_b
    

    the terminal is my friend, and shows TypeError

    TypeError: optional_arguments() got
               an unexpected keyword argument 'first_input'
    
  • I add the name in parentheses

    17# def optional_arguments():
    18# def optional_arguments(argument):
    19# def optional_arguments(argument, argument_b):
    20# def optional_arguments(argument, argument_b=None):
    21# def optional_arguments(argument, argument_b='doe'):
    22def optional_arguments(
    23    argument, argument_b='doe', first_input
    24):
    25    # return None
    26    # return ('jane', 'doe')
    27    return argument, argument_b
    

    the terminal is my friend, and shows SyntaxError

    SyntaxError: parameter without a default
         follows parameter with a default
    

    because parameters without default values must come before parameters with default values.

  • I change the order of the arguments

    17# def optional_arguments():
    18# def optional_arguments(argument):
    19# def optional_arguments(argument, argument_b):
    20# def optional_arguments(argument, argument_b=None):
    21# def optional_arguments(argument, argument_b='doe'):
    22def optional_arguments(
    23    # argument, argument_b='doe', first_input
    24    argument, first_input,
    25    argument_b='doe',
    26):
    27    # return None
    28    # return ('jane', 'doe')
    29    return argument, argument_b
    

    the terminal is my friend, and shows TypeError

    TypeError: optional_arguments() missing
               1 required positional argument: 'first_input'
    
  • I make first_input the first argument to see if the problem is the position it is in

    17# def optional_arguments():
    18# def optional_arguments(argument):
    19# def optional_arguments(argument, argument_b):
    20# def optional_arguments(argument, argument_b=None):
    21# def optional_arguments(argument, argument_b='doe'):
    22def optional_arguments(
    23    # argument, argument_b='doe', first_input
    24    # argument, first_input,
    25    first_input, argument,
    26    argument_b='doe',
    27):
    28    # return None
    29    # return ('jane', 'doe')
    30    return argument, argument_b
    

    the terminal is my friend, and shows TypeError

    TypeError: optional_arguments() missing
               1 required positional argument: 'argument'
    
  • I make argument optional

    17# def optional_arguments():
    18# def optional_arguments(argument):
    19# def optional_arguments(argument, argument_b):
    20# def optional_arguments(argument, argument_b=None):
    21# def optional_arguments(argument, argument_b='doe'):
    22def optional_arguments(
    23    # argument, argument_b='doe', first_input
    24    # argument, first_input,
    25    # first_input, argument,
    26    first_input, argument=None,
    27    argument_b='doe',
    28):
    29    # return None
    30    # return ('jane', 'doe')
    31    return argument, argument_b
    

    the terminal is my friend, and shows AssertionError

    AssertionError: assert (None, 'doe')
                        == ('jane', 'doe')
    

    the function returned a tuple with the default value of argument

  • I add first_input to the return statement

    17# def optional_arguments():
    18# def optional_arguments(argument):
    19# def optional_arguments(argument, argument_b):
    20# def optional_arguments(argument, argument_b=None):
    21# def optional_arguments(argument, argument_b='doe'):
    22def optional_arguments(
    23    # argument, argument_b='doe', first_input
    24    # argument, first_input,
    25    # first_input, argument,
    26    first_input, argument=None,
    27    argument_b='doe',
    28):
    29    # return None
    30    # return ('jane', 'doe')
    31    # return argument, argument_b
    32    return first_input, argument, argument_b
    

    the terminal is my friend, and shows AssertionError

    AssertionError: assert ('jane', None, 'doe')
                        == ('jane', 'doe')
    

    it looks like I do not need the argument parameter.

  • I remove argument from the parentheses and return statement

    17# def optional_arguments():
    18# def optional_arguments(argument):
    19# def optional_arguments(argument, argument_b):
    20# def optional_arguments(argument, argument_b=None):
    21# def optional_arguments(argument, argument_b='doe'):
    22def optional_arguments(
    23    # argument, argument_b='doe', first_input
    24    # argument, first_input,
    25    # first_input, argument,
    26    # first_input, argument=None,
    27    first_input,
    28    argument_b='doe',
    29):
    30    # return None
    31    # return ('jane', 'doe')
    32    # return argument, argument_b
    33    # return first_input, argument, argument_b
    34    return first_input, argument_b
    

    the terminal is my friend, and shows TypeError

    TypeError: optional_arguments() got
               an unexpected keyword argument 'last_input'.
               Did you mean 'first_input'?
    
  • I add the name in the parentheses

    17# def optional_arguments():
    18# def optional_arguments(argument):
    19# def optional_arguments(argument, argument_b):
    20# def optional_arguments(argument, argument_b=None):
    21# def optional_arguments(argument, argument_b='doe'):
    22def optional_arguments(
    23    # argument, argument_b='doe', first_input
    24    # argument, first_input,
    25    # first_input, argument,
    26    # first_input, argument=None,
    27    first_input,
    28    # argument_b='doe',
    29    argument_b='doe', last_input,
    30):
    31    # return None
    32    # return ('jane', 'doe')
    33    # return argument, argument_b
    34    # return first_input, argument, argument_b
    35    return first_input, argument_b
    

    the terminal is my friend, and shows SyntaxError

    SyntaxError: parameter without a default
         follows parameter with a default
    

    because parameters without default values must come before parameters with default values.

  • I give the argument a default value

    17# def optional_arguments():
    18# def optional_arguments(argument):
    19# def optional_arguments(argument, argument_b):
    20# def optional_arguments(argument, argument_b=None):
    21# def optional_arguments(argument, argument_b='doe'):
    22def optional_arguments(
    23    # argument, argument_b='doe', first_input
    24    # argument, first_input,
    25    # first_input, argument,
    26    # first_input, argument=None,
    27    first_input,
    28    # argument_b='doe',
    29    # argument_b='doe', last_input,
    30    argument_b='doe', last_input=None,
    31):
    32    # return None
    33    # return ('jane', 'doe')
    34    # return argument, argument_b
    35    # return first_input, argument, argument_b
    36    return first_input, argument_b
    

    the terminal is my friend, and shows AssertionError

    AssertionError: assert ('john', 'doe')
                        == ('john', 'smith')
    

    the last names are different.

  • I add last_input to the return statement

    17# def optional_arguments():
    18# def optional_arguments(argument):
    19# def optional_arguments(argument, argument_b):
    20# def optional_arguments(argument, argument_b=None):
    21# def optional_arguments(argument, argument_b='doe'):
    22def optional_arguments(
    23    # argument, argument_b='doe', first_input
    24    # argument, first_input,
    25    # first_input, argument,
    26    # first_input, argument=None,
    27    first_input,
    28    # argument_b='doe',
    29    # argument_b='doe', last_input,
    30    argument_b='doe', last_input=None,
    31):
    32    # return None
    33    # return ('jane', 'doe')
    34    # return argument, argument_b
    35    # return first_input, argument, argument_b
    36    # return first_input, argument_b
    37    return first_input, argument_b, last_input
    

    the terminal is my friend, and shows AssertionError

    AssertionError: assert ('jane', 'doe', None)
                        == ('jane', 'doe')
    

    I only need two inputs.

  • I remove argument_b from the parentheses and return statement, then change the default value of last_input to 'doe'

    17# def optional_arguments():
    18# def optional_arguments(argument):
    19# def optional_arguments(argument, argument_b):
    20# def optional_arguments(argument, argument_b=None):
    21# def optional_arguments(argument, argument_b='doe'):
    22def optional_arguments(
    23    # argument, argument_b='doe', first_input
    24    # argument, first_input,
    25    # first_input, argument,
    26    # first_input, argument=None,
    27    first_input,
    28    # argument_b='doe',
    29    # argument_b='doe', last_input,
    30    # argument_b='doe', last_input=None,
    31    last_input='doe',
    32):
    33    # return None
    34    # return ('jane', 'doe')
    35    # return argument, argument_b
    36    # return first_input, argument, argument_b
    37    # return first_input, argument_b
    38    # return first_input, argument_b, last_input
    39    return first_input, last_input
    

    the terminal is my friend, and shows AttributeError

    AttributeError: module 'src.functions'
                    has no attribute 'args_and_kwargs'
    
  • I add a function definition for args_and_kwargs

    39    return first_input, last_input
    40
    41
    42def args_and_kwargs():
    43    return None
    

    the terminal is my friend, and shows TypeError

    TypeError: args_and_kwargs() got
               an unexpected keyword argument 'last_input'
    
  • I add last_input in parentheses

    42# def args_and_kwargs():
    43def args_and_kwargs(last_input):
    44    return None
    

    the terminal is my friend, and shows TypeError

    TypeError: args_and_kwargs() got
               multiple values for argument 'last_input'
    

    which means it got called with last_input as a positional and keyword argument. Since I have it as the first position, Python reads it as two values for the same argument.

  • I add another name before last_input

    42# def args_and_kwargs():
    43# def args_and_kwargs(last_input):
    44def args_and_kwargs(argument, last_input):
    45    return None
    

    the terminal is my friend, and shows AssertionError

    AssertionError: assert None == ('first', 'last')
    
  • I return the inputs

    42# def args_and_kwargs():
    43# def args_and_kwargs(last_input):
    44def args_and_kwargs(argument, last_input):
    45    # return None
    46    return argument, last_input
    

    the terminal is my friend, and shows AttributeError

    AttributeError: module 'src.functions'
                    has no attribute 'keyword_arguments'
    
  • I add a function definition for keyword_arguments

    46    return argument, last_input
    47
    48
    49def keyword_arguments():
    50    return None
    

    the terminal is my friend, and shows TypeError

    TypeError: keyword_arguments() got
               an unexpected keyword argument 'first_input'
    
  • I add first_input to the parentheses

    49# def keyword_arguments():
    50def keyword_arguments(first_input):
    51    return None
    

    the terminal is my friend, and shows TypeError

    TypeError: keyword_arguments() got
               an unexpected keyword argument 'last_input'.
               Did you mean 'first_input'?
    
  • I add last_input to the parentheses

    49# def keyword_arguments():
    50# def keyword_arguments(first_input):
    51def keyword_arguments(first_input, last_input):
    52    return None
    

    the terminal is my friend, and shows AssertionError

    AssertionError: assert None == ('first', 'last')0
    
  • I copy and paste the tuple from the terminal as the return statement

    49# def keyword_arguments():
    50# def keyword_arguments(first_input):
    51def keyword_arguments(first_input, last_input):
    52    # return None
    53    return ('first', 'last')
    

    the terminal is my friend, and shows AssertionError

    AssertionError: assert ('first', 'last') == (1, 0)
    
  • I change the return statement to see the difference between the inputs and the output

    49# def keyword_arguments():
    50# def keyword_arguments(first_input):
    51def keyword_arguments(first_input, last_input):
    52    # return None
    53    # return ('first', 'last')
    54    return first_input, last_input
    

    the terminal is my friend, and shows AttributeError

    AttributeError: module 'src.functions'
                    has no attribute 'positional_arguments'.
                    Did you mean: 'optional_arguments'?
    
  • I add a function definition for positional_arguments

    54    return first_input, last_input
    55
    56
    57def positional_arguments():
    58    return None
    

    the terminal is my friend, and shows TypeError

    TypeError: positional_arguments() got
               an unexpected keyword argument 'last_input'
    
  • I add the name to the parentheses in the definition

    57# def positional_arguments():
    58def positional_arguments(last_input):
    59    return None
    

    the terminal is my friend, and shows TypeError

    TypeError: positional_arguments() got
               an unexpected keyword argument 'first_input'.
               Did you mean 'last_input'?
    
  • I add last_input to the parentheses

    57# def positional_arguments():
    58# def positional_arguments(last_input):
    59def positional_arguments(last_input, first_input):
    60    return None
    

    the terminal is my friend, and shows AssertionError

    AssertionError: assert None
        == ({3, 1, 2, 'n'}, {'key': 'value'})
    
  • I copy (ctrl/command+c) and paste (ctrl/command+v) the value from the terminal to change the return statement

    57# def positional_arguments():
    58# def positional_arguments(last_input):
    59def positional_arguments(last_input, first_input):
    60    # return None
    61    return ({3, 1, 2, 'n'}, {'key': 'value'})
    

    the terminal is my friend, and shows AssertionError

    AssertionError:
        assert ({1, 2, 3, 'n...ey': 'value'})
            == ('first', 'last')
    
  • I change the return statement to see the difference between the inputs and output

    57# def positional_arguments():
    58# def positional_arguments(last_input):
    59def positional_arguments(last_input, first_input):
    60    # return None
    61    # return ({3, 1, 2, 'n'}, {'key': 'value'})
    62    return last_input, first_input
    

    the terminal is my friend, and shows AssertionError

    AssertionError:
        assert ({'key': 'val...3, 1, 'n', 2})
            == ({3, 1, 'n', ...ey': 'value'})
    
    • The order is reversed.

    • I want better error messages.

  • I change the order of the inputs in the the return statement

    57# def positional_arguments():
    58# def positional_arguments(last_input):
    59def positional_arguments(last_input, first_input):
    60    # return None
    61    # return ({3, 1, 2, 'n'}, {'key': 'value'})
    62    # return last_input, first_input
    63    return first_input, last_input
    

    the terminal is my friend, and shows AssertionError

    AssertionError: assert ('last', 'first')
                        == ('first', 'last')
    

    the order is reversed.

  • I change the order of inputs in the parentheses

    57# def positional_arguments():
    58# def positional_arguments(last_input):
    59# def positional_arguments(last_input, first_input):
    60def positional_arguments(first_input, last_input):
    61    # return None
    62    # return ({3, 1, 2, 'n'}, {'key': 'value'})
    63    # return last_input, first_input
    64    return first_input, last_input
    

    the terminal is my friend, and shows AttributeError

    AttributeError: module 'src.functions'
                    has no attribute 'identity'
    
  • I add a function definition for identity

    64    return first_input, last_input
    65
    66
    67def identity():
    68    return None
    

    the terminal is my friend, and shows TypeError

    TypeError: identity() takes
               0 positional arguments but 1 was given
    
  • I add a name to the function definition to make it take input

    67# def identity():
    68def identity(argument):
    69    return None
    

    the terminal is my friend, and shows AssertionError

    AssertionError: assert None == object
    
  • I change the return statement to give the test what it wants

    67# def identity():
    68def identity(argument):
    69    # return None
    70    return object
    

    the terminal is my friend, and shows AssertionError

    AssertionError: assert <class 'object'> == None
    
  • I return the input

    67# def identity():
    68def identity(argument):
    69    # return None
    70    # return object
    71    return argument
    

    the terminal is my friend, and shows AttributeError

    AttributeError: module 'src.functions'
                    has no attribute 'constant'
    
  • I add a function definition for constant

    71    return argument
    72
    73
    74def constant():
    75    return None
    

    the terminal is my friend, and shows AssertionError

    AssertionError: assert None == 'the same thing'
    
  • I copy and paste the value from the terminal as the return statement

    74def constant():
    75    # return None
    76    return 'the same thing'
    

    the terminal is my friend, and shows AttributeError

    AttributeError: module 'src.functions'
                    has no attribute 'return_leaves_the_function'
    
  • I add a function definition for return_leaves_the_function

    76    return 'the same thing'
    77
    78
    79def return_leaves_the_function():
    80    return None
    

    the terminal is my friend, and shows AttributeError

    AttributeError: module 'src.functions'
                    has no attribute 'w_return_none'
    
  • I add a function definition for w_return_none

    79def return_leaves_the_function():
    80    return None
    81
    82
    83def w_return_none():
    84    return None
    

    the terminal is my friend, and shows AttributeError

    AttributeError: module 'src.functions'
                    has no attribute 'w_return'
    
  • I add a function definition for w_return

    83def w_return_none():
    84    return None
    85
    86
    87def w_return():
    88    return None
    

    the terminal is my friend, and shows AttributeError

    AttributeError: module 'src.functions'
                    has no attribute 'w_pass'
    
  • I add a function definition for w_pass

    87def w_return():
    88    return None
    89
    90
    91def w_pass():
    92    return None
    

    all tests are passing!


REFACTOR: make it better


  • I remove the commented lines

  • I add a git commit message in the other terminal

    git commit -am 'test functions'
    

close the project

  • I close functions.py

  • I click in the terminal where the tests are running

  • I use q on the keyboard to leave the tests. The terminal goes back to the command line.

  • I change directory to the parent of functions

    cd ..
    

    the terminal shows

    .../pumping_python
    

    I am back in the pumping_python directory.


review

I can write solutions in a different module from the tests.


code from the chapter

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


what is next?

So far, you know

Would you like to test using a class to remove repetition of inputs from functions?


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.