Issue #12760: Add a create mode to open(). Patch by David Townshend.
This commit is contained in:
parent
8a9b9c7d16
commit
dc3044c704
|
@ -471,10 +471,13 @@ Raw File I/O
|
|||
* an integer representing the number of an existing OS-level file descriptor
|
||||
to which the resulting :class:`FileIO` object will give access.
|
||||
|
||||
The *mode* can be ``'r'``, ``'w'`` or ``'a'`` for reading (default), writing,
|
||||
or appending. The file will be created if it doesn't exist when opened for
|
||||
writing or appending; it will be truncated when opened for writing. Add a
|
||||
``'+'`` to the mode to allow simultaneous reading and writing.
|
||||
The *mode* can be ``'r'``, ``'w'``, ``'x'`` or ``'a'`` for reading
|
||||
(default), writing, creating or appending. The file will be created if it
|
||||
doesn't exist when opened for writing or appending; it will be truncated
|
||||
when opened for writing. :exc:`FileExistsError` will be raised if it already
|
||||
exists when opened for creating. Opening a file for creating implies
|
||||
writing, so this mode behaves in a similar way to ``'w'``. Add a ``'+'`` to
|
||||
the mode to allow simultaneous reading and writing.
|
||||
|
||||
The :meth:`read` (when called with a positive argument), :meth:`readinto`
|
||||
and :meth:`write` methods on this class will only make one system call.
|
||||
|
@ -487,6 +490,7 @@ Raw File I/O
|
|||
|
||||
.. versionchanged:: 3.3
|
||||
The *opener* parameter was added.
|
||||
The ``'x'`` mode was added.
|
||||
|
||||
In addition to the attributes and methods from :class:`IOBase` and
|
||||
:class:`RawIOBase`, :class:`FileIO` provides the following data
|
||||
|
|
|
@ -591,7 +591,8 @@ These functions create new :term:`file objects <file object>`. (See also :func:`
|
|||
the built-in :func:`open` function.
|
||||
|
||||
When specified, the *mode* argument must start with one of the letters
|
||||
``'r'``, ``'w'``, or ``'a'``, otherwise a :exc:`ValueError` is raised.
|
||||
``'r'``, ``'w'``, ``'x'`` or ``'a'``, otherwise a :exc:`ValueError` is
|
||||
raised.
|
||||
|
||||
On Unix, when the *mode* argument starts with ``'a'``, the *O_APPEND* flag is
|
||||
set on the file descriptor (which the :c:func:`fdopen` implementation already
|
||||
|
@ -599,6 +600,8 @@ These functions create new :term:`file objects <file object>`. (See also :func:`
|
|||
|
||||
Availability: Unix, Windows.
|
||||
|
||||
.. versionchanged:: 3.3
|
||||
The ``'x'`` mode was added.
|
||||
|
||||
.. _os-fd-ops:
|
||||
|
||||
|
|
|
@ -408,6 +408,15 @@ parameter to control parameters of the secure channel.
|
|||
(Contributed by Sijin Joseph in :issue:`8808`)
|
||||
|
||||
|
||||
io
|
||||
--
|
||||
|
||||
The :func:`~io.open` function has a new ``'x'`` mode that can be used to create
|
||||
a new file, and raise a :exc:`FileExistsError` if the file already exists.
|
||||
|
||||
(Contributed by David Townshend in :issue:`12760`)
|
||||
|
||||
|
||||
lzma
|
||||
----
|
||||
|
||||
|
|
34
Lib/_pyio.py
34
Lib/_pyio.py
|
@ -38,21 +38,22 @@ def open(file, mode="r", buffering=-1, encoding=None, errors=None,
|
|||
wrapped. (If a file descriptor is given, it is closed when the
|
||||
returned I/O object is closed, unless closefd is set to False.)
|
||||
|
||||
mode is an optional string that specifies the mode in which the file
|
||||
is opened. It defaults to 'r' which means open for reading in text
|
||||
mode. Other common values are 'w' for writing (truncating the file if
|
||||
it already exists), and 'a' for appending (which on some Unix systems,
|
||||
means that all writes append to the end of the file regardless of the
|
||||
current seek position). In text mode, if encoding is not specified the
|
||||
encoding used is platform dependent. (For reading and writing raw
|
||||
bytes use binary mode and leave encoding unspecified.) The available
|
||||
modes are:
|
||||
mode is an optional string that specifies the mode in which the file is
|
||||
opened. It defaults to 'r' which means open for reading in text mode. Other
|
||||
common values are 'w' for writing (truncating the file if it already
|
||||
exists), 'x' for creating and writing to a new file, and 'a' for appending
|
||||
(which on some Unix systems, means that all writes append to the end of the
|
||||
file regardless of the current seek position). In text mode, if encoding is
|
||||
not specified the encoding used is platform dependent. (For reading and
|
||||
writing raw bytes use binary mode and leave encoding unspecified.) The
|
||||
available modes are:
|
||||
|
||||
========= ===============================================================
|
||||
Character Meaning
|
||||
--------- ---------------------------------------------------------------
|
||||
'r' open for reading (default)
|
||||
'w' open for writing, truncating the file first
|
||||
'x' create a new file and open it for writing
|
||||
'a' open for writing, appending to the end of the file if it exists
|
||||
'b' binary mode
|
||||
't' text mode (default)
|
||||
|
@ -63,7 +64,8 @@ def open(file, mode="r", buffering=-1, encoding=None, errors=None,
|
|||
|
||||
The default mode is 'rt' (open for reading text). For binary random
|
||||
access, the mode 'w+b' opens and truncates the file to 0 bytes, while
|
||||
'r+b' opens the file without truncation.
|
||||
'r+b' opens the file without truncation. The 'x' mode implies 'w' and
|
||||
raises an `FileExistsError` if the file already exists.
|
||||
|
||||
Python distinguishes between files opened in binary and text modes,
|
||||
even when the underlying operating system doesn't. Files opened in
|
||||
|
@ -154,8 +156,9 @@ def open(file, mode="r", buffering=-1, encoding=None, errors=None,
|
|||
if errors is not None and not isinstance(errors, str):
|
||||
raise TypeError("invalid errors: %r" % errors)
|
||||
modes = set(mode)
|
||||
if modes - set("arwb+tU") or len(mode) > len(modes):
|
||||
if modes - set("axrwb+tU") or len(mode) > len(modes):
|
||||
raise ValueError("invalid mode: %r" % mode)
|
||||
creating = "x" in modes
|
||||
reading = "r" in modes
|
||||
writing = "w" in modes
|
||||
appending = "a" in modes
|
||||
|
@ -163,14 +166,14 @@ def open(file, mode="r", buffering=-1, encoding=None, errors=None,
|
|||
text = "t" in modes
|
||||
binary = "b" in modes
|
||||
if "U" in modes:
|
||||
if writing or appending:
|
||||
if creating or writing or appending:
|
||||
raise ValueError("can't use U and writing mode at once")
|
||||
reading = True
|
||||
if text and binary:
|
||||
raise ValueError("can't have text and binary mode at once")
|
||||
if reading + writing + appending > 1:
|
||||
if creating + reading + writing + appending > 1:
|
||||
raise ValueError("can't have read/write/append mode at once")
|
||||
if not (reading or writing or appending):
|
||||
if not (creating or reading or writing or appending):
|
||||
raise ValueError("must have exactly one of read/write/append mode")
|
||||
if binary and encoding is not None:
|
||||
raise ValueError("binary mode doesn't take an encoding argument")
|
||||
|
@ -179,6 +182,7 @@ def open(file, mode="r", buffering=-1, encoding=None, errors=None,
|
|||
if binary and newline is not None:
|
||||
raise ValueError("binary mode doesn't take a newline argument")
|
||||
raw = FileIO(file,
|
||||
(creating and "x" or "") +
|
||||
(reading and "r" or "") +
|
||||
(writing and "w" or "") +
|
||||
(appending and "a" or "") +
|
||||
|
@ -205,7 +209,7 @@ def open(file, mode="r", buffering=-1, encoding=None, errors=None,
|
|||
raise ValueError("can't have unbuffered text I/O")
|
||||
if updating:
|
||||
buffer = BufferedRandom(raw, buffering)
|
||||
elif writing or appending:
|
||||
elif creating or writing or appending:
|
||||
buffer = BufferedWriter(raw, buffering)
|
||||
elif reading:
|
||||
buffer = BufferedReader(raw, buffering)
|
||||
|
|
|
@ -2825,6 +2825,19 @@ class MiscIOTest(unittest.TestCase):
|
|||
self.assertTrue(wf.closed)
|
||||
self.assertTrue(rf.closed)
|
||||
|
||||
def test_create_fail(self):
|
||||
# 'x' mode fails if file is existing
|
||||
with self.open(support.TESTFN, 'w'):
|
||||
pass
|
||||
self.assertRaises(FileExistsError, self.open, support.TESTFN, 'x')
|
||||
|
||||
def test_create_writes(self):
|
||||
# 'x' mode opens for writing
|
||||
with self.open(support.TESTFN, 'xb') as f:
|
||||
f.write(b"spam")
|
||||
with self.open(support.TESTFN, 'rb') as f:
|
||||
self.assertEqual(b"spam", f.read())
|
||||
|
||||
class CMiscIOTest(MiscIOTest):
|
||||
io = io
|
||||
|
||||
|
|
|
@ -996,6 +996,7 @@ Erik Tollerud
|
|||
Matias Torchinsky
|
||||
Sandro Tosi
|
||||
Richard Townsend
|
||||
David Townshend
|
||||
Laurence Tratt
|
||||
Matthias Troffaes
|
||||
John Tromp
|
||||
|
|
|
@ -10,6 +10,8 @@ What's New in Python 3.3 Alpha 1?
|
|||
Core and Builtins
|
||||
-----------------
|
||||
|
||||
- Issue #12760: Add a create mode to open(). Patch by David Townshend.
|
||||
|
||||
- Issue #13738: Simplify implementation of bytes.lower() and bytes.upper().
|
||||
|
||||
- Issue #13577: Built-in methods and functions now have a __qualname__.
|
||||
|
|
|
@ -108,18 +108,19 @@ PyDoc_STRVAR(open_doc,
|
|||
"mode is an optional string that specifies the mode in which the file\n"
|
||||
"is opened. It defaults to 'r' which means open for reading in text\n"
|
||||
"mode. Other common values are 'w' for writing (truncating the file if\n"
|
||||
"it already exists), and 'a' for appending (which on some Unix systems,\n"
|
||||
"means that all writes append to the end of the file regardless of the\n"
|
||||
"current seek position). In text mode, if encoding is not specified the\n"
|
||||
"encoding used is platform dependent. (For reading and writing raw\n"
|
||||
"bytes use binary mode and leave encoding unspecified.) The available\n"
|
||||
"modes are:\n"
|
||||
"it already exists), 'x' for creating and writing to a new file, and\n"
|
||||
"'a' for appending (which on some Unix systems, means that all writes\n"
|
||||
"append to the end of the file regardless of the current seek position).\n"
|
||||
"In text mode, if encoding is not specified the encoding used is platform\n"
|
||||
"dependent. (For reading and writing raw bytes use binary mode and leave\n"
|
||||
"encoding unspecified.) The available modes are:\n"
|
||||
"\n"
|
||||
"========= ===============================================================\n"
|
||||
"Character Meaning\n"
|
||||
"--------- ---------------------------------------------------------------\n"
|
||||
"'r' open for reading (default)\n"
|
||||
"'w' open for writing, truncating the file first\n"
|
||||
"'x' create a new file and open it for writing\n"
|
||||
"'a' open for writing, appending to the end of the file if it exists\n"
|
||||
"'b' binary mode\n"
|
||||
"'t' text mode (default)\n"
|
||||
|
@ -130,7 +131,8 @@ PyDoc_STRVAR(open_doc,
|
|||
"\n"
|
||||
"The default mode is 'rt' (open for reading text). For binary random\n"
|
||||
"access, the mode 'w+b' opens and truncates the file to 0 bytes, while\n"
|
||||
"'r+b' opens the file without truncation.\n"
|
||||
"'r+b' opens the file without truncation. The 'x' mode implies 'w' and\n"
|
||||
"raises an `FileExistsError` if the file already exists.\n"
|
||||
"\n"
|
||||
"Python distinguishes between files opened in binary and text modes,\n"
|
||||
"even when the underlying operating system doesn't. Files opened in\n"
|
||||
|
@ -223,7 +225,7 @@ io_open(PyObject *self, PyObject *args, PyObject *kwds)
|
|||
char *encoding = NULL, *errors = NULL, *newline = NULL;
|
||||
unsigned i;
|
||||
|
||||
int reading = 0, writing = 0, appending = 0, updating = 0;
|
||||
int creating = 0, reading = 0, writing = 0, appending = 0, updating = 0;
|
||||
int text = 0, binary = 0, universal = 0;
|
||||
|
||||
char rawmode[5], *m;
|
||||
|
@ -254,6 +256,9 @@ io_open(PyObject *self, PyObject *args, PyObject *kwds)
|
|||
char c = mode[i];
|
||||
|
||||
switch (c) {
|
||||
case 'x':
|
||||
creating = 1;
|
||||
break;
|
||||
case 'r':
|
||||
reading = 1;
|
||||
break;
|
||||
|
@ -290,6 +295,7 @@ io_open(PyObject *self, PyObject *args, PyObject *kwds)
|
|||
}
|
||||
|
||||
m = rawmode;
|
||||
if (creating) *(m++) = 'x';
|
||||
if (reading) *(m++) = 'r';
|
||||
if (writing) *(m++) = 'w';
|
||||
if (appending) *(m++) = 'a';
|
||||
|
@ -312,9 +318,9 @@ io_open(PyObject *self, PyObject *args, PyObject *kwds)
|
|||
return NULL;
|
||||
}
|
||||
|
||||
if (reading + writing + appending > 1) {
|
||||
if (creating + reading + writing + appending > 1) {
|
||||
PyErr_SetString(PyExc_ValueError,
|
||||
"must have exactly one of read/write/append mode");
|
||||
"must have exactly one of create/read/write/append mode");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
@ -408,7 +414,7 @@ io_open(PyObject *self, PyObject *args, PyObject *kwds)
|
|||
|
||||
if (updating)
|
||||
Buffered_class = (PyObject *)&PyBufferedRandom_Type;
|
||||
else if (writing || appending)
|
||||
else if (creating || writing || appending)
|
||||
Buffered_class = (PyObject *)&PyBufferedWriter_Type;
|
||||
else if (reading)
|
||||
Buffered_class = (PyObject *)&PyBufferedReader_Type;
|
||||
|
|
|
@ -46,6 +46,7 @@
|
|||
typedef struct {
|
||||
PyObject_HEAD
|
||||
int fd;
|
||||
unsigned int created : 1;
|
||||
unsigned int readable : 1;
|
||||
unsigned int writable : 1;
|
||||
signed int seekable : 2; /* -1 means unknown */
|
||||
|
@ -152,6 +153,7 @@ fileio_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
|
|||
self = (fileio *) type->tp_alloc(type, 0);
|
||||
if (self != NULL) {
|
||||
self->fd = -1;
|
||||
self->created = 0;
|
||||
self->readable = 0;
|
||||
self->writable = 0;
|
||||
self->seekable = -1;
|
||||
|
@ -290,15 +292,23 @@ fileio_init(PyObject *oself, PyObject *args, PyObject *kwds)
|
|||
s = mode;
|
||||
while (*s) {
|
||||
switch (*s++) {
|
||||
case 'r':
|
||||
case 'x':
|
||||
if (rwa) {
|
||||
bad_mode:
|
||||
PyErr_SetString(PyExc_ValueError,
|
||||
"Must have exactly one of read/write/append "
|
||||
"Must have exactly one of create/read/write/append "
|
||||
"mode and at most one plus");
|
||||
goto error;
|
||||
}
|
||||
rwa = 1;
|
||||
self->created = 1;
|
||||
self->writable = 1;
|
||||
flags |= O_EXCL | O_CREAT;
|
||||
break;
|
||||
case 'r':
|
||||
if (rwa)
|
||||
goto bad_mode;
|
||||
rwa = 1;
|
||||
self->readable = 1;
|
||||
break;
|
||||
case 'w':
|
||||
|
@ -988,6 +998,12 @@ fileio_truncate(fileio *self, PyObject *args)
|
|||
static char *
|
||||
mode_string(fileio *self)
|
||||
{
|
||||
if (self->created) {
|
||||
if (self->readable)
|
||||
return "xb+";
|
||||
else
|
||||
return "xb";
|
||||
}
|
||||
if (self->readable) {
|
||||
if (self->writable)
|
||||
return "rb+";
|
||||
|
@ -1049,15 +1065,17 @@ fileio_getstate(fileio *self)
|
|||
PyDoc_STRVAR(fileio_doc,
|
||||
"file(name: str[, mode: str][, opener: None]) -> file IO object\n"
|
||||
"\n"
|
||||
"Open a file. The mode can be 'r', 'w' or 'a' for reading (default),\n"
|
||||
"writing or appending. The file will be created if it doesn't exist\n"
|
||||
"when opened for writing or appending; it will be truncated when\n"
|
||||
"opened for writing. Add a '+' to the mode to allow simultaneous\n"
|
||||
"reading and writing. A custom opener can be used by passing a\n"
|
||||
"callable as *opener*. The underlying file descriptor for the file\n"
|
||||
"Open a file. The mode can be 'r', 'w', 'x' or 'a' for reading (default),\n"
|
||||
"writing, creating or appending. The file will be created if it doesn't\n"
|
||||
"exist when opened for writing or appending; it will be truncated when\n"
|
||||
"opened for writing. A `FileExistsError` will be raised if it already\n"
|
||||
"exists when opened for creating. Opening a file for creating implies\n"
|
||||
"writing so this mode behaves in a similar way to 'w'.Add a '+' to the mode\n"
|
||||
"to allow simultaneous reading and writing. A custom opener can be used by\n"
|
||||
"passing a callable as *opener*. The underlying file descriptor for the file\n"
|
||||
"object is then obtained by calling opener with (*name*, *flags*).\n"
|
||||
"*opener* must return an open file descriptor (passing os.open as\n"
|
||||
"*opener* results in functionality similar to passing None).");
|
||||
"*opener* must return an open file descriptor (passing os.open as *opener*\n"
|
||||
"results in functionality similar to passing None).");
|
||||
|
||||
PyDoc_STRVAR(read_doc,
|
||||
"read(size: int) -> bytes. read at most size bytes, returned as bytes.\n"
|
||||
|
|
Loading…
Reference in New Issue