separate and equal functions

All the functions in the functions project were written in tests/test_functions.py. I want to move them to the functions module in the src folder so that I can keep the tests and solutions separate.


preview

I have these tests by the end of the chapter

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

  • 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 to src

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 tests/test_functions.py

 9def test_making_a_function_w_pass():
10    def w_pass():
11        pass
12
13    # assert_is_none(w_pass())
14    assert_is_none(src.functions.w_pass())
15
16
17def 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 tests/test_functions.py.


GREEN: make it pass


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

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

    the terminal is my friend, and shows AttributeError

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

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

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

    260# Exceptions seen
    261# AssertionError
    262# NameError
    263# TypeError
    264# SyntaxError
    265# AttributeError
    
  • I open __init__.py from the functions folder in the src folder.

  • I remove all the text then add a copy of the w_pass function to src/functions/__init__.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 tests/test_functions.py

    12def test_making_a_function_w_pass():
    13    assert_is_none(src.functions.w_pass())
    14
    15
    16def 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 tests/test_functions.py is now to the w_pass function in __init__.py in the functions folder in the src folder.

    Python follows this path when src.functions.w_pass is called

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

    git commit -am 'move w_pass to src'
    

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


move w_return

RED: make it fail



GREEN: make it pass


I add a copy of the w_return function to src/functions/__init__.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 tests/test_functions.py

    16def test_making_a_function_w_return():
    17    assert_is_none(src.functions.w_return())
    18
    19
    20def 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 tests/test_functions.py is now to the w_return function in __init__.py in the functions folder in the src folder.

    Python follows this path when src.functions.w_return is called

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

    git commit -am 'move w_return to src'
    

move w_return_none

RED: make it fail



GREEN: make it pass


I add a copy of the w_return_none function to src/functions/__init__.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 tests/test_functions.py

    20def test_making_a_function_w_return_none():
    21    assert_is_none(src.functions.w_return_none())
    22
    23
    24def 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 tests/test_functions.py is now to the w_return_none function in __init__.py in the functions folder in the src folder.

    Python follows this path when src.functions.w_return_none is called

    src.functions.w_return_none
    src/
    └── functions/
        └── __init__.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 src'
    

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 tests/test_functions.py

    24def test_what_happens_after_functions_return():
    25    def return_leaves_the_function():
    26        return None
    27        return 'only one way for this line to run'
    28
    29    # assert_is_none(return_leaves_the_function())
    30    assert_is_none(
    31        src.functions.return_leaves_the_function()
    32    )
    33
    34
    35def 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 src/functions/__init__.py.


GREEN: make it pass


I add a copy of return_leaves_the_function to src/functions/__init__.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 tests/test_functions.py

    24def test_what_happens_after_functions_return():
    25    assert_is_none(
    26        src.functions.return_leaves_the_function()
    27    )
    28
    29
    30def test_constant_function():
    

    the test is still green because the call that was made to return_leaves_the_function that was in tests/test_functions.py is now to return_leaves_the_function in __init__.py in the functions folder in the src folder.

    Python follows this path when src.functions.return_leaves_the_function is called

    src.functions.return_leaves_the_function
    src/
    └── functions/
        └── __init__.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 src'
    

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 tests/test_functions.py

    30def test_constant_function():
    31    def constant():
    32        return 'the same thing'
    33
    34    # assert_equal(constant(), 'the same thing')
    35    assert_equal(
    36        src.functions.constant(), 'the same thing'
    37    )
    38
    39
    40def 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 __init__.py in the functions folder in the src folder.


GREEN: make it pass


I add a copy of the constant function to src/functions/__init__.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 tests/test_functions.py

    30def test_constant_function():
    31    assert_equal(
    32        src.functions.constant(), 'the same thing'
    33    )
    34
    35
    36def test_identity_function():
    

    the test is still green because the call that was made to the constant function that was in tests/test_functions.py is now to the constant function in __init__.py in the functions folder in the src folder.

    Python follows this path when src.functions.constant is called

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

    git commit -am \
    'move constant function to src'
    

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 tests/test_functions.py

    36def test_identity_function():
    37    def identity(the_input):
    38        return the_input
    39
    40    # assert_is_none(identity(None))
    41    # assert_equal(identity(object), object)
    42    assert_is_none(src.functions.identity(None))
    43    assert_equal(
    44        src.functions.identity(object), object
    45    )
    46
    47
    48def 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 __init__.py in the functions folder in the src folder.


GREEN: make it pass


I add a copy of the identity function to src/functions/__init__.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 tests/test_functions.py

    36def test_identity_function():
    37    assert_is_none(src.functions.identity(None))
    38    assert_equal(
    39        src.functions.identity(object), object
    40    )
    41
    42
    43def test_why_use_a_function():
    

    the test is still green because the call that was made to the identity function that was in tests/test_functions.py is now to the identity function in __init__.py in the functions folder in the src folder.

    Python follows this path when src.functions.identity is called

    src.functions.identity
    src/
    └── functions/
        └── __init__.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 src'
    

move positional_arguments

RED: make it fail


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

  • I comment out the positional_arguments function in tests/test_functions.py

    43def test_why_use_a_function():
    44    def add_x(number):
    45        return 3 + number
    46
    47    assert_equal(add_x(0), 3)
    48    assert_equal(add_x(1), 4)
    49    assert_equal(add_x(2), 5)
    50    assert_equal(add_x(3), 6)
    51    assert_equal(add_x(4), 7)
    52    assert_equal(add_x(5), 8)
    53    assert_equal(add_x(6), 9)
    54    assert_equal(add_x(7), 10)
    55    assert_equal(add_x(8), 11)
    56    assert_equal(add_x(9), 12)
    57
    58
    59# def positional_arguments(first_input, last_input):
    60#     return first_input, last_input
    61
    62
    63def 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 tests/test_functions.py

    43def test_why_use_a_function():
    44    def add_x(number):
    45        return 3 + number
    46
    47    assert_equal(add_x(0), 3)
    48    assert_equal(add_x(1), 4)
    49    assert_equal(add_x(2), 5)
    50    assert_equal(add_x(3), 6)
    51    assert_equal(add_x(4), 7)
    52    assert_equal(add_x(5), 8)
    53    assert_equal(add_x(6), 9)
    54    assert_equal(add_x(7), 10)
    55    assert_equal(add_x(8), 11)
    56    assert_equal(add_x(9), 12)
    57
    58
    59def test_positional_arguments():
    
  • I add a git commit message in the other terminal

    git commit -am \
    'move positional_arguments to src'
    

move keyword_arguments

RED: make it fail


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

  • I comment out the keyword_arguments function in tests/test_functions.py

    85    a_set = {0, 1, 2, 'n'}
    86    a_dictionary = {'key': 'value'}
    87    assert_equal(
    88        keyword_arguments(
    89            a_set, a_dictionary,
    90        ),
    91        (a_set, a_dictionary)
    92    )
    93
    94
    95# def keyword_arguments(first_input, last_input):
    96#     return first_input, last_input
    97
    98
    99def test_keyword_arguments():
    

    the terminal is my friend, and shows NameError

    NameError: name 'keyword_arguments' is not defined
    

GREEN: make it pass



REFACTOR: make it better


  • I remove the commented lines from test_positional_arguments and test_keyword_arguments and the commented keyword_arguments function from tests/test_functions.py

    59def test_positional_arguments():
    60    positional_arguments = (
    61        src.functions.positional_arguments
    62    )
    63    first, last = 'first', 'last'
    64
    65    assert_equal(
    66        positional_arguments(first, last),
    67        (first, last)
    68    )
    69    assert_equal(
    70        positional_arguments(last, first),
    71        (last, first)
    72    )
    73
    74    assert_equal(
    75        positional_arguments(0, 1), (0, 1)
    76    )
    77
    78    a_tuple = (0, 1, 2, 'n')
    79    a_list = [0, 1, 2, 'n']
    80    assert_equal(
    81        positional_arguments(a_tuple, a_list),
    82        (a_tuple, a_list)
    83    )
    84
    85    a_set = {0, 1, 2, 'n'}
    86    a_dictionary = {'key': 'value'}
    87    assert_equal(
    88        src.functions.keyword_arguments(
    89            a_set, a_dictionary,
    90        ),
    91        (a_set, a_dictionary)
    92    )
    
     95def test_keyword_arguments():
     96    keyword_arguments = (
     97        src.functions.keyword_arguments
     98    )
     99    first, last = 'first', 'last'
    100
    101    assert_equal(
    102        keyword_arguments(
    103            first_input=first, last_input=last,
    104        ),
    105        (first, last)
    106    )
    107    assert_equal(
    108        keyword_arguments(
    109            last_input=last, first_input=first,
    110        ),
    111        (first, last)
    112    )
    113
    114    assert_equal(
    115        keyword_arguments(
    116            last_input=0, first_input=1,
    117        ),
    118        (1, 0)
    119    )
    120
    121    a_tuple = (0, 1, 2, 'n')
    122    a_list = [0, 1, 2, 'n']
    123    assert_equal(
    124        keyword_arguments(
    125            first_input=a_tuple,
    126            last_input=a_list,
    127        ),
    128        (a_tuple, a_list)
    129    )
    130
    131    a_set = {0, 1, 2, 'n'}
    132    a_dictionary = {'key': 'value'}
    133    assert_equal(
    134        src.functions.positional_arguments(
    135            last_input=a_dictionary,
    136            first_input=a_set,
    137        ),
    138        (a_set, a_dictionary)
    139    )
    140
    141
    142def test_args_and_kwargs():
    
  • I add a git commit message in the other terminal

    git commit -am \
    'move keyword_arguments to src'
    

move args_and_kwargs

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 tests/test_functions.py

    142def test_args_and_kwargs():
    143    def args_and_kwargs(first_input, last_input):
    144        return first_input, last_input
    145
    146    first, last = 'first', 'last'
    147
    148    assert_equal(
    149        # args_and_kwargs(
    150        src.functions.args_and_kwargs(
    151            first, last_input=last
    152        ),
    153        (first, last)
    154    )
    155
    156
    157def 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 __init__.py in the functions folder in the src folder.


GREEN: make it pass


I add a copy of the args_and_kwargs function to src/functions/__init__.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 tests/test_functions.py

    142def test_args_and_kwargs():
    143    first, last = 'first', 'last'
    144
    145    assert_equal(
    146        src.functions.args_and_kwargs(
    147            first, last_input=last
    148        ),
    149        (first, last)
    150    )
    151
    152
    153def test_optional_arguments():
    

    the test is still green because the call that was made to the args_and_kwargs function that was in tests/test_functions.py is now to the args_and_kwargs function in __init__.py in the functions folder in the src folder.

    Python follows this path when src.functions.args_and_kwargs is called

    src.functions.args_and_kwargs
    src/
    └── functions/
        └── __init__.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 src'
    

move optional_arguments

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 tests/test_functions.py

    153def test_optional_arguments():
    154    # def optional_arguments(
    155    #     first_input, last_input='doe',
    156    # ):
    157    #     return first_input, last_input
    158
    159    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

    153def test_optional_arguments():
    154    # def optional_arguments(
    155    #     first_input, last_input='doe',
    156    # ):
    157    #     return first_input, last_input
    158    optional_arguments = (
    159        src.functions.optional_arguments
    160    )
    161
    162    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 src/functions/__init__.py.

  • I add a copy of the optional_arguments function to src/functions/__init__.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 tests/test_functions.py are now made to the optional_arguments function in __init__.py in the functions folder in the src folder.

    Python follows this path when optional_arguments is called in test_optional_arguments

    optional_arguments = src.functions.optional_arguments
    
    src/
    └── functions/
        └── __init__.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 tests/test_functions.py

    153def test_optional_arguments():
    154    optional_arguments = (
    155        src.functions.optional_arguments
    156    )
    157
    158    first_name, last_name = 'jane', 'doe'
    
  • I add a git commit message in the other terminal

    git commit -am \
    'move optional_arguments to src'
    

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 tests/test_functions.py

    192def test_unknown_number_of_arguments():
    193    # def unknown_number_of_arguments(
    194    #     *positional_arguments, **keyword_arguments
    195    # ):
    196    #     return positional_arguments, keyword_arguments
    197
    198    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

    192def test_unknown_number_of_arguments():
    193    # def unknown_number_of_arguments(
    194    #     *positional_arguments, **keyword_arguments
    195    # ):
    196    #     return positional_arguments, keyword_arguments
    197    unknown_number_of_arguments = (
    198        src.functions.unknown_number_of_arguments
    199    )
    200
    201    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 src/functions/__init__.py.

  • I add a copy of the unknown_number_of_arguments function to src/functions/__init__.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 tests/test_functions.py are now made to the unknown_number_of_arguments function in __init__.py in the functions folder in the src folder.

    Python follows this path when unknown_number_of_arguments is called in test_unknown_number_of_arguments

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

REFACTOR: make it better



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 tests/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 tests/test_functions.py

  • I delete all the text in src/functions/__init__.py and the terminal shows 11 failures.

    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


  • I start with the last AttributeError and add a function definition for unknown_number_of_arguments to src/functions/__init__.py

    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 take 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'
    

    because the call uses a name (b) that is not in the parentheses of its definition.

  • 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'
    

    because the call uses a name (c) that is not in the parentheses of its definition.

  • 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

    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')
    23    return argument, argument_b
    

    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'
    

    because the call uses a name (first_input) that is not in the parentheses of its definition.

  • 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'?
    

    because the call uses a name (last_input) that is not in the parentheses of its definition.

  • 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'
    

    because the call uses a name (first_input) that is not in the parentheses of its definition.

  • 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')
    
  • 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'
    

    because the call uses a name (last_input) that is …

  • 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'?
    

    because …

  • I add first_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 the 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'})
    
  • I change the order of the inputs in 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

     1def unknown_number_of_arguments(
     2        *positional, **keyword,
     3    ):
     4    return positional, keyword
     5
     6
     7def optional_arguments(
     8        first_input, last_input='doe',
     9    ):
    10    return first_input, last_input
    
    13def args_and_kwargs(argument, last_input):
    14    return argument, last_input
    15
    16
    17def keyword_arguments(first_input, last_input):
    18    return first_input, last_input
    19
    20
    21def positional_arguments(first_input, last_input):
    22    return first_input, last_input
    
    25def identity(argument):
    26    return argument
    27
    28
    29def constant():
    30    return 'the same thing'
    31
    32
    33def return_leaves_the_function():
    34    return None
    
    37def w_return_none():
    38    return None
    39
    40
    41def w_return():
    42    return None
    43
    44
    45def w_pass():
    46    return None
    
  • I add a git commit message in the other terminal

    git commit -am 'test functions'
    

close the project

  • I close src/functions/__init__.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?

would you like to separate the tests from the solution in the telephone project?


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.