OLD | NEW |
1 # urllib3/filepost.py | 1 # urllib3/filepost.py |
2 # Copyright 2008-2012 Andrey Petrov and contributors (see CONTRIBUTORS.txt) | 2 # Copyright 2008-2013 Andrey Petrov and contributors (see CONTRIBUTORS.txt) |
3 # | 3 # |
4 # This module is part of urllib3 and is released under | 4 # This module is part of urllib3 and is released under |
5 # the MIT License: http://www.opensource.org/licenses/mit-license.php | 5 # the MIT License: http://www.opensource.org/licenses/mit-license.php |
6 | 6 |
7 import codecs | 7 import codecs |
8 import mimetypes | 8 import mimetypes |
9 | 9 |
10 from uuid import uuid4 | 10 from uuid import uuid4 |
11 from io import BytesIO | 11 from io import BytesIO |
12 | 12 |
13 from .packages import six | 13 from .packages import six |
14 from .packages.six import b | 14 from .packages.six import b |
| 15 from .fields import RequestField |
15 | 16 |
16 writer = codecs.lookup('utf-8')[3] | 17 writer = codecs.lookup('utf-8')[3] |
17 | 18 |
18 | 19 |
19 def choose_boundary(): | 20 def choose_boundary(): |
20 """ | 21 """ |
21 Our embarassingly-simple replacement for mimetools.choose_boundary. | 22 Our embarassingly-simple replacement for mimetools.choose_boundary. |
22 """ | 23 """ |
23 return uuid4().hex | 24 return uuid4().hex |
24 | 25 |
25 | 26 |
26 def get_content_type(filename): | 27 def iter_field_objects(fields): |
27 return mimetypes.guess_type(filename)[0] or 'application/octet-stream' | 28 """ |
| 29 Iterate over fields. |
| 30 |
| 31 Supports list of (k, v) tuples and dicts, and lists of |
| 32 :class:`~urllib3.fields.RequestField`. |
| 33 |
| 34 """ |
| 35 if isinstance(fields, dict): |
| 36 i = six.iteritems(fields) |
| 37 else: |
| 38 i = iter(fields) |
| 39 |
| 40 for field in i: |
| 41 if isinstance(field, RequestField): |
| 42 yield field |
| 43 else: |
| 44 yield RequestField.from_tuples(*field) |
28 | 45 |
29 | 46 |
30 def iter_fields(fields): | 47 def iter_fields(fields): |
31 """ | 48 """ |
32 Iterate over fields. | 49 Iterate over fields. |
33 | 50 |
| 51 .. deprecated :: |
| 52 |
| 53 The addition of `~urllib3.fields.RequestField` makes this function |
| 54 obsolete. Instead, use :func:`iter_field_objects`, which returns |
| 55 `~urllib3.fields.RequestField` objects, instead. |
| 56 |
34 Supports list of (k, v) tuples and dicts. | 57 Supports list of (k, v) tuples and dicts. |
| 58 |
35 """ | 59 """ |
36 if isinstance(fields, dict): | 60 if isinstance(fields, dict): |
37 return ((k, v) for k, v in six.iteritems(fields)) | 61 return ((k, v) for k, v in six.iteritems(fields)) |
38 | 62 |
39 return ((k, v) for k, v in fields) | 63 return ((k, v) for k, v in fields) |
40 | 64 |
41 | 65 |
42 def encode_multipart_formdata(fields, boundary=None): | 66 def encode_multipart_formdata(fields, boundary=None): |
43 """ | 67 """ |
44 Encode a dictionary of ``fields`` using the multipart/form-data MIME format. | 68 Encode a dictionary of ``fields`` using the multipart/form-data MIME format. |
45 | 69 |
46 :param fields: | 70 :param fields: |
47 Dictionary of fields or list of (key, value) or (key, value, MIME type) | 71 Dictionary of fields or list of (key, :class:`~urllib3.fields.RequestFie
ld`). |
48 field tuples. The key is treated as the field name, and the value as | |
49 the body of the form-data bytes. If the value is a tuple of two | |
50 elements, then the first element is treated as the filename of the | |
51 form-data section and a suitable MIME type is guessed based on the | |
52 filename. If the value is a tuple of three elements, then the third | |
53 element is treated as an explicit MIME type of the form-data section. | |
54 | |
55 Field names and filenames must be unicode. | |
56 | 72 |
57 :param boundary: | 73 :param boundary: |
58 If not specified, then a random boundary will be generated using | 74 If not specified, then a random boundary will be generated using |
59 :func:`mimetools.choose_boundary`. | 75 :func:`mimetools.choose_boundary`. |
60 """ | 76 """ |
61 body = BytesIO() | 77 body = BytesIO() |
62 if boundary is None: | 78 if boundary is None: |
63 boundary = choose_boundary() | 79 boundary = choose_boundary() |
64 | 80 |
65 for fieldname, value in iter_fields(fields): | 81 for field in iter_field_objects(fields): |
66 body.write(b('--%s\r\n' % (boundary))) | 82 body.write(b('--%s\r\n' % (boundary))) |
67 | 83 |
68 if isinstance(value, tuple): | 84 writer(body).write(field.render_headers()) |
69 if len(value) == 3: | 85 data = field.data |
70 filename, data, content_type = value | |
71 else: | |
72 filename, data = value | |
73 content_type = get_content_type(filename) | |
74 writer(body).write('Content-Disposition: form-data; name="%s"; ' | |
75 'filename="%s"\r\n' % (fieldname, filename)) | |
76 body.write(b('Content-Type: %s\r\n\r\n' % | |
77 (content_type,))) | |
78 else: | |
79 data = value | |
80 writer(body).write('Content-Disposition: form-data; name="%s"\r\n' | |
81 % (fieldname)) | |
82 body.write(b'\r\n') | |
83 | 86 |
84 if isinstance(data, int): | 87 if isinstance(data, int): |
85 data = str(data) # Backwards compatibility | 88 data = str(data) # Backwards compatibility |
86 | 89 |
87 if isinstance(data, six.text_type): | 90 if isinstance(data, six.text_type): |
88 writer(body).write(data) | 91 writer(body).write(data) |
89 else: | 92 else: |
90 body.write(data) | 93 body.write(data) |
91 | 94 |
92 body.write(b'\r\n') | 95 body.write(b'\r\n') |
93 | 96 |
94 body.write(b('--%s--\r\n' % (boundary))) | 97 body.write(b('--%s--\r\n' % (boundary))) |
95 | 98 |
96 content_type = str('multipart/form-data; boundary=%s' % boundary) | 99 content_type = str('multipart/form-data; boundary=%s' % boundary) |
97 | 100 |
98 return body.getvalue(), content_type | 101 return body.getvalue(), content_type |
OLD | NEW |