#20493: HttpResponse objects cached with cache_page don't unpickle across Django
version leading to strange errors
-------------------------------------+--------------------
     Reporter:  cataliniacob         |      Owner:  nobody
         Type:  Bug                  |     Status:  new
    Component:  Core (Cache system)  |    Version:  1.5
     Severity:  Normal               |   Keywords:
 Triage Stage:  Unreviewed           |  Has patch:  0
Easy pickings:  0                    |      UI/UX:  0
-------------------------------------+--------------------
 I just upgraded from 1.4 to 1.5 and this took a while to track.

 If one has a cache that persists across a Django version change
 (filesystem cache, memcached that is not restarted), when executing any
 view decorated with @cache_page unpickling the HttpResponse from the cache
 leads to strange errors. For example, when upgrading from 1.4 to 1.5 I
 get:

 {{{
 [24/May/2013 15:53:42] "GET / HTTP/1.1" 200 5
 Traceback (most recent call last):
   File "/usr/lib64/python2.7/wsgiref/handlers.py", line 86, in run
     self.finish_response()
   File "/usr/lib64/python2.7/wsgiref/handlers.py", line 129, in
 finish_response
     self.close()
   File "/usr/lib64/python2.7/wsgiref/simple_server.py", line 36, in close
     SimpleHandler.close(self)
   File "/usr/lib64/python2.7/wsgiref/handlers.py", line 257, in close
     self.result.close()
   File "/home/catalin/hacking/envs/cached-httpresponse-across-
 versions/lib/python2.7/site-packages/django/http/response.py", line 231,
 in close
     for closable in self._closable_objects:
 AttributeError: 'HttpResponse' object has no attribute '_closable_objects'
 [24/May/2013 15:53:42] "GET / HTTP/1.1" 500 59
 ----------------------------------------
 Exception happened during processing of request from ('127.0.0.1', 49852)
 Traceback (most recent call last):
   File "/usr/lib64/python2.7/SocketServer.py", line 582, in
 process_request_thread
     self.finish_request(request, client_address)
   File "/usr/lib64/python2.7/SocketServer.py", line 323, in finish_request
     self.RequestHandlerClass(request, client_address, self)
   File "/home/catalin/hacking/envs/cached-httpresponse-across-
 versions/lib/python2.7/site-packages/django/core/servers/basehttp.py",
 line 150, in __init__
     super(WSGIRequestHandler, self).__init__(*args, **kwargs)
   File "/usr/lib64/python2.7/SocketServer.py", line 638, in __init__
     self.handle()
   File "/usr/lib64/python2.7/wsgiref/simple_server.py", line 124, in
 handle
     handler.run(self.server.get_app())
   File "/usr/lib64/python2.7/wsgiref/handlers.py", line 89, in run
     self.handle_error()
   File "/usr/lib64/python2.7/wsgiref/handlers.py", line 304, in
 handle_error
     self.finish_response()
   File "/usr/lib64/python2.7/wsgiref/handlers.py", line 127, in
 finish_response
     self.write(data)
   File "/usr/lib64/python2.7/wsgiref/handlers.py", line 210, in write
     self.send_headers()
   File "/usr/lib64/python2.7/wsgiref/handlers.py", line 267, in
 send_headers
     if not self.origin_server or self.client_is_modern():
   File "/usr/lib64/python2.7/wsgiref/handlers.py", line 280, in
 client_is_modern
     return self.environ['SERVER_PROTOCOL'].upper() != 'HTTP/0.9'
 TypeError: 'NoneType' object has no attribute '__getitem__'
 ----------------------------------------
 }}}

 This was at first quite perplexing because I checked response.py and
 {{{self._closable_objects}}} gets assigned in
 {{{HttpResponseBase.__init__}}} which gets called by
 {{{HttpResponse.__init__}}} so it seemed impossible for an HttpResponse
 instance not to have the attribute. Unpickling doesn't normally call
 {{{__init__}}} but this is not at all obvious or easy to track.

 There are similar issues but with a bit more clear stacktrace if
 downgrading from 1.5 to 1.4:
 {{{
 Traceback:
 File "/home/catalin/hacking/envs/cached-httpresponse-across-
 versions/lib/python2.7/site-packages/django/core/handlers/base.py" in
 get_response
   89.                     response = middleware_method(request)
 File "/home/catalin/hacking/envs/cached-httpresponse-across-
 versions/lib/python2.7/site-packages/django/middleware/cache.py" in
 process_request
   147.         response = self.cache.get(cache_key, None)
 File "/home/catalin/hacking/envs/cached-httpresponse-across-
 versions/lib/python2.7/site-
 packages/django/core/cache/backends/filebased.py" in get
   41.                     return pickle.load(f)

 Exception Type: ImportError at /
 Exception Value: No module named response
 }}}

 I'm surprised nobody seems to have run into this so far, googling for
 AttributeError on _closable_objects and searching the other bugs didn't
 show anything. I would imagine it's reasonably common to upgrade without
 restarting memcached or to have a filesystem based cache on a development
 machine where you don't bother to install memcached. And this happens on
 '''every''' view with @cache_page.

 I don't know what a good solution would be. I can imagine Django doesn't
 want to guarantee pickle/unpickle round trips across versions as those can
 be hard to maintain but I think there should at least be a warning in the
 upgrade notes (of every version): when upgrading Django you need to delete
 your cache.

-- 
Ticket URL: <https://code.djangoproject.com/ticket/20493>
Django <https://code.djangoproject.com/>
The Web framework for perfectionists with deadlines.

-- 
You received this message because you are subscribed to the Google Groups 
"Django updates" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
To post to this group, send email to [email protected].
To view this discussion on the web visit 
https://groups.google.com/d/msgid/django-updates/055.dbacffdd3e70e1677cf206377eee0a87%40djangoproject.com?hl=en.
For more options, visit https://groups.google.com/groups/opt_out.


Reply via email to