text: # -*- coding: utf-8 -*-
  """
  
  requests_toolbelt.multipart.decoder
  ===================================
  
  This holds all the implementation details of the MultipartDecoder
  
  """
  
  import sys
  import email.parser
  from .encoder import encode_with
  from requests.structures import CaseInsensitiveDict
  
  
  def _split_on_find(content, bound):
      point = content.find(bound)
      return content[:point], content[point + len(bound):]
  
  
  class ImproperBodyPartContentException(Exception):
      pass
  
  
  class NonMultipartContentTypeException(Exception):
      pass
  
  
  def _header_parser(string, encoding):
      major = sys.version_info[0]
      if major == 3:
          string = string.decode(encoding)
      headers = email.parser.HeaderParser().parsestr(string).items()
      return (
          (encode_with(k, encoding), encode_with(v, encoding))
          for k, v in headers
      )
  
  
  class BodyPart(object):
      """
  
      The ``BodyPart`` object is a ``Response``-like interface to an individual
      subpart of a multipart response. It is expected that these will
      generally be created by objects of the ``MultipartDecoder`` class.
  
      Like ``Response``, there is a ``CaseInsensitiveDict`` object named headers,
      ``content`` to access bytes, ``text`` to access unicode, and ``encoding``
      to access the unicode codec.
  
      """
  
      def __init__(self, content, encoding):
          self.encoding = encoding
          headers = {}
          # Split into header section (if any) and the content
          if b'\r\n\r\n' in content:
              first, self.content = _split_on_find(content, b'\r\n\r\n')
              if first != b'':
                  headers = _header_parser(first.lstrip(), encoding)
          else:
              raise ImproperBodyPartContentException(
                  'content does not contain CR-LF-CR-LF'
              )
          self.headers = CaseInsensitiveDict(headers)
  
      @property
      def text(self):
          """Content of the ``BodyPart`` in unicode."""
          return self.content.decode(self.encoding)
  
  
  class MultipartDecoder(object):
      """
  
      The ``MultipartDecoder`` object parses the multipart payload of
      a bytestring into a tuple of ``Response``-like ``BodyPart`` objects.
  
      The basic usage is::
  
          import requests
          from requests_toolbelt import MultipartDecoder
  
          response = request.get(url)
          decoder = MultipartDecoder.from_response(response)
          for part in decoder.parts:
              print(part.headers['content-type'])
  
      If the multipart content is not from a response, basic usage is::
  
          from requests_toolbelt import MultipartDecoder
  
          decoder = MultipartDecoder(content, content_type)
          for part in decoder.parts:
              print(part.headers['content-type'])
  
      For both these usages, there is an optional ``encoding`` parameter. This is
      a string, which is the name of the unicode codec to use (default is
      ``'utf-8'``).
  
      """
      def __init__(self, content, content_type, encoding='utf-8'):
          #: Original Content-Type header
          self.content_type = content_type
          #: Response body encoding
          self.encoding = encoding
          #: Parsed parts of the multipart response body
          self.parts = tuple()
          self._find_boundary()
          self._parse_body(content)
  
      def _find_boundary(self):
          ct_info = tuple(x.strip() for x in self.content_type.split(';'))
          mimetype = ct_info[0]
          if mimetype.split('/')[0].lower() != 'multipart':
              raise NonMultipartContentTypeException(
                  "Unexpected mimetype in content-type: '{0}'".format(mimetype)
              )
          for item in ct_info[1:]:
              attr, value = _split_on_find(
                  item,
                  '='
              )
              if attr.lower() == 'boundary':
                  self.boundary = encode_with(value.strip('"'), self.encoding)
  
      @staticmethod
      def _fix_first_part(part, boundary_marker):
          bm_len = len(boundary_marker)
          if boundary_marker == part[:bm_len]:
              return part[bm_len:]
          else:
              return part
  
      def _parse_body(self, content):
          boundary = b''.join((b'--', self.boundary))
  
          def body_part(part):
              fixed = MultipartDecoder._fix_first_part(part, boundary)
              return BodyPart(fixed, self.encoding)
  
          def test_part(part):
              return (part != b'' and
                      part != b'\r\n' and
                      part[:4] != b'--\r\n' and
                      part != b'--')
  
          parts = content.split(b''.join((b'\r\n', boundary)))
          self.parts = tuple(body_part(x) for x in parts if test_part(x))
  
      @classmethod
      def from_response(cls, response, encoding='utf-8'):
          content = response.content
          content_type = response.headers.get('content-type', None)
          return cls(content, content_type, encoding)
  