'Encodes a set of parameters (typically a name/value list) and
a set of files (a list of (name, filename, file_body)) into a
typical POST body, returning the (content_type, body).'

def encode_multipart(self, params, files):

    boundary = ('----------a_BoUnDaRy%s$' % random.random())
    content_type = ('multipart/form-data;    boundary=%s' % boundary)
    if six.PY3:
        boundary = boundary.encode('ascii')
    lines = []
    for (key, value) in params:
        lines.append(('--' + boundary))
        line = ('Content-Disposition:    form-data;    name="%s"' % key)
        if six.PY3:
            line = line.encode('utf8')
        lines.append(line)
        lines.append('')
        line = value
        if (six.PY3 and isinstance(line, six.text_type)):
            line = line.encode('utf8')
        lines.append(line)
    for file_info in files:
        (key, filename, value) = self._get_file_info(file_info)
        lines.append(('--' + boundary))
        line = ('Content-Disposition:    form-data;    name="%s";    filename="%s"' % (key, filename))
        if six.PY3:
            line = line.encode('utf8')
        lines.append(line)
        fcontent = mimetypes.guess_type(filename)[0]
        line = ('Content-Type:    %s' % (fcontent or 'application/octet-stream'))
        if six.PY3:
            line = line.encode('utf8')
        lines.append(line)
        lines.append('')
        lines.append(value)
    lines.append((('--' + boundary) + '--'))
    lines.append('')
    body = '\r\n'.join(lines)
    return (content_type, body)
