Search code examples
pythonarraystornadopython-internals

Python/Tornado Class wrapper caching issue


I'm implementing a wrapper around Python's array data structure. I'm doing this for practical reasons in my application, but this example code is just provided to reproduce the problem. The array doesn't seem to be 'cleared' for each request through the Tornado abstraction.

If I don't use my array abstraction, there is no problem. This leads me to believe there is a bug somewhere in the CPython implementation.

from tornado import websocket, web, ioloop
import json

class Array():
    _data = []
    def push(self, value):
        self._data.append(value)

    def json(self):
        return json.dumps(self._data)


class ClientHandler(web.RequestHandler):
    def prepare(self):
        self.set_header("content-type", "application/json")

    def get(self):
        array = Array()

        for i in range(0, 6):
            array.push({'id': i})

        self.write(array.json())
        self.finish()

app = web.Application([
    (r'/client', ClientHandler),
], debug=True)

if __name__ == '__main__':
    kwargs = {"address": "127.0.0.1"}
    app.listen(port=8888, **kwargs)
    ioloop.IOLoop.instance().start()

The output I get after refreshing the page once I've started the python process is as follows in sequence:

Sequence 1

[{"id": 0}, {"id": 1}, {"id": 2}, {"id": 3}, {"id": 4}, {"id": 5}, {"id": 6}]

Sequence 2

[{"id": 0}, {"id": 1}, {"id": 2}, {"id": 3}, {"id": 4}, {"id": 5}, {"id": 6}, {"id": 0}, {"id": 1}, {"id": 2}, {"id": 3}, {"id": 4}, {"id": 5}, {"id": 6}]

Sequence 3

[{"id": 0}, {"id": 1}, {"id": 2}, {"id": 3}, {"id": 4}, {"id": 5}, {"id": 6}, {"id": 0}, {"id": 1}, {"id": 2}, {"id": 3}, {"id": 4}, {"id": 5}, {"id": 6}, {"id": 0}, {"id": 1}, {"id": 2}, {"id": 3}, {"id": 4}, {"id": 5}, {"id": 6}]

This response out is not the expected output. The expected output should have a length of 6 of the JSON output array. This problem does NOT happen if I don't wrap Python's data structure.

Why does this happen? I'm a new enthusiastic Python user, but this type of thing discourages me from using the language if it can't even handle a simple abstraction.

Extra

To run this:

  • install the Tornado package at pip install tornado,
  • save the code I provided in a file called app.py
  • execute python app.py
  • open the web application in your broswer for http://127.0.0.1/client

Solution

  • The issue is because Array._data is actually a static member of Array meaning it's value will be the same over all instances of Array.

    class Array():
        _data = []
        def push(self, value):
            self._data.append(value)
    
        def json(self):
            return json.dumps(self._data)
    

    To solve the problem, make _data an instance member.

    class Array():
        def __init__(self):
            self._data = []
    
        def push(self, value):
            self._data.append(value)
    
        def json(self):
            return json.dumps(self._data)