87 """Remove *target* recursively and report whether it is really gone.
89 Clears the read-only attribute on whatever refuses and retries that one
90 path once. Returns the result of an existence check made afterwards, so the
91 answer is what is on disk rather than what the call believed.
93 func =
"remove_tree_force"
94 if not Path(target).exists():
95 _dbg(
"OCT-CORE", func, f
"nothing to remove at {target}", 4)
98 def _clear_and_retry(remover, path, _exc) -> None:
99 """Make one refusing path writable and try that one removal again."""
101 os.chmod(path, stat.S_IWRITE | stat.S_IREAD | stat.S_IEXEC)
107 if sys.version_info >= (3, 12):
108 shutil.rmtree(target, onexc=_clear_and_retry)
110 shutil.rmtree(target, onerror=_clear_and_retry)
111 except OSError
as exc:
114 _dbg(
"OCT-CORE", func, f
"SYSTEM_ERROR: rmtree({target}) raised: {exc}", 3)
116 gone =
not Path(target).exists()
117 _dbg(
"OCT-CORE", func, f
"{target}: {'removed' if gone else 'still present'}",