Fixed #19905 -- Fixed leakage of file descriptors in form wizard.
This commit is contained in:
parent
6f4d7f41b1
commit
c4c2c99669
|
@ -1,5 +1,7 @@
|
|||
from __future__ import unicode_literals
|
||||
|
||||
import copy
|
||||
|
||||
from django.core.urlresolvers import reverse
|
||||
from django.http import QueryDict
|
||||
from django.test import TestCase, override_settings
|
||||
|
@ -11,13 +13,30 @@ from django.contrib.formtools.wizard.views import (NamedUrlSessionWizardView,
|
|||
NamedUrlCookieWizardView)
|
||||
from django.contrib.formtools.tests.wizard.test_forms import get_request, Step1, Step2
|
||||
|
||||
from .forms import temp_storage
|
||||
|
||||
|
||||
UPLOADED_FILE_NAME = 'tests.py'
|
||||
|
||||
|
||||
class NamedWizardTests(object):
|
||||
|
||||
def setUp(self):
|
||||
self.testuser, created = User.objects.get_or_create(username='testuser1')
|
||||
# Get new step data, since we modify it during the tests.
|
||||
self.wizard_step_data = copy.deepcopy(self.wizard_step_data)
|
||||
self.wizard_step_data[0]['form1-user'] = self.testuser.pk
|
||||
|
||||
def tearDown(self):
|
||||
# Ensure that there are no files in the storage which could lead to false
|
||||
# results in the next tests. Deleting the whole storage dir is not really
|
||||
# an option since the storage is defined on the module level and can't be
|
||||
# easily reinitialized. (FIXME: The tests here should use the view classes
|
||||
# directly instead of the test client, then the storage issues would go
|
||||
# away too.)
|
||||
for file in temp_storage.listdir('')[1]:
|
||||
temp_storage.delete(file)
|
||||
|
||||
def test_initial_call(self):
|
||||
response = self.client.get(reverse('%s_start' % self.wizard_urlname))
|
||||
self.assertEqual(response.status_code, 302)
|
||||
|
@ -122,17 +141,21 @@ class NamedWizardTests(object):
|
|||
self.assertEqual(response.context['wizard']['steps'].current, 'form2')
|
||||
|
||||
post_data = self.wizard_step_data[1]
|
||||
post_data['form2-file1'].close()
|
||||
post_data['form2-file1'] = open(__file__, 'rb')
|
||||
response = self.client.post(
|
||||
reverse(self.wizard_urlname,
|
||||
kwargs={'step': response.context['wizard']['steps'].current}),
|
||||
post_data)
|
||||
with open(__file__, 'rb') as post_file:
|
||||
post_data['form2-file1'] = post_file
|
||||
response = self.client.post(
|
||||
reverse(self.wizard_urlname,
|
||||
kwargs={'step': response.context['wizard']['steps'].current}),
|
||||
post_data)
|
||||
response = self.client.get(response.url)
|
||||
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertEqual(response.context['wizard']['steps'].current, 'form3')
|
||||
|
||||
# Check that the file got uploaded properly.
|
||||
with open(__file__, 'rb') as f, temp_storage.open(UPLOADED_FILE_NAME) as f2:
|
||||
self.assertEqual(f.read(), f2.read())
|
||||
|
||||
response = self.client.post(
|
||||
reverse(self.wizard_urlname,
|
||||
kwargs={'step': response.context['wizard']['steps'].current}),
|
||||
|
@ -149,10 +172,10 @@ class NamedWizardTests(object):
|
|||
response = self.client.get(response.url)
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
||||
# After the wizard is done no files should exist anymore.
|
||||
self.assertFalse(temp_storage.exists(UPLOADED_FILE_NAME))
|
||||
|
||||
all_data = response.context['form_list']
|
||||
with open(__file__, 'rb') as f:
|
||||
self.assertEqual(all_data[1]['file1'].read(), f.read())
|
||||
all_data[1]['file1'].close()
|
||||
del all_data[1]['file1']
|
||||
self.assertEqual(all_data, [
|
||||
{'name': 'Pony', 'thirsty': True, 'user': self.testuser},
|
||||
|
@ -173,22 +196,22 @@ class NamedWizardTests(object):
|
|||
self.assertEqual(response.status_code, 200)
|
||||
|
||||
post_data = self.wizard_step_data[1]
|
||||
post_data['form2-file1'] = open(__file__, 'rb')
|
||||
response = self.client.post(
|
||||
reverse(self.wizard_urlname,
|
||||
kwargs={'step': response.context['wizard']['steps'].current}),
|
||||
post_data)
|
||||
with open(__file__, 'rb') as post_file:
|
||||
post_data['form2-file1'] = post_file
|
||||
response = self.client.post(
|
||||
reverse(self.wizard_urlname,
|
||||
kwargs={'step': response.context['wizard']['steps'].current}),
|
||||
post_data)
|
||||
response = self.client.get(response.url)
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertTrue(temp_storage.exists(UPLOADED_FILE_NAME))
|
||||
|
||||
step2_url = reverse(self.wizard_urlname, kwargs={'step': 'form2'})
|
||||
response = self.client.get(step2_url)
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertEqual(response.context['wizard']['steps'].current, 'form2')
|
||||
with open(__file__, 'rb') as f:
|
||||
self.assertEqual(
|
||||
response.context['wizard']['form'].files['form2-file1'].read(),
|
||||
f.read())
|
||||
with open(__file__, 'rb') as f, temp_storage.open(UPLOADED_FILE_NAME) as f2:
|
||||
self.assertEqual(f.read(), f2.read())
|
||||
|
||||
response = self.client.post(
|
||||
reverse(self.wizard_urlname,
|
||||
|
@ -205,9 +228,9 @@ class NamedWizardTests(object):
|
|||
self.assertEqual(response.status_code, 200)
|
||||
|
||||
all_data = response.context['all_cleaned_data']
|
||||
with open(__file__, 'rb') as f:
|
||||
self.assertEqual(all_data['file1'].read(), f.read())
|
||||
all_data['file1'].close()
|
||||
self.assertEqual(all_data['file1'].name, UPLOADED_FILE_NAME)
|
||||
self.assertTrue(all_data['file1'].closed)
|
||||
self.assertFalse(temp_storage.exists(UPLOADED_FILE_NAME))
|
||||
del all_data['file1']
|
||||
self.assertEqual(
|
||||
all_data,
|
||||
|
@ -236,12 +259,12 @@ class NamedWizardTests(object):
|
|||
self.assertEqual(response.status_code, 200)
|
||||
|
||||
post_data = self.wizard_step_data[1]
|
||||
post_data['form2-file1'].close()
|
||||
post_data['form2-file1'] = open(__file__, 'rb')
|
||||
response = self.client.post(
|
||||
reverse(self.wizard_urlname,
|
||||
kwargs={'step': response.context['wizard']['steps'].current}),
|
||||
post_data)
|
||||
with open(__file__, 'rb') as post_file:
|
||||
post_data['form2-file1'] = post_file
|
||||
response = self.client.post(
|
||||
reverse(self.wizard_urlname,
|
||||
kwargs={'step': response.context['wizard']['steps'].current}),
|
||||
post_data)
|
||||
response = self.client.get(response.url)
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
||||
|
|
|
@ -101,7 +101,9 @@ class TestStorage(object):
|
|||
file_ = SimpleUploadedFile('file.txt', b'content')
|
||||
storage.set_step_files(step, {'file': file_})
|
||||
|
||||
tmp_name = storage.get_step_files(step)['file'].name
|
||||
with storage.get_step_files(step)['file'] as file:
|
||||
tmp_name = file.name
|
||||
|
||||
self.assertTrue(storage.file_storage.exists(tmp_name))
|
||||
|
||||
storage.reset()
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
from __future__ import unicode_literals
|
||||
|
||||
import copy
|
||||
import os
|
||||
|
||||
from django import forms
|
||||
|
@ -12,6 +13,11 @@ from django.contrib.formtools.wizard.views import CookieWizardView
|
|||
from django.utils._os import upath
|
||||
from django.contrib.formtools.tests.models import Poet, Poem
|
||||
|
||||
from .forms import temp_storage
|
||||
|
||||
|
||||
UPLOADED_FILE_NAME = 'tests.py'
|
||||
|
||||
|
||||
class UserForm(forms.ModelForm):
|
||||
class Meta:
|
||||
|
@ -27,8 +33,20 @@ class WizardTests(object):
|
|||
|
||||
def setUp(self):
|
||||
self.testuser, created = User.objects.get_or_create(username='testuser1')
|
||||
# Get new step data, since we modify it during the tests.
|
||||
self.wizard_step_data = copy.deepcopy(self.wizard_step_data)
|
||||
self.wizard_step_data[0]['form1-user'] = self.testuser.pk
|
||||
|
||||
def tearDown(self):
|
||||
# Ensure that there are no files in the storage which could lead to false
|
||||
# results in the next tests. Deleting the whole storage dir is not really
|
||||
# an option since the storage is defined on the module level and can't be
|
||||
# easily reinitialized. (FIXME: The tests here should use the view classes
|
||||
# directly instead of the test client, then the storage issues would go
|
||||
# away too.)
|
||||
for file in temp_storage.listdir('')[1]:
|
||||
temp_storage.delete(file)
|
||||
|
||||
def test_initial_call(self):
|
||||
response = self.client.get(self.wizard_url)
|
||||
wizard = response.context['wizard']
|
||||
|
@ -97,11 +115,16 @@ class WizardTests(object):
|
|||
self.assertEqual(response.context['wizard']['steps'].current, 'form2')
|
||||
|
||||
post_data = self.wizard_step_data[1]
|
||||
post_data['form2-file1'] = open(upath(__file__), 'rb')
|
||||
response = self.client.post(self.wizard_url, post_data)
|
||||
with open(upath(__file__), 'rb') as post_file:
|
||||
post_data['form2-file1'] = post_file
|
||||
response = self.client.post(self.wizard_url, post_data)
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertEqual(response.context['wizard']['steps'].current, 'form3')
|
||||
|
||||
# Check that the file got uploaded properly.
|
||||
with open(upath(__file__), 'rb') as f, temp_storage.open(UPLOADED_FILE_NAME) as f2:
|
||||
self.assertEqual(f.read(), f2.read())
|
||||
|
||||
response = self.client.post(self.wizard_url, self.wizard_step_data[2])
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertEqual(response.context['wizard']['steps'].current, 'form4')
|
||||
|
@ -109,10 +132,10 @@ class WizardTests(object):
|
|||
response = self.client.post(self.wizard_url, self.wizard_step_data[3])
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
||||
# After the wizard is done no files should exist anymore.
|
||||
self.assertFalse(temp_storage.exists(UPLOADED_FILE_NAME))
|
||||
|
||||
all_data = response.context['form_list']
|
||||
with open(upath(__file__), 'rb') as f:
|
||||
self.assertEqual(all_data[1]['file1'].read(), f.read())
|
||||
all_data[1]['file1'].close()
|
||||
del all_data[1]['file1']
|
||||
self.assertEqual(all_data, [
|
||||
{'name': 'Pony', 'thirsty': True, 'user': self.testuser},
|
||||
|
@ -133,6 +156,7 @@ class WizardTests(object):
|
|||
post_data['form2-file1'] = post_file
|
||||
response = self.client.post(self.wizard_url, post_data)
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertTrue(temp_storage.exists(UPLOADED_FILE_NAME))
|
||||
|
||||
response = self.client.post(self.wizard_url, self.wizard_step_data[2])
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
@ -141,9 +165,9 @@ class WizardTests(object):
|
|||
self.assertEqual(response.status_code, 200)
|
||||
|
||||
all_data = response.context['all_cleaned_data']
|
||||
with open(upath(__file__), 'rb') as f:
|
||||
self.assertEqual(all_data['file1'].read(), f.read())
|
||||
all_data['file1'].close()
|
||||
self.assertEqual(all_data['file1'].name, UPLOADED_FILE_NAME)
|
||||
self.assertTrue(all_data['file1'].closed)
|
||||
self.assertFalse(temp_storage.exists(UPLOADED_FILE_NAME))
|
||||
del all_data['file1']
|
||||
self.assertEqual(all_data, {
|
||||
'name': 'Pony', 'thirsty': True, 'user': self.testuser,
|
||||
|
@ -160,9 +184,9 @@ class WizardTests(object):
|
|||
self.assertEqual(response.status_code, 200)
|
||||
|
||||
post_data = self.wizard_step_data[1]
|
||||
post_data['form2-file1'].close()
|
||||
post_data['form2-file1'] = open(upath(__file__), 'rb')
|
||||
response = self.client.post(self.wizard_url, post_data)
|
||||
with open(upath(__file__), 'rb') as post_file:
|
||||
post_data['form2-file1'] = post_file
|
||||
response = self.client.post(self.wizard_url, post_data)
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
||||
response = self.client.post(self.wizard_url, self.wizard_step_data[2])
|
||||
|
@ -188,9 +212,9 @@ class WizardTests(object):
|
|||
self.assertEqual(response.context['wizard']['steps'].current, 'form2')
|
||||
|
||||
post_data = self.wizard_step_data[1]
|
||||
post_data['form2-file1'].close()
|
||||
post_data['form2-file1'] = open(upath(__file__), 'rb')
|
||||
response = self.client.post(self.wizard_url, post_data)
|
||||
with open(upath(__file__), 'rb') as post_file:
|
||||
post_data['form2-file1'] = post_file
|
||||
response = self.client.post(self.wizard_url, post_data)
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertEqual(response.context['wizard']['steps'].current, 'form3')
|
||||
|
||||
|
|
|
@ -16,6 +16,7 @@ class BaseStorage(object):
|
|||
self.prefix = 'wizard_%s' % prefix
|
||||
self.request = request
|
||||
self.file_storage = file_storage
|
||||
self._files = {}
|
||||
|
||||
def init_data(self):
|
||||
self.data = {
|
||||
|
@ -82,8 +83,10 @@ class BaseStorage(object):
|
|||
for field, field_dict in six.iteritems(wizard_files):
|
||||
field_dict = field_dict.copy()
|
||||
tmp_name = field_dict.pop('tmp_name')
|
||||
files[field] = UploadedFile(
|
||||
file=self.file_storage.open(tmp_name), **field_dict)
|
||||
if (step, field) not in self._files:
|
||||
self._files[(step, field)] = UploadedFile(
|
||||
file=self.file_storage.open(tmp_name), **field_dict)
|
||||
files[field] = self._files[(step, field)]
|
||||
return files or None
|
||||
|
||||
def set_step_files(self, step, files):
|
||||
|
@ -111,4 +114,12 @@ class BaseStorage(object):
|
|||
return self.get_step_files(self.current_step)
|
||||
|
||||
def update_response(self, response):
|
||||
pass
|
||||
def post_render_callback(response):
|
||||
for file in self._files.values():
|
||||
if not file.closed:
|
||||
file.close()
|
||||
|
||||
if hasattr(response, 'render'):
|
||||
response.add_post_render_callback(post_render_callback)
|
||||
else:
|
||||
post_render_callback(response)
|
||||
|
|
|
@ -19,6 +19,7 @@ class CookieStorage(storage.BaseStorage):
|
|||
return json.loads(data, cls=json.JSONDecoder)
|
||||
|
||||
def update_response(self, response):
|
||||
super(CookieStorage, self).update_response(response)
|
||||
if self.data:
|
||||
response.set_signed_cookie(self.prefix, self.encoder.encode(self.data))
|
||||
else:
|
||||
|
|
Loading…
Reference in New Issue