From 2770c98f09345cd473de5df5af938f15b25220c3 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Tue, 30 Jun 2026 01:00:33 +0300 Subject: [PATCH 1/3] 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. From af74d12ff42bd25ecd0943f7db88110daeb7a1dc Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Mon, 6 Jul 2026 02:00:53 +0300 Subject: [PATCH 2/3] gh-152638: Postpone removal of askopenfiles() to 3.19 Deprecating in 3.16 and removing in 3.19 gives three feature releases of deprecation before removal. Co-Authored-By: Claude Opus 4.8 --- Doc/deprecations/pending-removal-in-3.18.rst | 8 -------- Doc/deprecations/pending-removal-in-3.19.rst | 8 ++++++++ Doc/library/dialog.rst | 2 +- Doc/whatsnew/3.16.rst | 2 +- Lib/tkinter/filedialog.py | 2 +- 5 files changed, 11 insertions(+), 11 deletions(-) diff --git a/Doc/deprecations/pending-removal-in-3.18.rst b/Doc/deprecations/pending-removal-in-3.18.rst index 61b82593032295f..19113aab981bbc6 100644 --- a/Doc/deprecations/pending-removal-in-3.18.rst +++ b/Doc/deprecations/pending-removal-in-3.18.rst @@ -11,14 +11,6 @@ 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/deprecations/pending-removal-in-3.19.rst b/Doc/deprecations/pending-removal-in-3.19.rst index 4a58c606ab7596e..d7e0cd2d33ddb86 100644 --- a/Doc/deprecations/pending-removal-in-3.19.rst +++ b/Doc/deprecations/pending-removal-in-3.19.rst @@ -40,3 +40,11 @@ Pending removal in Python 3.19 Before Python 3.14, this property was used to implement the corresponding ``read()`` and ``readline()`` methods for :class:`~imaplib.IMAP4` but this is no longer the case since then. + +* :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`.) diff --git a/Doc/library/dialog.rst b/Doc/library/dialog.rst index bbd7da32fc0fea1..1dd39a8ebb66258 100644 --- a/Doc/library/dialog.rst +++ b/Doc/library/dialog.rst @@ -176,7 +176,7 @@ specific value. objects, or an empty tuple if cancelled. The files are opened in mode *mode* (read-only ``'r'`` by default). - .. deprecated-removed:: next 3.18 + .. deprecated-removed:: next 3.19 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 diff --git a/Doc/whatsnew/3.16.rst b/Doc/whatsnew/3.16.rst index 288ce91e419fd68..ff7c7d5f825b358 100644 --- a/Doc/whatsnew/3.16.rst +++ b/Doc/whatsnew/3.16.rst @@ -553,7 +553,7 @@ New deprecations * :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 + removal in Python 3.19. 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. diff --git a/Lib/tkinter/filedialog.py b/Lib/tkinter/filedialog.py index 2b9051522edfbb8..69c77a406f95af2 100644 --- a/Lib/tkinter/filedialog.py +++ b/Lib/tkinter/filedialog.py @@ -423,7 +423,7 @@ def askopenfiles(mode = "r", **options): "tkinter.filedialog.askopenfiles", message=f"{warnings._DEPRECATED_MSG}; iterate over the names returned " "by askopenfilenames() and open them instead", - remove=(3, 18)) + remove=(3, 19)) files = askopenfilenames(**options) if files: From 21428b3671f9211fdec9e6b96a9425e46d63e339 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Sat, 11 Jul 2026 08:15:45 +0300 Subject: [PATCH 3/3] gh-152638: Import warnings locally and use semantic line breaks Import warnings inside askopenfiles() rather than at module level, since it is only used by the deprecation there. Break the new documentation lines at sentence and clause boundaries. Co-Authored-By: Claude Opus 4.8 --- Doc/library/dialog.rst | 16 ++++++++-------- Lib/tkinter/filedialog.py | 2 +- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/Doc/library/dialog.rst b/Doc/library/dialog.rst index 8f71d00c7d506a8..c18f2bb38b9ff1d 100644 --- a/Doc/library/dialog.rst +++ b/Doc/library/dialog.rst @@ -164,21 +164,21 @@ string, an empty tuple, an empty list or ``None``. .. function:: askopenfile(mode="r", **options) - Create an :class:`Open` dialog and return the opened file object, or - ``None`` if the dialog is 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 list if cancelled. + Create an :class:`Open` dialog and return a list of the opened file objects, + or an empty list if cancelled. The files are opened in mode *mode* (read-only ``'r'`` by default). .. deprecated-removed:: next 3.19 - 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. + 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) diff --git a/Lib/tkinter/filedialog.py b/Lib/tkinter/filedialog.py index bc339e55711b85d..5cf4959d3489fc8 100644 --- a/Lib/tkinter/filedialog.py +++ b/Lib/tkinter/filedialog.py @@ -19,7 +19,6 @@ import fnmatch import os import tkinter -import warnings from tkinter import ( ACTIVE, CENTER, EW, NORMAL, NS, NSEW, RAISED, W, YES, BOTTOM, TOP, Tk, X, Toplevel, END, Listbox, BOTH, @@ -576,6 +575,7 @@ def askopenfiles(mode = "r", **options): returns a list of open file objects or an empty list if cancel selected """ + import warnings warnings._deprecated( "tkinter.filedialog.askopenfiles", message=f"{warnings._DEPRECATED_MSG}; iterate over the names returned "