Repository navigation
Support moving across filesystems in pathlib.Path, as shutil.move() does #73991
Description
Activity
LaurentMazuel commented
on Mar 13, 2017 LaurentMazuelmannequinMannequinAuthorMore actionsTrying to use Pathlib and Path.replace on Windows if drive are different leads to an issue:
File "D:\\myscript.py", line 184, in update client_generated_path.replace(destination_folder) File "c:\\program files (x86)\\python35-32\\Lib\\pathlib.py", line 1273, in replace self.\_accessor.replace(self, target) File "c:\\program files (x86)\\python35-32\\Lib\\pathlib.py", line 377, in wrapped return strfunc(str(pathobjA), str(pathobjB), \*args) OSError: [WinError 17] The system cannot move the file to a different disk drive: 'C:\\\\MyFolder' -\> 'D:\\\\MyFolderNewName'This is a known situation of os.rename, and workaround I found is to use shutil or to copy/delete manually in two steps (e.g. http://stackoverflow.com/questions/21116510/python-oserror-winerror-17-the-system-cannot-move-the-file-to-a-different-d)
When using Pathlib, it's not that easy to workaround using shutil (even if thanks to Brett Cannon now shutil accepts Path in Py3.6, not everybody has Py3.6). At least this should be documented with a recommendation for that situation. I love Pathlib and it's too bad my code becomes complicated when it was so simple :(
- addedstdlibStandard Library Python modules in the Lib/ directoryStandard Library Python modules in the Lib/ directoryand removed
on Mar 13, 2017 LaurentMazuel commented
on Mar 13, 2017 LaurentMazuelmannequinMannequinAuthorMore actionsJust to confirm, I was able to workaround it with Py3.6:
# client_generated_path.replace(destination_folder) shutil.move(client_generated_path, destination_folder)
It is reasonable to think about adding a similar feature to pathlib if it doesn't support it and just special-case the drive-to-drive scenario for Windows
Moving a file across volumes isn't atomic. Getting an exception in this case can be useful. There could be a "strict" keyword-only parameter that defaults to False. If it's true, then replace() won't try to move the file.
I agree this needs to be different from replace(), due to not being atomic. That makes it an enhancement, so I'm removing 3.5.
I'm +1 on Path supporting something like shutil.move().
- addedtype-featureA feature request or enhancementA feature request or enhancement
on Mar 15, 2017 I also support the idea of getting something like shutil.move() into pathlib.
I should also mention that rename() (https://docs.python.org/3/library/pathlib.html#pathlib.Path.rename) and replace() (https://docs.python.org/3/library/pathlib.html#pathlib.Path.replace) already do exist, so it might be best to add a keyword-only flag to one of those for this use-case.
- added3.8 (EOL)end of lifeend of life3.9 (EOL)end of lifeend of life3.10 (EOL)end of lifeend of lifeand removed
on Mar 15, 2021 145 remaining items
- added 5 commits that reference this issue
on Aug 26, 2024 It's done! Thank you everyone who helped formulate and review this feature.
We've added new
copy(),copy_into(),move()andmove_into()methods, which work on both files and directories. The interfaces are refined to pathlib standards, with certain nonessential functionality (like support for filtering incopy()) omitted for now. Users may wish to propose new capabilities on the ideas forum.The new methods expect exact targets (or target directories for the
_into()variants), whereasshutilchecks for existing directories and adjusts the target as necessary. The latter behaviour is helpful in an interactive shell with tab-complete, but less so in a stored program where state of the filesystem might not be known (for example,shutil.copy('a', 'b')could create eitherborb/a).The
copy()andcopy_into()methods usefcntl.FICLONEoros.copy_file_rangewhere available (see GH-81338, GH-81340). This work has stalled for years inshutilfor backwards-compatibility concerns, happily not present here.Feedback most welcome! We have ~9 months until 3.14 beta 1 where the interface is more locked down, so there's still plenty of time make changes. I'll close this issue, but I can re-open it (or log new issues) if problems are identified. Thanks again, all.
Reacted by GalaxySnail, Andreas Poehlmann, Edgar Ramírez Mondragón, Nice Zombies, Johnny Arcitec, Tal Einat, Dima Tisnek, Petr Viktorin, traal, h-vetinari and 1 moreFor copying trees, what happens on error -- for example, when one of the files has a name that's incompatible with the target filesystem?
In that case you'll get an
OSError. Any copies already made will be left on disk, and in the case ofmove(), none of the original files will be deleted.It looks like, unlike
copy(), this method fails if I pass a path-like object that just happens to have a method namedstat(). It then fails atself._ensure_different_file(target)which already assumes aPathas its argument type. It really should do an isinstance check on the target. In my case,stat()is a coroutine method, so it fails withAttributeError: 'coroutine' object has no attribute 'st_ino'.Passing an object that looks similar but isn't the same is the caller's error. An
AttributeErroris exactly what you should get in this case.That said, it probably wouldn't be unreasonable to convert any incoming paths to the same type as the source. We shouldn't type check here, we should try to convert and let the errors bubble out to the caller.
Such type checks are done everywhere else, including in the method in question! This would be solved simply by doing said type check earlier.
Is it reasonable to expect this to fail on a
PathLikeobject? How am I supposed to know what I can pass as the target then?In general for pathlib, you should be able to pass any object that can be interpreted as the same kind of path as the one you're passing it to. I know that upsets the static typing people because it's hard to define, but being accepting of inputs is a key Python feature.
In this case, that means converting the input to the same type as the target before using it. Most of the functions that have type checks are essentially doing this - they're negative checks, followed by essentially
type(self)(other), which will try its hardest to parse the path out of the input. Butsamefileis not doing this and clearly ought to be. I suggest opening a new issue.
Metadata
Metadata
Assignees
Labels
Projects
- StatusShow more project fieldsDone
Note: these values reflect the state of the issue at the time it was migrated and might not reflect the current state.
Show more details
GitHub fields:
bugs.python.org fields:
Linked PRs
pathlib.Path.copy()#119058pathlib.Path.rmtree()#119060os.copy()and friends #119079shutil._rmtree_[un]safe(). #120517pathlib.Path.copy()#120519DummyPath.unlink()andrmdir()#120715pathlib.Path.copytree()#120718pathlib.Path.copy()#120806pathlib.Path.copytree()#121438pathlib.Path.move()#122073pathlib.Path.rmtree()intodelete()#122368pathlib.Path.copytree()intocopy()#122369pathlib.Path.copy()#122924pathlib.Path.delete()arguments #123158pathlib.Path.copy_into()andmove_into()#123314pathlib.Path.delete()private. #123315pathlib.Path.copy()andcopy_into()arguments #123337