Skip to content

Commit

Permalink
pythongh-102828: emit deprecation warning for onerror arg to shutil.r…
Browse files Browse the repository at this point in the history
…mtree (python#102850)
  • Loading branch information
iritkatriel authored Mar 21, 2023
1 parent 868490e commit 7f760c2
Show file tree
Hide file tree
Showing 4 changed files with 28 additions and 12 deletions.
10 changes: 7 additions & 3 deletions Doc/whatsnew/3.12.rst
Original file line number Diff line number Diff line change
Expand Up @@ -339,7 +339,8 @@ shutil

* :func:`shutil.rmtree` now accepts a new argument *onexc* which is an
error handler like *onerror* but which expects an exception instance
rather than a *(typ, val, tb)* triplet. *onerror* is deprecated.
rather than a *(typ, val, tb)* triplet. *onerror* is deprecated and
will be removed in Python 3.14.
(Contributed by Irit Katriel in :gh:`102828`.)


Expand Down Expand Up @@ -503,8 +504,8 @@ Deprecated
fields are deprecated. Use :data:`sys.last_exc` instead.
(Contributed by Irit Katriel in :gh:`102778`.)

* The *onerror* argument of :func:`shutil.rmtree` is deprecated. Use *onexc*
instead. (Contributed by Irit Katriel in :gh:`102828`.)
* The *onerror* argument of :func:`shutil.rmtree` is deprecated as will be removed
in Python 3.14. Use *onexc* instead. (Contributed by Irit Katriel in :gh:`102828`.)


Pending Removal in Python 3.13
Expand Down Expand Up @@ -586,6 +587,9 @@ Pending Removal in Python 3.14
functions that have been deprecated since Python 2 but only gained a
proper :exc:`DeprecationWarning` in 3.12. Remove them in 3.14.

* The *onerror* argument of :func:`shutil.rmtree` is deprecated in 3.12,
and will be removed in 3.14.

Pending Removal in Future Versions
----------------------------------

Expand Down
6 changes: 6 additions & 0 deletions Lib/shutil.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import fnmatch
import collections
import errno
import warnings

try:
import zlib
Expand Down Expand Up @@ -692,6 +693,11 @@ def rmtree(path, ignore_errors=False, onerror=None, *, onexc=None, dir_fd=None):
onerror is deprecated and only remains for backwards compatibility.
If both onerror and onexc are set, onerror is ignored and onexc is used.
"""

if onerror is not None:
warnings.warn("onerror argument is deprecated, use onexc instead",
DeprecationWarning)

sys.audit("shutil.rmtree", path, dir_fd)
if ignore_errors:
def onexc(*args):
Expand Down
8 changes: 4 additions & 4 deletions Lib/tempfile.py
Original file line number Diff line number Diff line change
Expand Up @@ -864,8 +864,8 @@ def __init__(self, suffix=None, prefix=None, dir=None,

@classmethod
def _rmtree(cls, name, ignore_errors=False):
def onerror(func, path, exc_info):
if issubclass(exc_info[0], PermissionError):
def onexc(func, path, exc):
if isinstance(exc, PermissionError):
def resetperms(path):
try:
_os.chflags(path, 0)
Expand All @@ -885,13 +885,13 @@ def resetperms(path):
cls._rmtree(path, ignore_errors=ignore_errors)
except FileNotFoundError:
pass
elif issubclass(exc_info[0], FileNotFoundError):
elif isinstance(exc, FileNotFoundError):
pass
else:
if not ignore_errors:
raise

_shutil.rmtree(name, onerror=onerror)
_shutil.rmtree(name, onexc=onexc)

@classmethod
def _cleanup(cls, name, warn_message, ignore_errors=False):
Expand Down
16 changes: 11 additions & 5 deletions Lib/test/test_shutil.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
unregister_unpack_format, get_unpack_formats,
SameFileError, _GiveupOnFastCopy)
import tarfile
import warnings
import zipfile
try:
import posix
Expand Down Expand Up @@ -207,7 +208,8 @@ def test_rmtree_fails_on_symlink_onerror(self):
errors = []
def onerror(*args):
errors.append(args)
shutil.rmtree(link, onerror=onerror)
with self.assertWarns(DeprecationWarning):
shutil.rmtree(link, onerror=onerror)
self.assertEqual(len(errors), 1)
self.assertIs(errors[0][0], os.path.islink)
self.assertEqual(errors[0][1], link)
Expand Down Expand Up @@ -268,7 +270,8 @@ def test_rmtree_fails_on_junctions_onerror(self):
errors = []
def onerror(*args):
errors.append(args)
shutil.rmtree(link, onerror=onerror)
with self.assertWarns(DeprecationWarning):
shutil.rmtree(link, onerror=onerror)
self.assertEqual(len(errors), 1)
self.assertIs(errors[0][0], os.path.islink)
self.assertEqual(errors[0][1], link)
Expand Down Expand Up @@ -337,7 +340,8 @@ def test_rmtree_errors_onerror(self):
errors = []
def onerror(*args):
errors.append(args)
shutil.rmtree(filename, onerror=onerror)
with self.assertWarns(DeprecationWarning):
shutil.rmtree(filename, onerror=onerror)
self.assertEqual(len(errors), 2)
self.assertIs(errors[0][0], os.scandir)
self.assertEqual(errors[0][1], filename)
Expand Down Expand Up @@ -406,7 +410,8 @@ def test_on_error(self):
self.addCleanup(os.chmod, self.child_file_path, old_child_file_mode)
self.addCleanup(os.chmod, self.child_dir_path, old_child_dir_mode)

shutil.rmtree(TESTFN, onerror=self.check_args_to_onerror)
with self.assertWarns(DeprecationWarning):
shutil.rmtree(TESTFN, onerror=self.check_args_to_onerror)
# Test whether onerror has actually been called.
self.assertEqual(self.errorState, 3,
"Expected call to onerror function did not happen.")
Expand Down Expand Up @@ -532,7 +537,8 @@ def onexc(*args):
self.addCleanup(os.chmod, self.child_file_path, old_child_file_mode)
self.addCleanup(os.chmod, self.child_dir_path, old_child_dir_mode)

shutil.rmtree(TESTFN, onerror=onerror, onexc=onexc)
with self.assertWarns(DeprecationWarning):
shutil.rmtree(TESTFN, onerror=onerror, onexc=onexc)
self.assertTrue(onexc_called)
self.assertFalse(onerror_called)

Expand Down

0 comments on commit 7f760c2

Please sign in to comment.