Surprise from Python on new years eve, 2016

Alright, so another usual night and I was trying to figure out a strange behavior with a Python class I was working. Looks like I had OOP lectures only in C++, and things are little bit different here. So here you go!

I had this strange class method here described as:


class BenchmarkResults(object):

    @classmethod
    def _generate_db_entries(cls, tests, skip_aggregated=False, parent=None, test_table = []):
         # This function recursively calls _generate_db_entries with
         # test_table param set to a list 

and this was processing in a JSON file from one of my POST API and doing some crazy stuff. The API calls this class like

class BotReportView(APIView):
    def post(self, request, format=None):
        test_data_results = BenchmarkResults(json.load(self.request.FILES.get('test_data')))
        results_table = test_data_results._generate_db_entries(test_data_results)

Now the funny part. I found that on subsequent calls to the API, the test_data_results kept on multiplying 😮 like the data from the previous POST also got appended to the current JSON which-should-be-processed. Thinking it was a problem with the client, I tried switching to curl, but with little luck.

The problem
Finally pycharm got me figuring out what went wrong, as it showed a warning on the test_table = [] something like:
The default method argument is mutable. Bah, taking at look at http://stackoverflow.com/questions/9039191/mutable-default-method-arguments-in-python I see that when an empty list [] is kept as a default formal argument for a function, the list is not killed in between, and keeps on growing on subsequent calls – surprise :\

Quoting http://stackoverflow.com/a/9039224/3355893, when we have something like:

def status(options=[]):
    options.append('new_option')
    return options

print status()
print status()
print status()

will print something like

['new_option']
['new_option', 'new_option']
['new_option', 'new_option', 'new_option']

Tough luck finding it out, but it took couple of hours 😀 I got my code easily fixed by not allowing [] to stay as a default param, but separately passing an empty list to the function during the call. Like

class BenchmarkResults(object):

    @classmethod
    def _generate_db_entries_helper(cls, tests, skip_aggregated=False, parent=None):
        test_table = []
        self._generate_db_entries(cls, tests, test_tables skip_aggregated=False, parent=None):

    @classmethod
    def _generate_db_entries(cls, tests, test_table, skip_aggregated=False, parent=None):
         # This function recursively calls _generate_db_entries with test_table param set to a list 

Yay. that was it, and latter called in the helper function, and things started working. You can see the change here, and finally, Happy New year 2017! Make every commit count!

Advertisements

Leave a Reply

Please log in using one of these methods to post your comment:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s