Skip to content

GH-73991: Add pathlib.Path.copytree() - #120718

Merged
barneygale merged 4 commits into
python:mainfrom
barneygale:path-copytree
Jun 23, 2024
Merged

GH-73991: Add pathlib.Path.copytree()#120718
barneygale merged 4 commits into
python:mainfrom
barneygale:path-copytree

Conversation

@barneygale

@barneygalebarneygale commented Jun 19, 2024

Copy link
Copy Markdown
Contributor

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). This is done instead of aggregating exceptions into an shutil.Error instance.

📚 Documentation preview 📚: https://cpython-previews--120718.org.readthedocs.build/

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).
@barneygale

Copy link
Copy Markdown
ContributorAuthor

Some parts of the design aren't obvious, particularly the ignore and on_error arguments. Happy to adjust or even remove some arguments if we'd like to kick the can down the road - I don't think either of those arguments are essential to expose straight away.

@barneygale
barneygale requested a review from pfmooreJune 19, 2024 02:31
@barneygale

Copy link
Copy Markdown
ContributorAuthor

@pfmoore I've requested your review as you've already given this idea some scrutiny on the forum! Hope that's alright, thank you.

Comment threadDoc/library/pathlib.rst Outdated
Comment threadLib/test/test_pathlib/test_pathlib.py
Comment threadLib/test/test_pathlib/test_pathlib_abc.py Outdated

@pfmoorepfmoore left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The code LGTM. I've dome a partial review of the tests, but I'll be honest I'm not entirely clear what the follow_symlinks argument does (it's not something I use myself) so I didn't really review those parts of the tests.

I'll try to find some time to do a more detailed review later.

Comment threadLib/test/test_pathlib/test_pathlib_abc.py Outdated
Comment threadLib/test/test_pathlib/test_pathlib_abc.py Outdated
Comment threadLib/test/test_pathlib/test_pathlib_abc.py
Co-authored-by: Nice Zombies <nineteendo19d0@gmail.com>
Comment threadLib/test/test_pathlib/test_pathlib.py Outdated
barneygaleand others added 2 commits June 23, 2024 17:37
Co-authored-by: Nice Zombies <nineteendo19d0@gmail.com>
@barneygale

barneygale commented Jun 23, 2024

Copy link
Copy Markdown
ContributorAuthor

Thanks for the reviews and fixes, both.

I'm not entirely clear what the follow_symlinks argument does (it's not something I use myself) so I didn't really review those parts of the tests.

The default behaviour (follow_symlinks=True) dereferences any symlinks in the source tree, so the final destination tree never contains symlinks. If you set follow_symlinks=False, any symlinks in the source tree will be re-created in the destination tree with the same targets.

Our forthcoming implementation of Path.move() will call source.copytree(target, follow_symlinks=False) then source.rmtree() (roughly), because users expect "move" operations to preserve symlinks (not follow them).

@pfmoore

Copy link
Copy Markdown
Member

Thanks for the explanation - that's the clearest I've seen it expressed, and for the first time (possibly ever) I get what's going on.

@barneygale
barneygale merged commit 35e998f into python:mainJun 23, 2024
mrahtz pushed a commit to mrahtz/cpython that referenced this pull request Jun 30, 2024
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>
noahbkim pushed a commit to hudson-trading/cpython that referenced this pull request Jul 11, 2024
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>
estyxx pushed a commit to estyxx/cpython that referenced this pull request Jul 17, 2024
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>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

@barneygale@pfmoore@nineteendo