From ef0c3214754f124447b6855ee821df7659ba45f5 Mon Sep 17 00:00:00 2001 From: yecril23pl <151100823+yecril23pl@users.noreply.github.com> Date: Sat, 24 Feb 2024 21:21:01 +0100 Subject: [PATCH 1/9] If the input stream is a text stream, use its binary buffer --- multipart/multipart.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/multipart/multipart.py b/multipart/multipart.py index 170151f..0bed6ab 100644 --- a/multipart/multipart.py +++ b/multipart/multipart.py @@ -1937,6 +1937,10 @@ def parse_form( content_length = float("inf") bytes_read = 0 + # If the input stream is a text stream, use its binary buffer + if isinstance (input_stream, type (sys. stdin)): + input_stream = input_stream .buffer + while True: # Read only up to the Content-Length given. max_readable = min(content_length - bytes_read, 1048576) From da1c57195e294ccc863748c58d95540c3370d8b7 Mon Sep 17 00:00:00 2001 From: yecril23pl <151100823+yecril23pl@users.noreply.github.com> Date: Thu, 29 Feb 2024 14:24:50 +0100 Subject: [PATCH 2/9] test parse_form accepts a TextIOWrapper --- tests/test_multipart.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/tests/test_multipart.py b/tests/test_multipart.py index 3a814fb..5004056 100644 --- a/tests/test_multipart.py +++ b/tests/test_multipart.py @@ -5,7 +5,7 @@ import sys import tempfile import unittest -from io import BytesIO +from io import BytesIO, TextIOWrapper from typing import TYPE_CHECKING from unittest.mock import Mock @@ -1280,6 +1280,14 @@ def test_parse_form(self): # 15 - i.e. all data is written. self.assertEqual(on_file.call_args[0][0].size, 15) + parse_form({"Content-Type": "application/octet-stream"}, TextIOWrapper(BytesIO(b"123456789012345")), on_field, on_file) + + assert on_file.call_count == 1 + + # Assert that the first argument of the call (a File object) has size + # 15 - i.e. all data is written. + self.assertEqual(on_file.call_args[0][0].size, 15) + def test_parse_form_content_length(self): files = [] From 4c33dd3d575ec3e9d1d3ca167442cb665b45bda3 Mon Sep 17 00:00:00 2001 From: yecril23pl <151100823+yecril23pl@users.noreply.github.com> Date: Thu, 29 Feb 2024 14:37:16 +0100 Subject: [PATCH 3/9] more TextIOWrapper tests --- tests/test_multipart.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/tests/test_multipart.py b/tests/test_multipart.py index 5004056..80ab0dd 100644 --- a/tests/test_multipart.py +++ b/tests/test_multipart.py @@ -1280,6 +1280,16 @@ def test_parse_form(self): # 15 - i.e. all data is written. self.assertEqual(on_file.call_args[0][0].size, 15) + parse_form({"Content-Type": "application/octet-stream"}, TextIOWrapper(BytesIO(b"123456789012345")) .buffer, on_field, on_file) + + assert on_file.call_count == 1 + + # Assert that the first argument of the call (a File object) has size + # 15 - i.e. all data is written. + self.assertEqual(on_file.call_args[0][0].size, 15) + + self .assertTrue (isinstance (TextIOWrapper(BytesIO(b"123456789012345")), type (sys .stdin))) + parse_form({"Content-Type": "application/octet-stream"}, TextIOWrapper(BytesIO(b"123456789012345")), on_field, on_file) assert on_file.call_count == 1 From 3b936bd95f33743810bc951c336109fd434ce4ce Mon Sep 17 00:00:00 2001 From: yecril23pl <151100823+yecril23pl@users.noreply.github.com> Date: Thu, 29 Feb 2024 15:00:01 +0100 Subject: [PATCH 4/9] test_parse_form: use a true text input file because TextIOWrapper is crazy --- tests/test_multipart.py | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) diff --git a/tests/test_multipart.py b/tests/test_multipart.py index 80ab0dd..e4510d1 100644 --- a/tests/test_multipart.py +++ b/tests/test_multipart.py @@ -1280,17 +1280,10 @@ def test_parse_form(self): # 15 - i.e. all data is written. self.assertEqual(on_file.call_args[0][0].size, 15) - parse_form({"Content-Type": "application/octet-stream"}, TextIOWrapper(BytesIO(b"123456789012345")) .buffer, on_field, on_file) - - assert on_file.call_count == 1 - - # Assert that the first argument of the call (a File object) has size - # 15 - i.e. all data is written. - self.assertEqual(on_file.call_args[0][0].size, 15) - - self .assertTrue (isinstance (TextIOWrapper(BytesIO(b"123456789012345")), type (sys .stdin))) - - parse_form({"Content-Type": "application/octet-stream"}, TextIOWrapper(BytesIO(b"123456789012345")), on_field, on_file) + with f as open ('12345678.txt', 'wt+'): + f .write ('123456789012345') + f .seek (0o0) + parse_form({"Content-Type": "application/octet-stream"}, f, on_field, on_file) assert on_file.call_count == 1 From e47d869c8b3f07b0d917e0b19125c9e0c8fa9564 Mon Sep 17 00:00:00 2001 From: yecril23pl <151100823+yecril23pl@users.noreply.github.com> Date: Thu, 29 Feb 2024 15:03:19 +0100 Subject: [PATCH 5/9] fix syntax --- tests/test_multipart.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/test_multipart.py b/tests/test_multipart.py index e4510d1..dd90982 100644 --- a/tests/test_multipart.py +++ b/tests/test_multipart.py @@ -5,7 +5,7 @@ import sys import tempfile import unittest -from io import BytesIO, TextIOWrapper +from io import BytesIO from typing import TYPE_CHECKING from unittest.mock import Mock @@ -1280,7 +1280,7 @@ def test_parse_form(self): # 15 - i.e. all data is written. self.assertEqual(on_file.call_args[0][0].size, 15) - with f as open ('12345678.txt', 'wt+'): + with open ('12345678.txt', 'wt+') as f: f .write ('123456789012345') f .seek (0o0) parse_form({"Content-Type": "application/octet-stream"}, f, on_field, on_file) From 70d8a27109b9c07a4fd78daa809f31d15afc7ce6 Mon Sep 17 00:00:00 2001 From: yecril23pl <151100823+yecril23pl@users.noreply.github.com> Date: Thu, 29 Feb 2024 15:12:47 +0100 Subject: [PATCH 6/9] test_parse_form: test FileIO --- tests/test_multipart.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/tests/test_multipart.py b/tests/test_multipart.py index dd90982..3de77e3 100644 --- a/tests/test_multipart.py +++ b/tests/test_multipart.py @@ -1283,6 +1283,16 @@ def test_parse_form(self): with open ('12345678.txt', 'wt+') as f: f .write ('123456789012345') f .seek (0o0) + parse_form({"Content-Type": "application/octet-stream"}, f .buffer, on_field, on_file) + + assert on_file.call_count == 1 + + # Assert that the first argument of the call (a File object) has size + # 15 - i.e. all data is written. + self.assertEqual(on_file.call_args[0][0].size, 15) + + with open ('12345678.txt', 'r') as f: + self .assertTrue (isinstance (f, type (sys .stdin))) parse_form({"Content-Type": "application/octet-stream"}, f, on_field, on_file) assert on_file.call_count == 1 From 6abce1157cab692debe02f64478dda5ba8dd385a Mon Sep 17 00:00:00 2001 From: yecril23pl <151100823+yecril23pl@users.noreply.github.com> Date: Thu, 29 Feb 2024 15:21:06 +0100 Subject: [PATCH 7/9] split parse_form tests --- tests/test_multipart.py | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/tests/test_multipart.py b/tests/test_multipart.py index 3de77e3..67c5132 100644 --- a/tests/test_multipart.py +++ b/tests/test_multipart.py @@ -1268,7 +1268,7 @@ def test_create_form_parser_error(self): with self.assertRaises(ValueError): create_form_parser(headers, None, None) - def test_parse_form(self): + def test_parse_form_bytes(self): on_field = Mock() on_file = Mock() @@ -1280,6 +1280,10 @@ def test_parse_form(self): # 15 - i.e. all data is written. self.assertEqual(on_file.call_args[0][0].size, 15) + def test_parse_form_file(self): + on_field = Mock() + on_file = Mock() + with open ('12345678.txt', 'wt+') as f: f .write ('123456789012345') f .seek (0o0) @@ -1291,7 +1295,13 @@ def test_parse_form(self): # 15 - i.e. all data is written. self.assertEqual(on_file.call_args[0][0].size, 15) - with open ('12345678.txt', 'r') as f: + def test_parse_form_text(self): + on_field = Mock() + on_file = Mock() + + with open ('12345678.txt', 'wt+') as f: + f .write ('123456789012345') + f .seek (0o0) self .assertTrue (isinstance (f, type (sys .stdin))) parse_form({"Content-Type": "application/octet-stream"}, f, on_field, on_file) From 63f3a62b2f4f2165eb98fba5cb081b333cb31f26 Mon Sep 17 00:00:00 2001 From: yecril23pl <151100823+yecril23pl@users.noreply.github.com> Date: Thu, 29 Feb 2024 15:28:05 +0100 Subject: [PATCH 8/9] input buffer: test for TextIOBase --- tests/test_multipart.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/test_multipart.py b/tests/test_multipart.py index 67c5132..6df98a2 100644 --- a/tests/test_multipart.py +++ b/tests/test_multipart.py @@ -5,7 +5,7 @@ import sys import tempfile import unittest -from io import BytesIO +from io import BytesIO, TextIOBase from typing import TYPE_CHECKING from unittest.mock import Mock @@ -1302,7 +1302,7 @@ def test_parse_form_text(self): with open ('12345678.txt', 'wt+') as f: f .write ('123456789012345') f .seek (0o0) - self .assertTrue (isinstance (f, type (sys .stdin))) + self .assertTrue (isinstance (f, TextIOBase)) parse_form({"Content-Type": "application/octet-stream"}, f, on_field, on_file) assert on_file.call_count == 1 From 100fad414669ebf0b9df5b690dc995e9ac6a623d Mon Sep 17 00:00:00 2001 From: yecril23pl <151100823+yecril23pl@users.noreply.github.com> Date: Thu, 29 Feb 2024 15:31:14 +0100 Subject: [PATCH 9/9] case input stream is instance of TextIOBase --- multipart/multipart.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/multipart/multipart.py b/multipart/multipart.py index 0bed6ab..2c26b62 100644 --- a/multipart/multipart.py +++ b/multipart/multipart.py @@ -1938,7 +1938,7 @@ def parse_form( bytes_read = 0 # If the input stream is a text stream, use its binary buffer - if isinstance (input_stream, type (sys. stdin)): + if isinstance (input_stream, io .TextIOBase): input_stream = input_stream .buffer while True: