Ticket #20034: 1148.patch
File 1148.patch, 10.6 KB (added by , 12 years ago) |
---|
-
django/core/files/uploadhandler.py
From 5c68d02854a64cef882c2cc58a9cc3976b3587b8 Mon Sep 17 00:00:00 2001 From: TK Kocheran <rfkrocktk@gmail.com> Date: Tue, 12 Mar 2013 19:17:45 -0700 Subject: [PATCH 1/2] Modified upload handler API to be able to invoke callbacks when new fields/variables are successfully parsed from multipart POST request. Fix for #20034. --- django/core/files/uploadhandler.py | 6 ++++++ django/http/multipartparser.py | 7 +++++++ 2 files changed, 13 insertions(+) diff --git a/django/core/files/uploadhandler.py b/django/core/files/uploadhandler.py index f5e95cf..080b5d3 100644
a b def handle_raw_input(self, input_data, META, content_length, boundary, encoding= 84 84 """ 85 85 pass 86 86 87 def variable_complete(self, variable_name, variable_value): 88 """ 89 Signal that a new variable has been parsed from the multipart request. 90 """ 91 pass 92 87 93 def new_file(self, field_name, file_name, content_type, content_length, charset=None): 88 94 """ 89 95 Signal that a new file has been started. -
django/http/multipartparser.py
diff --git a/django/http/multipartparser.py b/django/http/multipartparser.py index 26e10da..cc5c809 100644
a b def parse(self): 169 169 170 170 self._post.appendlist(field_name, 171 171 force_text(data, encoding, errors='replace')) 172 173 for handler in handlers: 174 try : 175 handler.variable_complete(field_name, force_text(data, encoding, errors='replace')) 176 except StopFutureHandlers: 177 break 178 172 179 elif item_type == FILE: 173 180 # This is a file, use the handler... 174 181 file_name = disposition.get('filename') -
AUTHORS
-- 1.8.1.6 From d9866dad7c6d45d80af6053b2cdec2e885b4005d Mon Sep 17 00:00:00 2001 From: Tomasz Jaskowski <tadeck@gmail.com> Date: Sat, 18 May 2013 18:28:42 +0200 Subject: [PATCH 2/2] Refactored new feature #20034, added tests, documented. --- AUTHORS | 1 + django/core/files/uploadhandler.py | 15 +++--- django/http/multipartparser.py | 8 +-- docs/topics/http/file-uploads.txt | 15 ++++++ tests/requests/tests.py | 101 +++++++++++++++++++++++++++++++++++++ 5 files changed, 130 insertions(+), 10 deletions(-) diff --git a/AUTHORS b/AUTHORS index 4a9981d..2f91c08 100644
a b answer newbie questions, and generally made Django that much better: 293 293 Baurzhan Ismagulov <ibr@radix50.net> 294 294 Stephan Jaekel <steph@rdev.info> 295 295 james_027@yahoo.com 296 Tom Jaskowski <tadeck@gmail.com> 296 297 jcrasta@gmail.com 297 298 jdetaeye 298 299 Dmitry Jemerov <intelliyole@gmail.com> -
django/core/files/uploadhandler.py
diff --git a/django/core/files/uploadhandler.py b/django/core/files/uploadhandler.py index 080b5d3..49ed98d 100644
a b def handle_raw_input(self, input_data, META, content_length, boundary, encoding= 84 84 """ 85 85 pass 86 86 87 def variable_complete(self, variable_name, variable_value):88 """89 Signal that a new variable has been parsed from the multipart request.90 """91 pass92 93 87 def new_file(self, field_name, file_name, content_type, content_length, charset=None): 94 88 """ 95 89 Signal that a new file has been started. … … def file_complete(self, file_size): 119 113 """ 120 114 raise NotImplementedError() 121 115 116 def variable_complete(self, variable_name, variable_value): 117 """ 118 Signal that a new variable has been parsed from the multipart request. 119 120 :param variable_name: name of the variable 121 :param variable_value: value of the multipart variable 122 """ 123 pass 124 122 125 def upload_complete(self): 123 126 """ 124 127 Signal that the upload is complete. Subclasses should perform cleanup -
django/http/multipartparser.py
diff --git a/django/http/multipartparser.py b/django/http/multipartparser.py index cc5c809..6679899 100644
a b def parse(self): 167 167 else: 168 168 data = field_stream.read() 169 169 170 self._post.appendlist(field_name,171 force_text(data, encoding, errors='replace'))170 field_text = force_text(data, encoding, errors='replace') 171 self._post.appendlist(field_name, field_text) 172 172 173 173 for handler in handlers: 174 try 175 handler.variable_complete(field_name, f orce_text(data, encoding, errors='replace'))174 try: 175 handler.variable_complete(field_name, field_text) 176 176 except StopFutureHandlers: 177 177 break 178 178 -
docs/topics/http/file-uploads.txt
diff --git a/docs/topics/http/file-uploads.txt b/docs/topics/http/file-uploads.txt index 80bd5f3..aa857af 100644
a b attributes: 425 425 ``FileUploadHandler.upload_complete(self)`` 426 426 Callback signaling that the entire upload (all files) has completed. 427 427 428 ``FileUploadHandler.variable_complete(self, variable_name, variable_value)`` 429 Callback signaling that a new variable has been parsed. This is called 430 before new file is parsed. 431 432 ``variable_name`` is a string name of the non-file ``<input>`` field. 433 434 ``variable_value`` is the value (text) provided in the field by browser. 435 436 This method may raise a ``StopFutureHandlers`` exception to prevent 437 future handlers from handling this variable. 438 439 .. versionadded:: 1.6 440 441 The ``variable_complete`` method was added in Django 1.6. 442 428 443 ``FileUploadHandler.handle_raw_input(self, input_data, META, content_length, boundary, encoding)`` 429 444 Allows the handler to completely override the parsing of the raw 430 445 HTTP input. -
tests/requests/tests.py
diff --git a/tests/requests/tests.py b/tests/requests/tests.py index 676cd05..8e76832 100644
a b 1 1 # -*- encoding: utf-8 -*- 2 2 from __future__ import unicode_literals 3 3 4 from contextlib import contextmanager 4 5 import time 5 6 import warnings 6 7 from datetime import datetime, timedelta … … def read(self, len=0): 659 660 with self.assertRaises(UnreadablePostError): 660 661 request.body 661 662 663 def test_POST_new_variable_signal_in_multipart(self): 664 """ 665 Multi part POST requests should allow reading variables even before 666 parsing whole files. This can be done by overloading file upload 667 handlers' ``new_variable`` method. 668 """ 669 collected_variables_memory_handler = [] 670 collected_variables_temporary_handler = [] 671 672 def get_var_collector(handler): 673 """Retrieve variable collector for specific handler""" 674 def _collect_variable(self, variable_name, variable_value): 675 """Store variable in external list, to check it later""" 676 if handler == 'memory': 677 collected_variables_memory_handler.append( 678 (variable_name, variable_value), 679 ) 680 elif handler == 'temporary': 681 collected_variables_temporary_handler.append( 682 (variable_name, variable_value), 683 ) 684 else: 685 raise NotImplementedError( 686 'Collector for %s not implemented' % (handler,), 687 ) 688 return _collect_variable 689 690 @contextmanager 691 def override_upload_handler_methods(): 692 """Override parser method temporarily""" 693 from django.core.files.uploadhandler import ( 694 MemoryFileUploadHandler, TemporaryFileUploadHandler, 695 ) 696 old_method_memory = MemoryFileUploadHandler.variable_complete 697 old_method_temporary = TemporaryFileUploadHandler.variable_complete 698 699 # Override handler method: 700 MemoryFileUploadHandler.variable_complete = get_var_collector( 701 'memory', 702 ) 703 TemporaryFileUploadHandler.variable_complete = get_var_collector( 704 'temporary', 705 ) 706 yield 707 # Recover previous state: 708 MemoryFileUploadHandler.new_variable = old_method_memory 709 TemporaryFileUploadHandler.new_variable = old_method_temporary 710 711 payload = FakePayload( 712 "\r\n".join([ 713 '--boundary', 714 'Content-Disposition: form-data; name="n1"', 715 '', 716 'value', 717 '--boundary', 718 'Content-Disposition: form-data; name="n2"; filename="f.txt"', 719 'Content-Type: text/plain', 720 '', 721 '... contents of file1.txt ...', 722 'value', 723 '--boundary--', 724 'Content-Disposition: form-data; name="n3"', 725 '', 726 'value', 727 '--boundary--', 728 ''])) 729 request = WSGIRequest({ 730 'REQUEST_METHOD': 'POST', 731 'CONTENT_TYPE': 'multipart/form-data; boundary=boundary', 732 'CONTENT_LENGTH': len(payload), 733 'wsgi.input': payload}) 734 735 # Use specific upload handlers 736 upload_handlers = ( 737 'django.core.files.uploadhandler.MemoryFileUploadHandler', 738 'django.core.files.uploadhandler.TemporaryFileUploadHandler', 739 ) 740 with override_upload_handler_methods(), self.settings( 741 FILE_UPLOAD_HANDLERS=upload_handlers, 742 ): 743 self.assertEqual( 744 request.POST, 745 { 746 'n1': ['value'], 747 'n3': ['value'], 748 }, 749 ) 750 self.assertEqual(len(request.FILES), 1) 751 self.assertIn('n2', request.FILES) 752 753 # Confirm the variables have been collected: 754 self.assertEqual( 755 collected_variables_memory_handler, 756 [('n1', 'value'), ('n3', 'value')], 757 ) 758 self.assertEqual( 759 collected_variables_temporary_handler, 760 [('n1', 'value'), ('n3', 'value')], 761 ) 762 662 763 663 764 @skipIf(connection.vendor == 'sqlite' 664 765 and connection.settings_dict['NAME'] in ('', ':memory:'),