aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/Lib/pathlib/_abc.py
diff options
context:
space:
mode:
authorBarney Gale <barney.gale@gmail.com>2024-06-23 22:01:12 +0100
committerGitHub <noreply@github.com>2024-06-23 22:01:12 +0100
commit35e998f5608b04cdd331e67dd80d4829df71a5fd (patch)
tree44099e8e9ae00cad3684e3adb024fadcc7770050 /Lib/pathlib/_abc.py
parentbc37ac7b440b5e816f0b3915b830404290522603 (diff)
downloadcpython-35e998f5608b04cdd331e67dd80d4829df71a5fd.tar.gz
cpython-35e998f5608b04cdd331e67dd80d4829df71a5fd.zip
GH-73991: Add `pathlib.Path.copytree()` (#120718)
Add `pathlib.Path.copytree()` method, which recursively copies one directory to another. This differs from `shutil.copytree()` in the following respects: 1. Our method has a *follow_symlinks* argument, whereas shutil's has a *symlinks* argument with an inverted meaning. 2. Our method lacks something like a *copy_function* argument. It always uses `Path.copy()` to copy files. 3. Our method lacks something like a *ignore_dangling_symlinks* argument. Instead, users can filter out danging symlinks with *ignore*, or ignore exceptions with *on_error* 4. Our *ignore* argument is a callable that accepts a single path object, whereas shutil's accepts a path and a list of child filenames. 5. We add an *on_error* argument, which is a callable that accepts an `OSError` instance. (`Path.walk()` also accepts such a callable). Co-authored-by: Nice Zombies <nineteendo19d0@gmail.com>
Diffstat (limited to 'Lib/pathlib/_abc.py')
-rw-r--r--Lib/pathlib/_abc.py30
1 files changed, 30 insertions, 0 deletions
diff --git a/Lib/pathlib/_abc.py b/Lib/pathlib/_abc.py
index f1f350a1960..71973913921 100644
--- a/Lib/pathlib/_abc.py
+++ b/Lib/pathlib/_abc.py
@@ -815,6 +815,36 @@ class PathBase(PurePathBase):
else:
raise
+ def copytree(self, target, *, follow_symlinks=True, dirs_exist_ok=False,
+ ignore=None, on_error=None):
+ """
+ Recursively copy this directory tree to the given destination.
+ """
+ if not isinstance(target, PathBase):
+ target = self.with_segments(target)
+ if on_error is None:
+ def on_error(err):
+ raise err
+ stack = [(self, target)]
+ while stack:
+ source_dir, target_dir = stack.pop()
+ try:
+ sources = source_dir.iterdir()
+ target_dir.mkdir(exist_ok=dirs_exist_ok)
+ for source in sources:
+ if ignore and ignore(source):
+ continue
+ try:
+ if source.is_dir(follow_symlinks=follow_symlinks):
+ stack.append((source, target_dir.joinpath(source.name)))
+ else:
+ source.copy(target_dir.joinpath(source.name),
+ follow_symlinks=follow_symlinks)
+ except OSError as err:
+ on_error(err)
+ except OSError as err:
+ on_error(err)
+
def rename(self, target):
"""
Rename this path to the target path.