Seemingly wrong error message in the Plane Tickets exercise on the Python track

Trying to solve the “Ticket codes” part of the exercise with the following code:

def generate_codes(seat_numbers, flight_id):

while len(seat_numbers) > 0:
    code = seat_numbers.pop(0) + flight_id
    code += "0" * (12 - len(code))
    yield code

I get the following error message, but to me the outputs seem pretty identical:

[‘12AKL1022000’, ‘38BKL1022000’, ‘69CKL1022000’, ‘102BKL102200’] : Called generate_codes(, KL1022). The function returned [‘12AKL1022000’, ‘38BKL1022000’, ‘69CKL1022000’, ‘102BKL102200’], but the tests expected [‘12AKL1022000’, ‘38BKL1022000’, ‘69CKL1022000’, ‘102BKL102200’] when generating ticket numbers.

Hi @AndreyKartsev :wave:

Welcome to the Exercism forums! Thanks for raising this issue.

:thinking: It does look that way.

Because these are generators, I’ve written the tests to save results in variables, and it appears to be screwing with the detailed error message.

However, when I tested your function in the UI, this was the full error message:

AssertionError: Lists differ: [] != ['12AKL1022000', '38BKL1022000', '69CKL1022000', '102BKL102200']

Second list contains 4 additional elements.
First extra element 0:
'12AKL1022000'

- []
+ ['12AKL1022000', '38BKL1022000', '69CKL1022000', '102BKL102200'] : 

Called generate_codes([], KL1022). 
The function returned ['12AKL1022000', '38BKL1022000', '69CKL1022000', '102BKL102200'], 
but the tests expected ['12AKL1022000', '38BKL1022000', '69CKL1022000', '102BKL102200'] when generating ticket numbers.

The issue here is your while-loop. It is pop-ing off seats from the passed-in seat-number list to create the codes. It is then modifying and yielding the codes out. But when the tests then unpack that generator to compare actual vs expected, there is no longer any seat_numbers list to run

code = seat_numbers.pop(0) + flight_id` 

and

code += "0" * (12 - len(code))` 

If you look at the first error message being tossed by pytest, you can see the issue:

AssertionError: Lists differ: [] != ['12AKL1022000', '38BKL1022000', '69CKL1022000', '102BKL102200']

In effect, the test code is being handed the generator generate_codes([], KL1022) – a list with no seat numbers. Since at this point there aren’t any seat numbers to modify (they were all popped off), so there aren’t any seat numbers returned.


The way out of the situation is to use a for-loop and not (destructively) mutate the passed-in data:

    for seat in seat_numbers:
        code = seat + flight_id
        code += "0" * (12 - len(code))
        yield code

This leaves seat_numbers intact, so that when the generator is unpacked, there are seat numbers to operate on, and codes can be generated.

Meanwhile, I will need to think about how to modify the test to give better feedback, and rewrite the tests (if I can) to avoid this scenario. :slightly_smiling_face:

4 Likes

What a nice place Exercism is! :-)

2 Likes

Bethany, thank you so much for looking into this! I had assumed that the issue was popping items from the list, so I redid the exercise without that and got it working, but nice to get a detailed explanation for what was happening!

1 Like

You will love this. :wink: Sometimes, I can be really idiotic.

Here is the buggy test (scroll sideways for the comments):

    def test_generate_codes(self):
        test_data = [(["12A", "38B", "69C", "102B"],"KL1022"),
                      (["22C", "88B", "33A", "44B"], "DL1002")]
        result_data = [['12AKL1022000', '38BKL1022000', '69CKL1022000', '102BKL102200'],
                       ['22CDL1002000', '88BDL1002000', '33ADL1002000', '44BDL1002000']]

        for variant, ((seat_numbers, flight_id), expected) in enumerate(zip(test_data, result_data), start=1):
            with self.subTest(f"variation #{variant}", seat_numbbers=seat_numbers,
                              flight_id=flight_id, expected=expected):

                actual_result = list(generate_codes(seat_numbers, flight_id)) #<--note this line. `list()` is unpacking the returned generator.
                error_message = (f'Called generate_codes({seat_numbers}, {flight_id}). '
                                 f'The function returned {actual_result}, but the tests '
                                 f'expected {expected} when generating ticket numbers.')

                self.assertEqual(list(generate_codes(seat_numbers, flight_id)), expected, msg=error_message) #<-- considering that I already unpacked the generator and stashed it in a variable, why would I call it all over again?!!?  ***This is the bug.***

:woman_facepalming:

Fixed & merged in PR 3792. Your original code with list.pop() will work now without a pytest freakout. :smile:

4 Likes