Pathlib additions & changes

Hi Recently I've been trying out pathlib in some real code, and while it is a vast improvement on messing around with os, os.path and shutil there are a couple of suggestions I'd like to make. ** TL;DR: Add Path.copy, add Path.remove (replaces Path.rmdir and Path.unlink) and add flags to several methods. Details ====== 1. Add a copy method, i.e. source_path.copy(target_path), which by default should behave like shutil.copy2. 2. Why bother with the distinction between Path.unlink and Path.rmdir? Obviously they apply to different types of paths, but to a user, either way you just want to remove whatever is at the path, so perhaps just have a single Path.remove instead (but see point 3 below). 3. There are several other minor irritations where a common pattern requires several lines or the use of a lower-level library such as shutil. For example: * Mkdir where path exists, but we don't care (common pattern on scripts) if not path.exists(): path.mkdir(parent=True) * Recursively remove a directory (no sane way using pathlib alone) shutil.rmtree(str(path)) * Move a file, creating parents if necessary py> if not target.parent.exists(): target.parent.mkdir(parents=true) source.rename(target) There are others, but these are a couple that spring to mind. There are three options. Either we add a bunch of specific functions for each of these (e.g. Path.rmtree, Path.rename_with_mkdir, etc), or we add a whole lot of boolean arguments (e.g. Path.rename(make_parents=True), or we use flags, e.g. Path.rename(flags=MAKE_PARENTS) Using flags is, IMHO the neatest solution, and could replace some boolean arguments already included. What follows is a suggestion of where flags might be useful, including the new methods suggested above. I haven't put a huge amount of thought into these, wanting to just get the general idea on the table, so I'm sure that upon closer inspection some won't make much sense or could be better named. chmod: RECURSIVE | DONT_FOLLOW_SYMLINKS copy: WITH_STATS | MAKE_PARENTS | OVERWRITE_EXISTING | IGNORE_EXISTING iterdir: RECURSIVE (maybe not worth it because of globbing) lchmod: RECURSIVE (Could be dropped in favour of chmod(flags=DONT_FOLLOW_SYMLINKS)) lstat: (Could be dropped in favour of stat(flags=DONT_FOLLOW_SYMLINKS)) mkdir: MAKE_PARENTS | OVERWRITE_EXISTING | IGNORE_EXISTING remove: RECURSIVE rename: MAKE_PARENTS | OVERWRITE_EXISTING | IGNORE_EXISTING replace: (Could be dropped in favour of rename(flags=OVERWRITE_EXISTING) ) rmdir: (Could be dropped in favour of remove) stat: DONT_FOLLOW_SYMLINKS touch: MAKE_PARENTS | IGNORE_EXISTING unlink: (Could be dropped in favour of remove) Regards David

On June 22, 2015 3:30:26 AM CDT, David Townshend <aquavitae69@gmail.com> wrote:
Hi
Recently I've been trying out pathlib in some real code, and while it is a vast improvement on messing around with os, os.path and shutil there are a couple of suggestions I'd like to make.
** TL;DR: Add Path.copy, add Path.remove (replaces Path.rmdir and Path.unlink) and add flags to several methods.
Details ======
1. Add a copy method, i.e. source_path.copy(target_path), which by default should behave like shutil.copy2.
2. Why bother with the distinction between Path.unlink and Path.rmdir? Obviously they apply to different types of paths, but to a user, either way you just want to remove whatever is at the path, so perhaps just have a single Path.remove instead (but see point 3 below).
3. There are several other minor irritations where a common pattern requires several lines or the use of a lower-level library such as shutil. For example:
* Mkdir where path exists, but we don't care (common pattern on scripts) if not path.exists(): path.mkdir(parent=True)
You can just do' try: path.mkdir(parent=True) except FileExistsError: pass
* Recursively remove a directory (no sane way using pathlib alone) shutil.rmtree(str(path))
* Move a file, creating parents if necessary py> if not target.parent.exists(): target.parent.mkdir(parents=true) source.rename(target)
There are others, but these are a couple that spring to mind. There are three options. Either we add a bunch of specific functions for each of these (e.g. Path.rmtree, Path.rename_with_mkdir, etc), or we add a whole lot of boolean arguments (e.g. Path.rename(make_parents=True), or we use flags, e.g. Path.rename(flags=MAKE_PARENTS)
Using flags is, IMHO the neatest solution, and could replace some boolean arguments already included. What follows is a suggestion of where flags might be useful, including the new methods suggested above. I haven't put a huge amount of thought into these, wanting to just get the general idea on the table, so I'm sure that upon closer inspection some won't make much sense or could be better named.
I prefer keyword-only arguments. Flags aren't really Pythonic, IMO.
chmod: RECURSIVE | DONT_FOLLOW_SYMLINKS copy: WITH_STATS | MAKE_PARENTS | OVERWRITE_EXISTING | IGNORE_EXISTING iterdir: RECURSIVE (maybe not worth it because of globbing) lchmod: RECURSIVE (Could be dropped in favour of chmod(flags=DONT_FOLLOW_SYMLINKS)) lstat: (Could be dropped in favour of stat(flags=DONT_FOLLOW_SYMLINKS)) mkdir: MAKE_PARENTS | OVERWRITE_EXISTING | IGNORE_EXISTING remove: RECURSIVE rename: MAKE_PARENTS | OVERWRITE_EXISTING | IGNORE_EXISTING replace: (Could be dropped in favour of rename(flags=OVERWRITE_EXISTING) ) rmdir: (Could be dropped in favour of remove) stat: DONT_FOLLOW_SYMLINKS touch: MAKE_PARENTS | IGNORE_EXISTING unlink: (Could be dropped in favour of remove)
Regards David
------------------------------------------------------------------------
_______________________________________________ Python-ideas mailing list Python-ideas@python.org https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/
-- Sent from my Android device with K-9 Mail. Please excuse my brevity.

On Mon, Jun 22, 2015 at 6:30 PM, David Townshend <aquavitae69@gmail.com> wrote:
3. There are several other minor irritations where a common pattern requires several lines or the use of a lower-level library such as shutil. For example:
* Recursively remove a directory (no sane way using pathlib alone) shutil.rmtree(str(path))
I'm not sure shutil should be considered a lower-level library. It's a separate set of tools aimed at shell-like functionality. Removing a directory tree seems right for shutil; what if shutil.rmtree() would accept a Path object as an alternative to a str? That'd make reasonable sense, and it'd feel like the two modules were working well together. (Or can it already?) ChrisA

On 22 June 2015 at 15:59, Chris Angelico <rosuav@gmail.com> wrote:
On Mon, Jun 22, 2015 at 6:30 PM, David Townshend <aquavitae69@gmail.com> wrote:
3. There are several other minor irritations where a common pattern requires several lines or the use of a lower-level library such as shutil. For example:
* Recursively remove a directory (no sane way using pathlib alone) shutil.rmtree(str(path))
I'm not sure shutil should be considered a lower-level library. It's a separate set of tools aimed at shell-like functionality. Removing a directory tree seems right for shutil; what if shutil.rmtree() would accept a Path object as an alternative to a str? That'd make reasonable sense, and it'd feel like the two modules were working well together.
Agreed, shutil is higher level than pathlib, not lower. Having more stdlib functions (shutil is the most obvious example, but there are others) take pathlib.Path objects as well as strings would be a good change (and would set a nice example for 3rd party file manipulation modules). I'm sure the usual "patches welcome" applies :-) The main irritation about using "higher level" modules with path objects is the proliferation of str() calls. Accepting path objects natively fixes that: from shutil import rmtree rmtree(path) looks fine to me. Paul

On 22 Jun 2015 17:59, "Paul Moore" <p.f.moore@gmail.com> wrote:
On 22 June 2015 at 15:59, Chris Angelico <rosuav@gmail.com> wrote:
On Mon, Jun 22, 2015 at 6:30 PM, David Townshend <aquavitae69@gmail.com>
wrote:
3. There are several other minor irritations where a common pattern requires several lines or the use of a lower-level library such as shutil. For example:
* Recursively remove a directory (no sane way using pathlib alone) shutil.rmtree(str(path))
I'm not sure shutil should be considered a lower-level library. It's a separate set of tools aimed at shell-like functionality. Removing a directory tree seems right for shutil; what if shutil.rmtree() would accept a Path object as an alternative to a str? That'd make reasonable sense, and it'd feel like the two modules were working well together.
Agreed, shutil is higher level than pathlib, not lower.
Having more stdlib functions (shutil is the most obvious example, but there are others) take pathlib.Path objects as well as strings would be a good change (and would set a nice example for 3rd party file manipulation modules). I'm sure the usual "patches welcome" applies :-)
The main irritation about using "higher level" modules with path objects is the proliferation of str() calls. Accepting path objects natively fixes that:
from shutil import rmtree rmtree(path)
looks fine to me.
Paul _______________________________________________ Python-ideas mailing list Python-ideas@python.org https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/
I was going on the fact that the PEP talks about possibly including shutil functions, but I have no problem with making them accept Paths instead. If that's the best approach I'll see if I can put together a patch. David
participants (4)
-
Chris Angelico
-
David Townshend
-
Paul Moore
-
Ryan Gonzalez