Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/urllib3/filepost.py: 35%

37 statements  

« prev     ^ index     » next       coverage.py v7.3.2, created at 2023-12-08 06:40 +0000

1from __future__ import annotations 

2 

3import binascii 

4import codecs 

5import os 

6import typing 

7from io import BytesIO 

8 

9from .fields import _TYPE_FIELD_VALUE_TUPLE, RequestField 

10 

11writer = codecs.lookup("utf-8")[3] 

12 

13_TYPE_FIELDS_SEQUENCE = typing.Sequence[ 

14 typing.Union[typing.Tuple[str, _TYPE_FIELD_VALUE_TUPLE], RequestField] 

15] 

16_TYPE_FIELDS = typing.Union[ 

17 _TYPE_FIELDS_SEQUENCE, 

18 typing.Mapping[str, _TYPE_FIELD_VALUE_TUPLE], 

19] 

20 

21 

22def choose_boundary() -> str: 

23 """ 

24 Our embarrassingly-simple replacement for mimetools.choose_boundary. 

25 """ 

26 return binascii.hexlify(os.urandom(16)).decode() 

27 

28 

29def iter_field_objects(fields: _TYPE_FIELDS) -> typing.Iterable[RequestField]: 

30 """ 

31 Iterate over fields. 

32 

33 Supports list of (k, v) tuples and dicts, and lists of 

34 :class:`~urllib3.fields.RequestField`. 

35 

36 """ 

37 iterable: typing.Iterable[RequestField | tuple[str, _TYPE_FIELD_VALUE_TUPLE]] 

38 

39 if isinstance(fields, typing.Mapping): 

40 iterable = fields.items() 

41 else: 

42 iterable = fields 

43 

44 for field in iterable: 

45 if isinstance(field, RequestField): 

46 yield field 

47 else: 

48 yield RequestField.from_tuples(*field) 

49 

50 

51def encode_multipart_formdata( 

52 fields: _TYPE_FIELDS, boundary: str | None = None 

53) -> tuple[bytes, str]: 

54 """ 

55 Encode a dictionary of ``fields`` using the multipart/form-data MIME format. 

56 

57 :param fields: 

58 Dictionary of fields or list of (key, :class:`~urllib3.fields.RequestField`). 

59 Values are processed by :func:`urllib3.fields.RequestField.from_tuples`. 

60 

61 :param boundary: 

62 If not specified, then a random boundary will be generated using 

63 :func:`urllib3.filepost.choose_boundary`. 

64 """ 

65 body = BytesIO() 

66 if boundary is None: 

67 boundary = choose_boundary() 

68 

69 for field in iter_field_objects(fields): 

70 body.write(f"--{boundary}\r\n".encode("latin-1")) 

71 

72 writer(body).write(field.render_headers()) 

73 data = field.data 

74 

75 if isinstance(data, int): 

76 data = str(data) # Backwards compatibility 

77 

78 if isinstance(data, str): 

79 writer(body).write(data) 

80 else: 

81 body.write(data) 

82 

83 body.write(b"\r\n") 

84 

85 body.write(f"--{boundary}--\r\n".encode("latin-1")) 

86 

87 content_type = f"multipart/form-data; boundary={boundary}" 

88 

89 return body.getvalue(), content_type