1from __future__ import absolute_import
2
3import binascii
4import codecs
5import os
6from io import BytesIO
7
8from .fields import RequestField
9from .packages import six
10from .packages.six import b
11
12writer = codecs.lookup("utf-8")[3]
13
14
15def choose_boundary():
16    """
17    Our embarrassingly-simple replacement for mimetools.choose_boundary.
18    """
19    boundary = binascii.hexlify(os.urandom(16))
20    if not six.PY2:
21        boundary = boundary.decode("ascii")
22    return boundary
23
24
25def iter_field_objects(fields):
26    """
27    Iterate over fields.
28
29    Supports list of (k, v) tuples and dicts, and lists of
30    :class:`~urllib3.fields.RequestField`.
31
32    """
33    if isinstance(fields, dict):
34        i = six.iteritems(fields)
35    else:
36        i = iter(fields)
37
38    for field in i:
39        if isinstance(field, RequestField):
40            yield field
41        else:
42            yield RequestField.from_tuples(*field)
43
44
45def iter_fields(fields):
46    """
47    .. deprecated:: 1.6
48
49    Iterate over fields.
50
51    The addition of :class:`~urllib3.fields.RequestField` makes this function
52    obsolete. Instead, use :func:`iter_field_objects`, which returns
53    :class:`~urllib3.fields.RequestField` objects.
54
55    Supports list of (k, v) tuples and dicts.
56    """
57    if isinstance(fields, dict):
58        return ((k, v) for k, v in six.iteritems(fields))
59
60    return ((k, v) for k, v in fields)
61
62
63def encode_multipart_formdata(fields, boundary=None):
64    """
65    Encode a dictionary of ``fields`` using the multipart/form-data MIME format.
66
67    :param fields:
68        Dictionary of fields or list of (key, :class:`~urllib3.fields.RequestField`).
69
70    :param boundary:
71        If not specified, then a random boundary will be generated using
72        :func:`urllib3.filepost.choose_boundary`.
73    """
74    body = BytesIO()
75    if boundary is None:
76        boundary = choose_boundary()
77
78    for field in iter_field_objects(fields):
79        body.write(b("--%s\r\n" % (boundary)))
80
81        writer(body).write(field.render_headers())
82        data = field.data
83
84        if isinstance(data, int):
85            data = str(data)  # Backwards compatibility
86
87        if isinstance(data, six.text_type):
88            writer(body).write(data)
89        else:
90            body.write(data)
91
92        body.write(b"\r\n")
93
94    body.write(b("--%s--\r\n" % (boundary)))
95
96    content_type = str("multipart/form-data; boundary=%s" % boundary)
97
98    return body.getvalue(), content_type
99