From 2770c98f09345cd473de5df5af938f15b25220c3 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Tue, 30 Jun 2026 01:00:33 +0300 Subject: [PATCH] gh-152638: Deprecate tkinter.filedialog.askopenfiles() Opening several files at once is error-prone, and the returned list cannot be used in a "with" statement. Iterate over the names returned by askopenfilenames() and open them one by one instead. Co-Authored-By: Claude Opus 4.8 --- Doc/deprecations/pending-removal-in-3.18.rst | 8 ++++++++ Doc/library/dialog.rst | 20 +++++++++++++------ Doc/whatsnew/3.16.rst | 9 +++++++++ Lib/test/test_tkinter/test_filedialog.py | 8 ++++++++ Lib/tkinter/filedialog.py | 6 ++++++ ...-06-29-21-59-08.gh-issue-152638.92dpzo.rst | 4 ++++ 6 files changed, 49 insertions(+), 6 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2026-06-29-21-59-08.gh-issue-152638.92dpzo.rst diff --git a/Doc/deprecations/pending-removal-in-3.18.rst b/Doc/deprecations/pending-removal-in-3.18.rst index 19113aab981bbc6..61b82593032295f 100644 --- a/Doc/deprecations/pending-removal-in-3.18.rst +++ b/Doc/deprecations/pending-removal-in-3.18.rst @@ -11,6 +11,14 @@ Pending removal in Python 3.18 C implementation, has been deprecated since Python 3.13. (Contributed by Serhiy Storchaka in :gh:`89902`.) +* :mod:`tkinter`: + + * :func:`tkinter.filedialog.askopenfiles` has been deprecated since Python + 3.16. Iterate over the names returned by + :func:`~tkinter.filedialog.askopenfilenames` and open them one by one + instead. + (Contributed by Serhiy Storchaka in :gh:`152638`.) + * Deprecations defined by :pep:`829`: * ``import`` lines in :file:`{name}.pth` files are silently ignored. diff --git a/Doc/library/dialog.rst b/Doc/library/dialog.rst index c3f62117ed61d78..bbd7da32fc0fea1 100644 --- a/Doc/library/dialog.rst +++ b/Doc/library/dialog.rst @@ -165,15 +165,23 @@ versions, so test the result for truth rather than comparing it with a specific value. .. function:: askopenfile(mode="r", **options) - askopenfiles(mode="r", **options) - Create an :class:`Open` dialog. - :func:`askopenfile` returns the opened file object, or ``None`` if the - dialog is cancelled. - :func:`askopenfiles` returns a list of the opened file objects, or an empty - tuple if cancelled. + Create an :class:`Open` dialog and return the opened file object, or + ``None`` if the dialog is cancelled. + The file is opened in mode *mode* (read-only ``'r'`` by default). + +.. function:: askopenfiles(mode="r", **options) + + Create an :class:`Open` dialog and return a list of the opened file + objects, or an empty tuple if cancelled. The files are opened in mode *mode* (read-only ``'r'`` by default). + .. deprecated-removed:: next 3.18 + Opening several files at once is error-prone, and the returned list + cannot be used in a :keyword:`with` statement. + Iterate over the names returned by :func:`askopenfilenames` and open + them one by one instead. + .. function:: asksaveasfile(mode="w", **options) Create a :class:`SaveAs` dialog and return the opened file object, or diff --git a/Doc/whatsnew/3.16.rst b/Doc/whatsnew/3.16.rst index e215d4ddfdf41b7..288ce91e419fd68 100644 --- a/Doc/whatsnew/3.16.rst +++ b/Doc/whatsnew/3.16.rst @@ -550,6 +550,15 @@ New deprecations 3.9, now issues a deprecation warning on use. This property is slated for removal in 3.21. Use ``ast.Tuple.elts`` instead. +* :mod:`tkinter`: + + * :func:`tkinter.filedialog.askopenfiles` is deprecated and slated for + removal in Python 3.18. Opening several files at once is error-prone, and + the returned list cannot be used in a :keyword:`with` statement. Iterate + over the names returned by :func:`~tkinter.filedialog.askopenfilenames` and + open them one by one instead. + (Contributed by Serhiy Storchaka in :gh:`152638`.) + .. Add deprecations above alphabetically, not here at the end. .. include:: ../deprecations/pending-removal-in-3.17.rst diff --git a/Lib/test/test_tkinter/test_filedialog.py b/Lib/test/test_tkinter/test_filedialog.py index 054e719a0f883d9..1da9b31aceb91dd 100644 --- a/Lib/test/test_tkinter/test_filedialog.py +++ b/Lib/test/test_tkinter/test_filedialog.py @@ -69,5 +69,13 @@ def test_subclasses(self): self.assertEqual(d.top.title(), cls.title) +class DeprecationTest(unittest.TestCase): + + def test_askopenfiles_deprecated(self): + with swap_attr(filedialog, 'askopenfilenames', lambda **kw: ()): + with self.assertWarns(DeprecationWarning): + filedialog.askopenfiles() + + if __name__ == "__main__": unittest.main() diff --git a/Lib/tkinter/filedialog.py b/Lib/tkinter/filedialog.py index e2eff98e601c07c..2b9051522edfbb8 100644 --- a/Lib/tkinter/filedialog.py +++ b/Lib/tkinter/filedialog.py @@ -18,6 +18,7 @@ import fnmatch import os +import warnings from tkinter import ( Frame, LEFT, YES, BOTTOM, Entry, TOP, Button, Tk, X, Toplevel, RIGHT, Y, END, Listbox, BOTH, Scrollbar, @@ -418,6 +419,11 @@ def askopenfiles(mode = "r", **options): returns a list of open file objects or an empty list if cancel selected """ + warnings._deprecated( + "tkinter.filedialog.askopenfiles", + message=f"{warnings._DEPRECATED_MSG}; iterate over the names returned " + "by askopenfilenames() and open them instead", + remove=(3, 18)) files = askopenfilenames(**options) if files: diff --git a/Misc/NEWS.d/next/Library/2026-06-29-21-59-08.gh-issue-152638.92dpzo.rst b/Misc/NEWS.d/next/Library/2026-06-29-21-59-08.gh-issue-152638.92dpzo.rst new file mode 100644 index 000000000000000..3fbffb03cb38961 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-06-29-21-59-08.gh-issue-152638.92dpzo.rst @@ -0,0 +1,4 @@ +Deprecate :func:`tkinter.filedialog.askopenfiles`. Opening several files at +once is error-prone and the returned list cannot be used in a :keyword:`with` +statement; iterate over the names returned by +:func:`tkinter.filedialog.askopenfilenames` and open them one by one instead.