summaryrefslogtreecommitdiff
path: root/django
diff options
context:
space:
mode:
authorFlorian Apolloner <florian@apolloner.eu>2014-05-25 22:52:47 +0200
committerFlorian Apolloner <florian@apolloner.eu>2014-06-11 09:03:11 +0200
commit1ff11304dcebbabdede8ef3d659ca0e54055e2fd (patch)
treec3924926cdd778492c63059e781c92fee720b9b2 /django
parentde0e285be80e0ca756088a97335d8faa698fbe6b (diff)
[1.7.x] Fixed #22680 -- I/O operation on closed file.
This patch is two-fold; first it ensure that Django does close everything in request.FILES at the end of the request and secondly the storage system should no longer close any files during save, it's up to the caller to handle that -- or let Django close the files at the end of the request. Backport of e2efc8965edf684aaf48621680ef54b84e116576 from master.
Diffstat (limited to 'django')
-rw-r--r--django/core/files/storage.py2
-rw-r--r--django/core/files/uploadedfile.py3
-rw-r--r--django/core/handlers/base.py2
-rw-r--r--django/http/multipartparser.py10
-rw-r--r--django/http/request.py6
5 files changed, 18 insertions, 5 deletions
diff --git a/django/core/files/storage.py b/django/core/files/storage.py
index 98864f7f22..58c623b883 100644
--- a/django/core/files/storage.py
+++ b/django/core/files/storage.py
@@ -209,7 +209,6 @@ class FileSystemStorage(Storage):
# This file has a file path that we can move.
if hasattr(content, 'temporary_file_path'):
file_move_safe(content.temporary_file_path(), full_path)
- content.close()
# This is a normal uploadedfile that we can stream.
else:
@@ -228,7 +227,6 @@ class FileSystemStorage(Storage):
_file = os.fdopen(fd, mode)
_file.write(chunk)
finally:
- content.close()
locks.unlock(fd)
if _file is not None:
_file.close()
diff --git a/django/core/files/uploadedfile.py b/django/core/files/uploadedfile.py
index 75f25405b9..9a94894424 100644
--- a/django/core/files/uploadedfile.py
+++ b/django/core/files/uploadedfile.py
@@ -96,9 +96,6 @@ class InMemoryUploadedFile(UploadedFile):
def open(self, mode=None):
self.file.seek(0)
- def close(self):
- pass
-
def chunks(self, chunk_size=None):
self.file.seek(0)
yield self.read()
diff --git a/django/core/handlers/base.py b/django/core/handlers/base.py
index ffc48a076a..887898a33c 100644
--- a/django/core/handlers/base.py
+++ b/django/core/handlers/base.py
@@ -207,6 +207,8 @@ class BaseHandler(object):
signals.got_request_exception.send(sender=self.__class__, request=request)
response = self.handle_uncaught_exception(request, resolver, sys.exc_info())
+ response._closable_objects.append(request)
+
return response
def handle_uncaught_exception(self, request, resolver, exc_info):
diff --git a/django/http/multipartparser.py b/django/http/multipartparser.py
index 20232cb7c6..1bcace94cd 100644
--- a/django/http/multipartparser.py
+++ b/django/http/multipartparser.py
@@ -228,6 +228,7 @@ class MultiPartParser(object):
break
except SkipFile:
+ self._close_files()
# Just use up the rest of this file...
exhaust(field_stream)
else:
@@ -237,6 +238,7 @@ class MultiPartParser(object):
# If this is neither a FIELD or a FILE, just exhaust the stream.
exhaust(stream)
except StopUpload as e:
+ self._close_files()
if not e.connection_reset:
exhaust(self._input_data)
else:
@@ -268,6 +270,14 @@ class MultiPartParser(object):
"""Cleanup filename from Internet Explorer full paths."""
return filename and filename[filename.rfind("\\") + 1:].strip()
+ def _close_files(self):
+ # Free up all file handles.
+ # FIXME: this currently assumes that upload handlers store the file as 'file'
+ # We should document that... (Maybe add handler.free_file to complement new_file)
+ for handler in self._upload_handlers:
+ if hasattr(handler, 'file'):
+ handler.file.close()
+
class LazyStream(six.Iterator):
"""
diff --git a/django/http/request.py b/django/http/request.py
index ded4744a87..ecad639c82 100644
--- a/django/http/request.py
+++ b/django/http/request.py
@@ -5,6 +5,7 @@ import os
import re
import sys
from io import BytesIO
+from itertools import chain
from pprint import pformat
from django.conf import settings
@@ -245,6 +246,11 @@ class HttpRequest(object):
else:
self._post, self._files = QueryDict('', encoding=self._encoding), MultiValueDict()
+ def close(self):
+ if hasattr(self, '_files'):
+ for f in chain.from_iterable(l[1] for l in self._files.lists()):
+ f.close()
+
# File-like and iterator interface.
#
# Expects self._stream to be set to an appropriate source of bytes by