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 06f2d6ddbf4f48b..c18f2bb38b9ff1d 100644 --- a/Doc/library/dialog.rst +++ b/Doc/library/dialog.rst @@ -163,15 +163,23 @@ cancelled it is the empty value documented for that function -- an empty string, an empty tuple, an empty list or ``None``. .. 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 - list 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 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. + .. 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 3ee8de18cb5cd38..b936723ec574e2c 100644 --- a/Doc/whatsnew/3.16.rst +++ b/Doc/whatsnew/3.16.rst @@ -655,6 +655,15 @@ New deprecations Use the new public :class:`tempfile.TemporaryFileWrapper` instead, which is the return type of :func:`tempfile.NamedTemporaryFile`. +* :mod:`tkinter`: + + * :func:`tkinter.filedialog.askopenfiles` is deprecated and slated for + 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. + (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 817990a961e956e..f65ed19895a1ed4 100644 --- a/Lib/test/test_tkinter/test_filedialog.py +++ b/Lib/test/test_tkinter/test_filedialog.py @@ -203,5 +203,13 @@ def test_type_ahead(self): self.assertEqual([d.files.get(i) for i in sel], ['charlie']) +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 29020475a3cb538..5cf4959d3489fc8 100644 --- a/Lib/tkinter/filedialog.py +++ b/Lib/tkinter/filedialog.py @@ -575,6 +575,12 @@ 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 " + "by askopenfilenames() and open them instead", + remove=(3, 19)) files = askopenfilenames(**options) return [open(filename, mode) for filename in 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.