This issue tracker has been migrated to GitHub, and is currently read-only.
For more information, see the GitHub FAQs in the Python's Developer Guide.

classification
Title: shutil.move clobbers read-only files.
Type: enhancement Stage: resolved
Components: Library (Lib) Versions: Python 3.2
process
Status: closed Resolution: not a bug
Dependencies: 810879 Superseder:
Assigned To: tarek Nosy List: BreamoreBoy, bbrazil, brett.cannon, brian.curtin, eric.araujo, groodt, jemfinch, jlgijsbers, loewis, pitrou, r.david.murray, tarek, tim.golden
Priority: normal Keywords: easy

Created on 2004-12-01 05:40 by jemfinch, last changed 2022-04-11 14:56 by admin. This issue is now closed.

Messages (14)
msg23428 - (view) Author: Jeremy Fincher (jemfinch) Date: 2004-12-01 05:40
The summary states it fine.  shutil.move happily overwrites read-
only files.

It looks like it indiscriminately catches OSError, and never bothers 
to check whether it's a permission error.

It'd be nice if permission errors raised an exception that was a 
subclass of OSError, then it'd be cake to fix this (at least for 
*nices; I'm not sure about the Windows implications).

According to tracker #810879, clobbering read-only files isn't the 
desired behavior for shutil.copyfile, so I doubt it's desired for 
shutil.move.
msg23429 - (view) Author: Johannes Gijsbers (jlgijsbers) * (Python triager) Date: 2004-12-06 22:21
Logged In: YES 
user_id=469548

Your analysis is not correct. On Unix, you need write
permission to the *directory* to rename. So the os.rename()
call simply succeeds on a read-only file if you have write
access to its parent directory. I think we could shield from
this by always using the fallback implementation
(copy2+unlink(src)), but I'm not sure what the implications
of that would be (qua performance and cross-platform issues).
msg23430 - (view) Author: Martin v. Löwis (loewis) * (Python committer) Date: 2006-04-12 08:06
Logged In: YES 
user_id=21627

I don't think I will do anything about this anytime soon, so
unassigning myself.
msg107638 - (view) Author: Éric Araujo (eric.araujo) * (Python committer) Date: 2010-06-12 09:30
Is this bug still relevant?
msg113271 - (view) Author: Brian Brazil (bbrazil) * Date: 2010-08-08 14:53
Here's a quick test:

Python 3.2a1+ (py3k:83811, Aug  8 2010, 09:00:22) 
[GCC 4.2.4 (Ubuntu 4.2.4-1ubuntu4)] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import os, shutil
>>> open('a', 'w').write('a')
1
>>> open('b', 'w').write('b')
1
>>> os.chmod('b', 000)
>>> shutil.move('a', 'b')
>>> open('b').read()
'a'
>>> 

This is the correct behaviour on Unix, so I'd say this can be closed off.
msg114985 - (view) Author: Mark Lawrence (BreamoreBoy) * Date: 2010-08-26 15:19
Brian, Tim, any comments on this wrt Windows or do you think this can be closed?  Could there be an impact on any other OS?  I'll close if there's no response, unless someone else feels fit to close this anyway.
msg116165 - (view) Author: Mark Lawrence (BreamoreBoy) * Date: 2010-09-12 10:31
No reply to msg114985.
msg116237 - (view) Author: Brian Curtin (brian.curtin) * (Python committer) Date: 2010-09-12 22:00
I haven't had time to investigate but it shouldn't be closed just yet. Someone will get to it.
msg116305 - (view) Author: Antoine Pitrou (pitrou) * (Python committer) Date: 2010-09-13 14:01
I agree that this follows Unix behaviour: a read-only file is a file whose contents cannot be modified, but you can replace it with another totally different file. You can also delete it, by the way (*).

Also, even if this weren't the desired behaviour, changing it would break compatibility for existing scripts.

(*)

>>> open('b', 'w').write('b')
1
>>> os.chmod('b', 000)
>>> os.remove('b')
>>> open('b', 'r')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
IOError: [Errno 2] No such file or directory: 'b'
msg164888 - (view) Author: Greg Roodt (groodt) * Date: 2012-07-07 15:52
I believe this can be closed.

shutil.copyfile attempts to copy file contents from src to dst, so it makes sense for this to fail if dst is read-only.

shutil.move replaces the dst, which is permitted based on directory permissions.
msg164897 - (view) Author: Éric Araujo (eric.araujo) * (Python committer) Date: 2012-07-07 16:08
Would a doc clarification be useful?
msg165121 - (view) Author: Greg Roodt (groodt) * Date: 2012-07-09 20:14
I can add some more info to the docs if anybody feels they are required, but I think they are sufficient. It already mentions that it copies file contents and that the correct permissions are required.

This is mentioned in the docs for shutil.copyfile:

The destination location must be writable; otherwise, an OSError exception will be raised. If dst already exists, it will be replaced.
msg183896 - (view) Author: Brett Cannon (brett.cannon) * (Python committer) Date: 2013-03-10 20:24
Closing since everyone seems to agree that the current behaviour is fine and Tarek has not said anything since being assigned the bug.
msg183897 - (view) Author: R. David Murray (r.david.murray) * (Python committer) Date: 2013-03-10 20:32
Since I spent some time thinking about this, I want to leave some notes:

The linux 'mv' command issues the following prompt:

  > mv t2/myfile t1
  mv: try to overwrite ‘t1/myfile’, overriding mode 0000 (---------)?

So modern linux, at least, will not overwrite the file unless the '-f' flag is given explicitly (or you answer yes to the prompt, but that's not relevant to shutil.move).

However, shutil.move does not claim to implement 'mv'.  The release note in 3.3 indicates we've adapted its symlink handling to mimic mv, but the rest of the text is explicit about the implementation *in terms of other stdlib functions*.

So really the only way to understand the behavior of shutil.move is to understand what those other functions do on your platform.  The OP's confusion had to do with the behavior of *the Unix file system* in the face of a rename operation, not with the definition of shutil.move itself (which says that it uses rename, and links to its description).

Someone could think through the description of 'rename' and open an issue to improve *its* documentation if that seems warranted, but I think that "has permission" is somewhat platform dependent and so it probably isn't appropriate to change that doc, either.

Thus, I agree that this should be closed.
History
Date User Action Args
2022-04-11 14:56:08adminsetgithub: 41261
2013-03-10 20:32:37r.david.murraysetnosy: + r.david.murray
messages: + msg183897

resolution: rejected -> not a bug
stage: resolved
2013-03-10 20:24:40brett.cannonsetstatus: open -> closed

nosy: + brett.cannon
messages: + msg183896

resolution: rejected
2012-07-09 20:14:47groodtsetmessages: + msg165121
2012-07-07 16:08:19eric.araujosetmessages: + msg164897
2012-07-07 15:52:53groodtsetnosy: + groodt
messages: + msg164888
2010-09-13 14:02:20pitrousetstage: test needed -> (no value)
type: behavior -> enhancement
versions: - Python 3.1, Python 2.7
2010-09-13 14:01:38pitrousetnosy: + pitrou
messages: + msg116305
2010-09-12 22:00:28brian.curtinsetstatus: closed -> open
resolution: not a bug -> (no value)
messages: + msg116237
2010-09-12 10:31:34BreamoreBoysetstatus: pending -> closed
resolution: not a bug
messages: + msg116165
2010-08-26 15:19:41BreamoreBoysetstatus: open -> pending
versions: + Python 3.1, Python 2.7, Python 3.2, - Python 2.6
nosy: + tim.golden, brian.curtin, BreamoreBoy

messages: + msg114985
2010-08-08 14:53:10bbrazilsetnosy: + bbrazil
messages: + msg113271
2010-06-12 09:30:58eric.araujosetassignee: tarek

messages: + msg107638
nosy: + tarek, eric.araujo
2009-04-22 05:08:41ajaksu2setkeywords: + easy
2009-03-30 22:36:36ajaksu2setdependencies: + shutil.copyfile fails when dst exists read-only
type: behavior
stage: test needed
versions: + Python 2.6
2004-12-01 05:40:47jemfinchcreate