-
-
Notifications
You must be signed in to change notification settings - Fork 30.9k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add create mode to open() #56969
Comments
Currently, opening a file with open(file, 'w') overwrites existing files. It would be useful for open() to raise an error when the file exists. This proposal is to add a 'c' mode to open, which has the effect to creating a file and opening it for writing, but raising an IOError if it already exists (i.e. the same as os.open(file, os.O_EXCL|os.O_CREAT). The attached patch implements this, including tests and documentation. |
I'm not sure that O_EXCL is portable (exist on all platforms) because Python source code uses "#ifdef O_EXCL". |
I think this should be brought up on python-ideas or python-dev. |
See also bpo-12105.
Furthermore, you can achieve the same thing with: |
It was discussed on python-ideas, but the subject of the thread was actually on shutils.move so it was not really discussed much. I will repost this idea separately. |
The "#ifdef O_EXCL" in the source code is probably very old. Copying a message I posted on python-ideas: O_EXCL is a POSIX standard. It is also supported Probably there are very old systems which don't support it, and perhaps As for NFS, there's an interesting comment from 2007 here: “My NFS tester shows that it at least appears to work with Linux, |
My aim isn't to add all the commonly used flags, that would be pointless since its already possible using os.open. The aim is to add a missing feature to the builtin open(), i.e. file creation. At the moment open() implements read, write, and append, and creation is only implied by write. But in many cases, an explicit creation is desired (i.e, specifically create a new file, with an exception on failure). It is true that this is possible with os.open, but it is somewhat obscure, especially for beginners, and is not as easy to read as "open(file, 'c')" |
If it's supported by Windows then I'm OK (not that I personaly care
Well, I'd rather have this flag called 'x', to be consistent with """
open(2)). If the By the way, could you submit your patch as a mercurial diff ("hg diff")? |
Changing form 'c' to 'x' is easy enough, and if there is already a convention it makes sense to stick to it. I thought I had done a mercurial diff! I'll try again and resubmit. |
A minor documentation error in io.rst line 475 which was changed to:
and should have "creating" at the front I assume, like you have it later. |
Yeah, but I think "exclusively" is quite misleading since it does not |
It might be misleading, but I find it clear enough, and this name has been endorsed by POSIX. Furthermore, there's an added bonus: actually, with the old I/O layer, one can already pass an 'x' flag to open, since it just calls fopen: I don't know if it's documented behavior, but the OP in bpo-12105 was using it with python 2. Finally, when I read open('/tmp/foo', 'wx'), it's immediately clear to me what's going on, while I'd have to look at open()'s documentation to find out what the 'c' flag does. |
See bpo-12103: it is not documented. |
I hope this patch suits you better :-) I've updated the documentation typo (thanks for pointing that out). I've also changed 'c' to 'x', since I think that if there is a convention we should stick to it. I don't think that it matters if the glibc docs say 'exclusive', as long it its clear in the python docs what it does, which I think it is. Having said that, I don't really have a strong opinion either way, so I'll happily change it back to 'c' if its preferred. |
So, what was the conclusion of the discussion brought up on python-dev? |
I haven't seen any discussion on python-dev. Have I missed something? |
It was on Python-ideas actually: |
Ah, right. Well I think all arguments against it were quite weak (or even wrong). Let's see:
So this looks like a reasonable feature request IMHO. (*) http://pubs.opengroup.org/onlinepubs/007908799/xsh/open.html |
What if we can override the inner call to os.open()? Then creation mode can be expressed like this: |
I agree it would be a much better situation than what we have now. |
I see this has been marked as a duplicate of http://bugs.python.org/issue12797. Please explain how this is, since that proposal does not appear to provide the functionality discussed here. |
bpo-12797 would allow things like: def create_exclusive_file(filename):
return open(filename, "w",
opener=lambda path, mode: os.open(path, mode|os.O_CREAT|os.O_EXCL)) |
It is already possible to write a wrapper function that does it: def create(file):
fd = os.open(file, os.O_EXCL | os.O_CREAT | os.O_WRONLY)
return os.fdopen(fd) The point it not that it can't be done, but that it is not straight forward. The docs say this about os.open(): "This function is intended for low-level I/O. For normal usage, use the built-in function open()" I wouldn't call creating a new file low-level I/O, but it is normal usage. |
See bpo-13424 for a doc request about this. |
C11 uses 'x' for this, for what it's worth. This is not a "duplicate issue". The openat solution is no easier than the os.open solution. |
Ok, let's re-open then. I'm not sold on the feature, but the fact C11 adds a dedicated letter mode for it could be a good excuse for us to mimick it :) |
Amaury did not suggest to use openat, but the new opener argument to open, which was especially added for use cases such as the one discussed here:
That’s why this bug was initially closed as duplicate. |
Sorry, yes. Wrong words, same thought. We can implement this using opener, but we could implement this with os.open before. What's changed, except that there's more ways to do it? (There is slightly more versatility with the opener method, but no more obviousness and no less typing). My understanding from reading the other thread is that this is not the primary use-case of the new parameter for open(). In fact, this ticket was not really mentioned at all there. |
I agree with your opinion. I re-read this report:
I don’t have a strong opinion on “opener is generic and good enough” vs. “not as ice as it could be”. Antoine seems to agree with you, so your patch will get reviewed and eventually accepted. Cheers |
I've done a small review. |
I intend to commit this patch within a couple days (unless anyone objects of course). |
I don't think the "created()" method has to be exposed. People can inspect the "mode" attribute if they want to have that information. There's some bogus indentation in the patch (it uses tab characters in some places). Also: + if not (creating + reading or writing or appending): Not sure why the "+". Shouldn't it be "or" instead? |
Here's a new version of the patch that should address all the comments. |
Just a small note: FileExistsError is raised, not exactly OSError, when |
I've updated the doc accordingly. |
New changeset bf609baff4d3 by Charles-François Natali in branch 'default': |
Committed. |
Nick suggested to call the new flag "exclusive create" in the doc (and explain in whatsnew that it's based C11 new 'x' flag). |
I've done a bit or rewording in the io docs, but I honestly don't know if this is any better that what you already had! |
New changeset 8bcbe2dc3835 by Charles-François Natali in branch 'default': |
Thanks, I've committed your version. |
Shouldn't the documentation of builtin open() (in Doc/library/functions.rst) be updated too? |
New changeset ef406c4c6463 by Charles-François Natali in branch 'default': |
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:
The text was updated successfully, but these errors were encountered: