diff options
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 | 
