mirror of https://github.com/python/cpython
Generalize file.writelines() to allow iterable objects.
This commit is contained in:
parent
d31db7e939
commit
2c9aa5ea8d
|
@ -1312,8 +1312,10 @@ Files have the following methods:
|
||||||
the \method{flush()} or \method{close()} method is called.
|
the \method{flush()} or \method{close()} method is called.
|
||||||
\end{methoddesc}
|
\end{methoddesc}
|
||||||
|
|
||||||
\begin{methoddesc}[file]{writelines}{list}
|
\begin{methoddesc}[file]{writelines}{sequence}
|
||||||
Write a list of strings to the file. There is no return value.
|
Write a sequence of strings to the file. The sequence can be any
|
||||||
|
iterable object producing strings, typically a list of strings.
|
||||||
|
There is no return value.
|
||||||
(The name is intended to match \method{readlines()};
|
(The name is intended to match \method{readlines()};
|
||||||
\method{writelines()} does not add line separators.)
|
\method{writelines()} does not add line separators.)
|
||||||
\end{methoddesc}
|
\end{methoddesc}
|
||||||
|
|
|
@ -641,6 +641,59 @@ class TestCase(unittest.TestCase):
|
||||||
self.assertEqual(indexOf(iclass, i), i)
|
self.assertEqual(indexOf(iclass, i), i)
|
||||||
self.assertRaises(ValueError, indexOf, iclass, -1)
|
self.assertRaises(ValueError, indexOf, iclass, -1)
|
||||||
|
|
||||||
|
# Test iterators with file.writelines().
|
||||||
|
def test_writelines(self):
|
||||||
|
f = file(TESTFN, "w")
|
||||||
|
|
||||||
|
try:
|
||||||
|
self.assertRaises(TypeError, f.writelines, None)
|
||||||
|
self.assertRaises(TypeError, f.writelines, 42)
|
||||||
|
|
||||||
|
f.writelines(["1\n", "2\n"])
|
||||||
|
f.writelines(("3\n", "4\n"))
|
||||||
|
f.writelines({'5\n': None})
|
||||||
|
f.writelines({})
|
||||||
|
|
||||||
|
# Try a big chunk too.
|
||||||
|
class Iterator:
|
||||||
|
def __init__(self, start, finish):
|
||||||
|
self.start = start
|
||||||
|
self.finish = finish
|
||||||
|
self.i = self.start
|
||||||
|
|
||||||
|
def next(self):
|
||||||
|
if self.i >= self.finish:
|
||||||
|
raise StopIteration
|
||||||
|
result = str(self.i) + '\n'
|
||||||
|
self.i += 1
|
||||||
|
return result
|
||||||
|
|
||||||
|
def __iter__(self):
|
||||||
|
return self
|
||||||
|
|
||||||
|
class Whatever:
|
||||||
|
def __init__(self, start, finish):
|
||||||
|
self.start = start
|
||||||
|
self.finish = finish
|
||||||
|
|
||||||
|
def __iter__(self):
|
||||||
|
return Iterator(self.start, self.finish)
|
||||||
|
|
||||||
|
f.writelines(Whatever(6, 6+2000))
|
||||||
|
f.close()
|
||||||
|
|
||||||
|
f = file(TESTFN)
|
||||||
|
expected = [str(i) + "\n" for i in range(1, 2006)]
|
||||||
|
self.assertEqual(list(f), expected)
|
||||||
|
|
||||||
|
finally:
|
||||||
|
f.close()
|
||||||
|
try:
|
||||||
|
unlink(TESTFN)
|
||||||
|
except OSError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
# Test iterators on RHS of unpacking assignments.
|
# Test iterators on RHS of unpacking assignments.
|
||||||
def test_unpack_iter(self):
|
def test_unpack_iter(self):
|
||||||
a, b = 1, 2
|
a, b = 1, 2
|
||||||
|
|
|
@ -3,6 +3,8 @@ What's New in Python 2.2a4?
|
||||||
|
|
||||||
Core
|
Core
|
||||||
|
|
||||||
|
- file.writelines() now accepts any iterable object producing strings.
|
||||||
|
|
||||||
- PyUnicode_FromEncodedObject() now works very much like
|
- PyUnicode_FromEncodedObject() now works very much like
|
||||||
PyObject_Str(obj) in that it tries to use __str__/tp_str
|
PyObject_Str(obj) in that it tries to use __str__/tp_str
|
||||||
on the object if the object is not a string or buffer. This
|
on the object if the object is not a string or buffer. This
|
||||||
|
|
|
@ -1164,55 +1164,54 @@ file_write(PyFileObject *f, PyObject *args)
|
||||||
}
|
}
|
||||||
|
|
||||||
static PyObject *
|
static PyObject *
|
||||||
file_writelines(PyFileObject *f, PyObject *args)
|
file_writelines(PyFileObject *f, PyObject *seq)
|
||||||
{
|
{
|
||||||
#define CHUNKSIZE 1000
|
#define CHUNKSIZE 1000
|
||||||
PyObject *list, *line;
|
PyObject *list, *line;
|
||||||
|
PyObject *it; /* iter(seq) */
|
||||||
PyObject *result;
|
PyObject *result;
|
||||||
int i, j, index, len, nwritten, islist;
|
int i, j, index, len, nwritten, islist;
|
||||||
|
|
||||||
|
assert(seq != NULL);
|
||||||
if (f->f_fp == NULL)
|
if (f->f_fp == NULL)
|
||||||
return err_closed();
|
return err_closed();
|
||||||
if (args == NULL || !PySequence_Check(args)) {
|
|
||||||
PyErr_SetString(PyExc_TypeError,
|
result = NULL;
|
||||||
"writelines() argument must be a sequence of strings");
|
list = NULL;
|
||||||
return NULL;
|
islist = PyList_Check(seq);
|
||||||
|
if (islist)
|
||||||
|
it = NULL;
|
||||||
|
else {
|
||||||
|
it = PyObject_GetIter(seq);
|
||||||
|
if (it == NULL) {
|
||||||
|
PyErr_SetString(PyExc_TypeError,
|
||||||
|
"writelines() requires an iterable argument");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
/* From here on, fail by going to error, to reclaim "it". */
|
||||||
|
list = PyList_New(CHUNKSIZE);
|
||||||
|
if (list == NULL)
|
||||||
|
goto error;
|
||||||
}
|
}
|
||||||
islist = PyList_Check(args);
|
|
||||||
|
|
||||||
/* Strategy: slurp CHUNKSIZE lines into a private list,
|
/* Strategy: slurp CHUNKSIZE lines into a private list,
|
||||||
checking that they are all strings, then write that list
|
checking that they are all strings, then write that list
|
||||||
without holding the interpreter lock, then come back for more. */
|
without holding the interpreter lock, then come back for more. */
|
||||||
index = 0;
|
for (index = 0; ; index += CHUNKSIZE) {
|
||||||
if (islist)
|
|
||||||
list = NULL;
|
|
||||||
else {
|
|
||||||
list = PyList_New(CHUNKSIZE);
|
|
||||||
if (list == NULL)
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
result = NULL;
|
|
||||||
|
|
||||||
for (;;) {
|
|
||||||
if (islist) {
|
if (islist) {
|
||||||
Py_XDECREF(list);
|
Py_XDECREF(list);
|
||||||
list = PyList_GetSlice(args, index, index+CHUNKSIZE);
|
list = PyList_GetSlice(seq, index, index+CHUNKSIZE);
|
||||||
if (list == NULL)
|
if (list == NULL)
|
||||||
return NULL;
|
goto error;
|
||||||
j = PyList_GET_SIZE(list);
|
j = PyList_GET_SIZE(list);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
for (j = 0; j < CHUNKSIZE; j++) {
|
for (j = 0; j < CHUNKSIZE; j++) {
|
||||||
line = PySequence_GetItem(args, index+j);
|
line = PyIter_Next(it);
|
||||||
if (line == NULL) {
|
if (line == NULL) {
|
||||||
if (PyErr_ExceptionMatches(
|
if (PyErr_Occurred())
|
||||||
PyExc_IndexError)) {
|
goto error;
|
||||||
PyErr_Clear();
|
break;
|
||||||
break;
|
|
||||||
}
|
|
||||||
/* Some other error occurred.
|
|
||||||
XXX We may lose some output. */
|
|
||||||
goto error;
|
|
||||||
}
|
}
|
||||||
PyList_SetItem(list, j, line);
|
PyList_SetItem(list, j, line);
|
||||||
}
|
}
|
||||||
|
@ -1271,14 +1270,15 @@ file_writelines(PyFileObject *f, PyObject *args)
|
||||||
|
|
||||||
if (j < CHUNKSIZE)
|
if (j < CHUNKSIZE)
|
||||||
break;
|
break;
|
||||||
index += CHUNKSIZE;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Py_INCREF(Py_None);
|
Py_INCREF(Py_None);
|
||||||
result = Py_None;
|
result = Py_None;
|
||||||
error:
|
error:
|
||||||
Py_XDECREF(list);
|
Py_XDECREF(list);
|
||||||
|
Py_XDECREF(it);
|
||||||
return result;
|
return result;
|
||||||
|
#undef CHUNKSIZE
|
||||||
}
|
}
|
||||||
|
|
||||||
static char readline_doc[] =
|
static char readline_doc[] =
|
||||||
|
@ -1342,10 +1342,10 @@ static char xreadlines_doc[] =
|
||||||
"often quicker, due to reading ahead internally.";
|
"often quicker, due to reading ahead internally.";
|
||||||
|
|
||||||
static char writelines_doc[] =
|
static char writelines_doc[] =
|
||||||
"writelines(list of strings) -> None. Write the strings to the file.\n"
|
"writelines(sequence_of_strings) -> None. Write the strings to the file.\n"
|
||||||
"\n"
|
"\n"
|
||||||
"Note that newlines are not added. This is equivalent to calling write()\n"
|
"Note that newlines are not added. The sequence can be any iterable object\n"
|
||||||
"for each string in the list.";
|
"producing strings. This is equivalent to calling write() for each string.";
|
||||||
|
|
||||||
static char flush_doc[] =
|
static char flush_doc[] =
|
||||||
"flush() -> None. Flush the internal I/O buffer.";
|
"flush() -> None. Flush the internal I/O buffer.";
|
||||||
|
|
|
@ -118,8 +118,7 @@ def main(args):
|
||||||
|
|
||||||
def restore(which):
|
def restore(which):
|
||||||
restored = difflib.restore(sys.stdin.readlines(), which)
|
restored = difflib.restore(sys.stdin.readlines(), which)
|
||||||
for line in restored:
|
sys.stdout.writelines(restored)
|
||||||
print line,
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
args = sys.argv[1:]
|
args = sys.argv[1:]
|
||||||
|
|
Loading…
Reference in New Issue