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