From ace29525f042f9960a7429fc8bcdc520516d2f77 Mon Sep 17 00:00:00 2001 From: Mark Byrne Date: Tue, 26 May 2026 16:12:25 +0200 Subject: [PATCH 1/6] tempfile: Raise a ``ValueError`` if the ``prefix`` or ``suffix`` contains a directory component. gh-79459 --- Lib/tempfile.py | 4 ++++ Lib/test/test_tempfile.py | 26 ++++++++++++++++++++++++++ 2 files changed, 30 insertions(+) diff --git a/Lib/tempfile.py b/Lib/tempfile.py index ad81c5d3417b4c..27f66fb7474fcf 100644 --- a/Lib/tempfile.py +++ b/Lib/tempfile.py @@ -119,11 +119,15 @@ def _sanitize_params(prefix, suffix, dir): output_type = _infer_return_type(prefix, suffix, dir) if suffix is None: suffix = output_type() + if _os.path.dirname(suffix): + raise ValueError("'prefix' or 'suffix' can't contain a directory component") if prefix is None: if output_type is str: prefix = template else: prefix = _os.fsencode(template) + if _os.path.dirname(prefix): + raise ValueError("'prefix' or 'suffix' can't contain a directory component") if dir is None: if output_type is str: dir = gettempdir() diff --git a/Lib/test/test_tempfile.py b/Lib/test/test_tempfile.py index cd960ed99117b6..48e2da6313135e 100644 --- a/Lib/test/test_tempfile.py +++ b/Lib/test/test_tempfile.py @@ -2154,5 +2154,31 @@ def test_cleanup_safe(self): # platforms, but don't forget to update the docs. self.assertTrue(tempfile._rmtree_use_dir_fd) + +class TestPrefixAndSuffix(BaseTestCase): + def test_value_error_if_prefix_or_suffix_contains_directory(self): + MESSAGE = "'prefix' or 'suffix' can't contain a directory component" + + if os.altsep is None: + data = ( + ((os.sep), None), + (os.fsencode(os.sep), tempfile.gettempdirb()), + ) + else: + data = ( + ((os.altsep), None), + (os.fsencode(os.altsep), tempfile.gettempdirb()), + ) + + for value, directory in data: + with self.subTest((value, directory)): + with self.assertRaisesRegex(ValueError, MESSAGE): + tempfile.mkstemp(dir=directory, prefix=value) + with self.assertRaisesRegex(ValueError, MESSAGE): + os.rmdir(tempfile.mkdtemp(dir=directory, prefix=value)) + with self.assertRaisesRegex(ValueError, MESSAGE): + tempfile.NamedTemporaryFile(dir=directory, prefix=value, delete=True) + + if __name__ == "__main__": unittest.main() From 05394cd11d3bfb400a960a430c5ec745b08c12ba Mon Sep 17 00:00:00 2001 From: Mark Byrne Date: Tue, 26 May 2026 23:55:17 +0200 Subject: [PATCH 2/6] Use code-review suggestions. Co-authored-by: Victor Stinner --- Lib/tempfile.py | 4 +-- Lib/test/test_tempfile.py | 55 ++++++++++++++++++++++++++------------- 2 files changed, 39 insertions(+), 20 deletions(-) diff --git a/Lib/tempfile.py b/Lib/tempfile.py index 27f66fb7474fcf..22c0bd91dc8238 100644 --- a/Lib/tempfile.py +++ b/Lib/tempfile.py @@ -120,14 +120,14 @@ def _sanitize_params(prefix, suffix, dir): if suffix is None: suffix = output_type() if _os.path.dirname(suffix): - raise ValueError("'prefix' or 'suffix' can't contain a directory component") + raise ValueError("'suffix' can't contain a directory component") if prefix is None: if output_type is str: prefix = template else: prefix = _os.fsencode(template) if _os.path.dirname(prefix): - raise ValueError("'prefix' or 'suffix' can't contain a directory component") + raise ValueError("'prefix' can't contain a directory component") if dir is None: if output_type is str: dir = gettempdir() diff --git a/Lib/test/test_tempfile.py b/Lib/test/test_tempfile.py index 48e2da6313135e..f89a1e6ab2e2d5 100644 --- a/Lib/test/test_tempfile.py +++ b/Lib/test/test_tempfile.py @@ -2156,28 +2156,47 @@ def test_cleanup_safe(self): class TestPrefixAndSuffix(BaseTestCase): - def test_value_error_if_prefix_or_suffix_contains_directory(self): - MESSAGE = "'prefix' or 'suffix' can't contain a directory component" - - if os.altsep is None: - data = ( - ((os.sep), None), - (os.fsencode(os.sep), tempfile.gettempdirb()), - ) - else: - data = ( - ((os.altsep), None), - (os.fsencode(os.altsep), tempfile.gettempdirb()), - ) + DATA = ( + f"dir{os.sep}name", + f"{os.sep}abs_name", + os.fsencode(f"dir{os.sep}name"), + os.fsencode(f"{os.sep}abs_name"), + ) + if os.altsep is not None: + DATA += ( + f"dir{os.altsep}name", + f"{os.altsep}abs_name", + os.fsencode(f"dir{os.altsep}name"), + os.fsencode(f"{os.altsep}abs_name"), + ) + + def test_prefix_error(self): + MESSAGE = "'prefix' can't contain a directory component" + + for value in self.DATA: + with self.subTest((value)): + with self.assertRaisesRegex(ValueError, MESSAGE): + tempfile.mkstemp(prefix=value) + with self.assertRaisesRegex(ValueError, MESSAGE): + os.rmdir(tempfile.mkdtemp(prefix=value)) + with self.assertRaisesRegex(ValueError, MESSAGE): + tempfile.TemporaryFile(prefix=value) + with self.assertRaisesRegex(ValueError, MESSAGE): + tempfile.NamedTemporaryFile(prefix=value) - for value, directory in data: - with self.subTest((value, directory)): + def test_suffix_error(self): + MESSAGE = "'suffix' can't contain a directory component" + + for value in self.DATA: + with self.subTest((value)): + with self.assertRaisesRegex(ValueError, MESSAGE): + tempfile.mkstemp(suffix=value) with self.assertRaisesRegex(ValueError, MESSAGE): - tempfile.mkstemp(dir=directory, prefix=value) + os.rmdir(tempfile.mkdtemp(suffix=value)) with self.assertRaisesRegex(ValueError, MESSAGE): - os.rmdir(tempfile.mkdtemp(dir=directory, prefix=value)) + tempfile.TemporaryFile(suffix=value) with self.assertRaisesRegex(ValueError, MESSAGE): - tempfile.NamedTemporaryFile(dir=directory, prefix=value, delete=True) + tempfile.NamedTemporaryFile(suffix=value) if __name__ == "__main__": From 3f64d5b2808235eef7f13754e30795cb2cc888bb Mon Sep 17 00:00:00 2001 From: Mark Byrne Date: Thu, 28 May 2026 15:54:11 +0200 Subject: [PATCH 3/6] Use Victor's refactor suggestions. --- Lib/test/test_tempfile.py | 66 ++++++++++++++++++--------------------- 1 file changed, 31 insertions(+), 35 deletions(-) diff --git a/Lib/test/test_tempfile.py b/Lib/test/test_tempfile.py index f89a1e6ab2e2d5..a5f354b7a45348 100644 --- a/Lib/test/test_tempfile.py +++ b/Lib/test/test_tempfile.py @@ -2155,47 +2155,43 @@ def test_cleanup_safe(self): self.assertTrue(tempfile._rmtree_use_dir_fd) -class TestPrefixAndSuffix(BaseTestCase): - DATA = ( - f"dir{os.sep}name", - f"{os.sep}abs_name", - os.fsencode(f"dir{os.sep}name"), - os.fsencode(f"{os.sep}abs_name"), - ) - if os.altsep is not None: - DATA += ( - f"dir{os.altsep}name", - f"{os.altsep}abs_name", - os.fsencode(f"dir{os.altsep}name"), - os.fsencode(f"{os.altsep}abs_name"), - ) - - def test_prefix_error(self): - MESSAGE = "'prefix' can't contain a directory component" - - for value in self.DATA: - with self.subTest((value)): - with self.assertRaisesRegex(ValueError, MESSAGE): +class TestMisc(BaseTestCase): + def test_prefix_suffix_error(self): + tests = [ + f"dir{os.sep}name", + f"{os.sep}abs_name", + ] + if os.altsep is not None: + tests.extend(( + f"dir{os.altsep}name", + f"{os.altsep}abs_name", + )) + if support.MS_WINDOWS: + tests.append('C:name') + tests.extend(tuple(os.fsencode(path) for path in tests)) + + PREFIX_ERR = "'prefix' can't contain a directory component" + SUFFIX_ERR = "'suffix' can't contain a directory component" + for value in tests: + with self.subTest(value): + # test prefix + with self.assertRaisesRegex(ValueError, PREFIX_ERR): tempfile.mkstemp(prefix=value) - with self.assertRaisesRegex(ValueError, MESSAGE): - os.rmdir(tempfile.mkdtemp(prefix=value)) - with self.assertRaisesRegex(ValueError, MESSAGE): + with self.assertRaisesRegex(ValueError, PREFIX_ERR): + tempfile.mkdtemp(prefix=value) + with self.assertRaisesRegex(ValueError, PREFIX_ERR): tempfile.TemporaryFile(prefix=value) - with self.assertRaisesRegex(ValueError, MESSAGE): + with self.assertRaisesRegex(ValueError, PREFIX_ERR): tempfile.NamedTemporaryFile(prefix=value) - def test_suffix_error(self): - MESSAGE = "'suffix' can't contain a directory component" - - for value in self.DATA: - with self.subTest((value)): - with self.assertRaisesRegex(ValueError, MESSAGE): + # test suffix + with self.assertRaisesRegex(ValueError, SUFFIX_ERR): tempfile.mkstemp(suffix=value) - with self.assertRaisesRegex(ValueError, MESSAGE): - os.rmdir(tempfile.mkdtemp(suffix=value)) - with self.assertRaisesRegex(ValueError, MESSAGE): + with self.assertRaisesRegex(ValueError, SUFFIX_ERR): + tempfile.mkdtemp(suffix=value) + with self.assertRaisesRegex(ValueError, SUFFIX_ERR): tempfile.TemporaryFile(suffix=value) - with self.assertRaisesRegex(ValueError, MESSAGE): + with self.assertRaisesRegex(ValueError, SUFFIX_ERR): tempfile.NamedTemporaryFile(suffix=value) From 90cc53b3c912f76d934b16e72ee3aed33c8da1d7 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Mon, 5 Oct 2026 15:10:11 +0200 Subject: [PATCH 4/6] Apply batched suggestions from code review Co-authored-by: Victor Stinner --- Lib/tempfile.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Lib/tempfile.py b/Lib/tempfile.py index 22c0bd91dc8238..82c1ee353117e9 100644 --- a/Lib/tempfile.py +++ b/Lib/tempfile.py @@ -120,14 +120,14 @@ def _sanitize_params(prefix, suffix, dir): if suffix is None: suffix = output_type() if _os.path.dirname(suffix): - raise ValueError("'suffix' can't contain a directory component") + raise ValueError("suffix can't contain a directory component") if prefix is None: if output_type is str: prefix = template else: prefix = _os.fsencode(template) if _os.path.dirname(prefix): - raise ValueError("'prefix' can't contain a directory component") + raise ValueError("prefix can't contain a directory component") if dir is None: if output_type is str: dir = gettempdir() From 33b9fde31c5c429d2b87e105b3cd82357f131dc1 Mon Sep 17 00:00:00 2001 From: "blurb-it[bot]" <43283697+blurb-it[bot]@users.noreply.github.com> Date: Mon, 5 Oct 2026 13:57:11 +0000 Subject: [PATCH 5/6] =?UTF-8?q?=F0=9F=93=9C=F0=9F=A4=96=20Added=20by=20blu?= =?UTF-8?q?rb=5Fit.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../next/Library/2026-10-05-13-57-08.gh-issue-79459.2RgBDL.rst | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 Misc/NEWS.d/next/Library/2026-10-05-13-57-08.gh-issue-79459.2RgBDL.rst diff --git a/Misc/NEWS.d/next/Library/2026-10-05-13-57-08.gh-issue-79459.2RgBDL.rst b/Misc/NEWS.d/next/Library/2026-10-05-13-57-08.gh-issue-79459.2RgBDL.rst new file mode 100644 index 00000000000000..2bbe77d6972e22 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-10-05-13-57-08.gh-issue-79459.2RgBDL.rst @@ -0,0 +1,2 @@ +:mod:`tempfile` functions that take a ``prefix`` or ``suffix`` argument now raise a :exc:`ValueError` if they contain a directory component: +:func:`tempfile.mkstemp`, :func:`tempfile.mkdtemp`, :func:`tempfile.TemporaryFile`, :func:`tempfile.NamedTemporaryFile`. From b1097848c310c341f14935a1c09add2b119b13ee Mon Sep 17 00:00:00 2001 From: Mark Byrne Date: Mon, 5 Oct 2026 16:19:45 +0200 Subject: [PATCH 6/6] Update the unit tests to reflect the earlier commit in this PR to remove quotation marks from the error message. --- Lib/test/test_tempfile.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Lib/test/test_tempfile.py b/Lib/test/test_tempfile.py index a5f354b7a45348..2f7b8798daa8c9 100644 --- a/Lib/test/test_tempfile.py +++ b/Lib/test/test_tempfile.py @@ -2170,8 +2170,8 @@ def test_prefix_suffix_error(self): tests.append('C:name') tests.extend(tuple(os.fsencode(path) for path in tests)) - PREFIX_ERR = "'prefix' can't contain a directory component" - SUFFIX_ERR = "'suffix' can't contain a directory component" + PREFIX_ERR = "prefix can't contain a directory component" + SUFFIX_ERR = "suffix can't contain a directory component" for value in tests: with self.subTest(value): # test prefix