Commit c4c2c996 authored by Florian Apolloner's avatar Florian Apolloner
Browse files

Fixed #19905 -- Fixed leakage of file descriptors in form wizard.

parent 6f4d7f41
Loading
Loading
Loading
Loading
+50 −27
Original line number Diff line number Diff line
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,8 +141,8 @@ 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')
        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}),
@@ -133,6 +152,10 @@ class NamedWizardTests(object):
        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')
        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,8 +259,8 @@ 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')
        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}),
+3 −1
Original line number Diff line number Diff line
@@ -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()
+38 −14
Original line number Diff line number Diff line
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')
        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,8 +184,8 @@ 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')
        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)

@@ -188,8 +212,8 @@ 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')
        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')
+14 −3
Original line number Diff line number Diff line
@@ -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(
            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)
+1 −0
Original line number Diff line number Diff line
@@ -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: