diff options
| author | James Rutherford | 2015-03-11 10:38:03 +0000 |
|---|---|---|
| committer | James Rutherford | 2015-03-11 10:38:03 +0000 |
| commit | 4a2d27975ab5249269aebafd803be87a2107092b (patch) | |
| tree | 55b524c93b02eef404304f734be98871bbb1324f /rest_framework/parsers.py | |
| parent | 856dc855c952746f566a6a8de263afe951362dfb (diff) | |
| parent | dc56e5a0f41fdd6350e91a5749023d086bd1640f (diff) | |
| download | django-rest-framework-4a2d27975ab5249269aebafd803be87a2107092b.tar.bz2 | |
Merge pull request #1 from tomchristie/master
Merge in from upstream
Diffstat (limited to 'rest_framework/parsers.py')
| -rw-r--r-- | rest_framework/parsers.py | 149 |
1 files changed, 37 insertions, 112 deletions
diff --git a/rest_framework/parsers.py b/rest_framework/parsers.py index 96bfac84..437d1339 100644 --- a/rest_framework/parsers.py +++ b/rest_framework/parsers.py @@ -5,17 +5,18 @@ They give us a generic way of being able to handle various media types on the request, such as form content or json encoded data. """ from __future__ import unicode_literals + from django.conf import settings from django.core.files.uploadhandler import StopFutureHandlers from django.http import QueryDict from django.http.multipartparser import MultiPartParser as DjangoMultiPartParser from django.http.multipartparser import MultiPartParserError, parse_header, ChunkIter -from rest_framework.compat import yaml, etree +from django.utils import six +from django.utils.six.moves.urllib import parse as urlparse +from django.utils.encoding import force_text from rest_framework.exceptions import ParseError -from rest_framework.compat import six +from rest_framework import renderers import json -import datetime -import decimal class DataAndFiles(object): @@ -47,6 +48,7 @@ class JSONParser(BaseParser): """ media_type = 'application/json' + renderer_class = renderers.JSONRenderer def parse(self, stream, media_type=None, parser_context=None): """ @@ -62,29 +64,6 @@ class JSONParser(BaseParser): raise ParseError('JSON parse error - %s' % six.text_type(exc)) -class YAMLParser(BaseParser): - """ - Parses YAML-serialized data. - """ - - media_type = 'application/yaml' - - def parse(self, stream, media_type=None, parser_context=None): - """ - Parses the incoming bytestream as YAML and returns the resulting data. - """ - assert yaml, 'YAMLParser requires pyyaml to be installed' - - parser_context = parser_context or {} - encoding = parser_context.get('encoding', settings.DEFAULT_CHARSET) - - try: - data = stream.read().decode(encoding) - return yaml.safe_load(data) - except (ValueError, yaml.parser.ParserError) as exc: - raise ParseError('YAML parse error - %s' % six.u(exc)) - - class FormParser(BaseParser): """ Parser for form data. @@ -121,7 +100,8 @@ class MultiPartParser(BaseParser): parser_context = parser_context or {} request = parser_context['request'] encoding = parser_context.get('encoding', settings.DEFAULT_CHARSET) - meta = request.META + meta = request.META.copy() + meta['CONTENT_TYPE'] = media_type upload_handlers = request.upload_handlers try: @@ -129,79 +109,7 @@ class MultiPartParser(BaseParser): data, files = parser.parse() return DataAndFiles(data, files) except MultiPartParserError as exc: - raise ParseError('Multipart form parse error - %s' % six.u(exc)) - - -class XMLParser(BaseParser): - """ - XML parser. - """ - - media_type = 'application/xml' - - def parse(self, stream, media_type=None, parser_context=None): - """ - Parses the incoming bytestream as XML and returns the resulting data. - """ - assert etree, 'XMLParser requires defusedxml to be installed' - - parser_context = parser_context or {} - encoding = parser_context.get('encoding', settings.DEFAULT_CHARSET) - parser = etree.DefusedXMLParser(encoding=encoding) - try: - tree = etree.parse(stream, parser=parser, forbid_dtd=True) - except (etree.ParseError, ValueError) as exc: - raise ParseError('XML parse error - %s' % six.u(exc)) - data = self._xml_convert(tree.getroot()) - - return data - - def _xml_convert(self, element): - """ - convert the xml `element` into the corresponding python object - """ - - children = list(element) - - if len(children) == 0: - return self._type_convert(element.text) - else: - # if the fist child tag is list-item means all children are list-item - if children[0].tag == "list-item": - data = [] - for child in children: - data.append(self._xml_convert(child)) - else: - data = {} - for child in children: - data[child.tag] = self._xml_convert(child) - - return data - - def _type_convert(self, value): - """ - Converts the value returned by the XMl parse into the equivalent - Python type - """ - if value is None: - return value - - try: - return datetime.datetime.strptime(value, '%Y-%m-%d %H:%M:%S') - except ValueError: - pass - - try: - return int(value) - except ValueError: - pass - - try: - return decimal.Decimal(value) - except decimal.InvalidOperation: - pass - - return value + raise ParseError('Multipart form parse error - %s' % six.text_type(exc)) class FileUploadParser(BaseParser): @@ -244,7 +152,7 @@ class FileUploadParser(BaseParser): None, encoding) if result is not None: - return DataAndFiles(None, {'file': result[1]}) + return DataAndFiles({}, {'file': result[1]}) # This is the standard case. possible_sizes = [x.chunk_size for x in upload_handlers if x.chunk_size] @@ -252,25 +160,26 @@ class FileUploadParser(BaseParser): chunks = ChunkIter(stream, chunk_size) counters = [0] * len(upload_handlers) - for handler in upload_handlers: + for index, handler in enumerate(upload_handlers): try: handler.new_file(None, filename, content_type, content_length, encoding) except StopFutureHandlers: + upload_handlers = upload_handlers[:index + 1] break for chunk in chunks: - for i, handler in enumerate(upload_handlers): + for index, handler in enumerate(upload_handlers): chunk_length = len(chunk) - chunk = handler.receive_data_chunk(chunk, counters[i]) - counters[i] += chunk_length + chunk = handler.receive_data_chunk(chunk, counters[index]) + counters[index] += chunk_length if chunk is None: break - for i, handler in enumerate(upload_handlers): - file_obj = handler.file_complete(counters[i]) + for index, handler in enumerate(upload_handlers): + file_obj = handler.file_complete(counters[index]) if file_obj: - return DataAndFiles(None, {'file': file_obj}) + return DataAndFiles({}, {'file': file_obj}) raise ParseError("FileUpload parse error - " "none of upload handlers can handle the stream") @@ -286,7 +195,23 @@ class FileUploadParser(BaseParser): try: meta = parser_context['request'].META - disposition = parse_header(meta['HTTP_CONTENT_DISPOSITION']) - return disposition[1]['filename'] - except (AttributeError, KeyError): + disposition = parse_header(meta['HTTP_CONTENT_DISPOSITION'].encode('utf-8')) + filename_parm = disposition[1] + if 'filename*' in filename_parm: + return self.get_encoded_filename(filename_parm) + return force_text(filename_parm['filename']) + except (AttributeError, KeyError, ValueError): pass + + def get_encoded_filename(self, filename_parm): + """ + Handle encoded filenames per RFC6266. See also: + http://tools.ietf.org/html/rfc2231#section-4 + """ + encoded_filename = force_text(filename_parm['filename*']) + try: + charset, lang, filename = encoded_filename.split('\'', 2) + filename = urlparse.unquote(filename) + except (ValueError, LookupError): + filename = force_text(filename_parm['filename']) + return filename |
