From 614741247f46ce7854a655cff9bf7aa545a1414c Mon Sep 17 00:00:00 2001 From: Marcelo Trylesinski Date: Fri, 10 Apr 2026 11:19:08 +0200 Subject: [PATCH] Add UPLOAD_DELETE_TMP to FormParser config --- python_multipart/multipart.py | 21 +++++++++------------ tests/test_multipart.py | 25 +++++++++++++++++++++++++ 2 files changed, 34 insertions(+), 12 deletions(-) diff --git a/python_multipart/multipart.py b/python_multipart/multipart.py index 0e7634d..00d57a7 100644 --- a/python_multipart/multipart.py +++ b/python_multipart/multipart.py @@ -44,14 +44,6 @@ class MultipartCallbacks(TypedDict, total=False): on_headers_finished: Callable[[], None] on_end: Callable[[], None] - class FormParserConfig(TypedDict): - UPLOAD_DIR: str | None - UPLOAD_KEEP_FILENAME: bool - UPLOAD_KEEP_EXTENSIONS: bool - UPLOAD_ERROR_ON_BAD_CTE: bool - MAX_MEMORY_FILE_SIZE: int - MAX_BODY_SIZE: float - class FileConfig(TypedDict, total=False): UPLOAD_DIR: str | bytes | None UPLOAD_DELETE_TMP: bool @@ -59,6 +51,10 @@ class FileConfig(TypedDict, total=False): UPLOAD_KEEP_EXTENSIONS: bool MAX_MEMORY_FILE_SIZE: int + class FormParserConfig(FileConfig): + UPLOAD_ERROR_ON_BAD_CTE: bool + MAX_BODY_SIZE: float + class _FormProtocol(Protocol): def write(self, data: bytes) -> int: ... @@ -1488,8 +1484,8 @@ class FormParser: information about the uploaded file. In such cases, you can provide the file name of the uploaded file manually. FileClass: The class to use for uploaded files. Defaults to :class:`File`, but you can provide your own class - if you wish to customize behaviour. The class will be instantiated as FileClass(file_name, field_name), and - it must provide the following functions:: + if you wish to customize behaviour. The class will be instantiated as + FileClass(file_name, field_name, config=config), and it must provide the following functions:: - file_instance.write(data) - file_instance.finalize() - file_instance.close() @@ -1510,6 +1506,7 @@ class if you wish to customize behaviour. The class will be instantiated as Fie "MAX_BODY_SIZE": float("inf"), "MAX_MEMORY_FILE_SIZE": 1 * 1024 * 1024, "UPLOAD_DIR": None, + "UPLOAD_DELETE_TMP": True, "UPLOAD_KEEP_FILENAME": False, "UPLOAD_KEEP_EXTENSIONS": False, # Error on invalid Content-Transfer-Encoding? @@ -1557,7 +1554,7 @@ def __init__( def on_start() -> None: nonlocal file - file = FileClass(file_name, None, config=cast("FileConfig", self.config)) + file = FileClass(file_name, None, config=self.config) def on_data(data: bytes, start: int, end: int) -> None: nonlocal file @@ -1695,7 +1692,7 @@ def on_headers_finished() -> None: if file_name is None: f_multi = FieldClass(field_name) else: - f_multi = FileClass(file_name, field_name, config=cast("FileConfig", self.config)) + f_multi = FileClass(file_name, field_name, config=self.config) is_file = True # Parse the given Content-Transfer-Encoding to determine what diff --git a/tests/test_multipart.py b/tests/test_multipart.py index 2817e41..9a9cbee 100644 --- a/tests/test_multipart.py +++ b/tests/test_multipart.py @@ -885,6 +885,31 @@ def test_random_splitting(self) -> None: self.assert_field(b"field", b"test1") self.assert_file(b"file", b"file.txt", b"test2") + def test_upload_delete_tmp_config(self) -> None: + with tempfile.TemporaryDirectory() as upload_dir: + self.make( + "----WebKitFormBoundary5BZGOJCWtXGYC9HW", + config={"UPLOAD_DIR": upload_dir, "UPLOAD_DELETE_TMP": False, "MAX_MEMORY_FILE_SIZE": 1}, + ) + + test_file = "single_file.http" + with open(os.path.join(http_tests_dir, test_file), "rb") as f: + test_data = f.read() + + self.f.write(test_data) + self.f.finalize() + + self.assertEqual(len(self.files), 1) + uploaded_file = self.files[0] + assert uploaded_file.actual_file_name is not None + actual_file_name = uploaded_file.actual_file_name.decode(sys.getfilesystemencoding()) + uploaded_file.close() + + try: + self.assertTrue(os.path.exists(actual_file_name)) + finally: + os.unlink(actual_file_name) + @parametrize("param", [t for t in http_tests if t["name"] in single_byte_tests]) def test_feed_single_bytes(self, param: TestParams) -> None: """