Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 2c9aa5e

Browse files
committed
Generalize file.writelines() to allow iterable objects.
1 parent d31db7e commit 2c9aa5e

5 files changed

Lines changed: 91 additions & 35 deletions

File tree

Doc/lib/libstdtypes.tex

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1312,8 +1312,10 @@ \subsubsection{File Objects\obindex{file}
13121312
the \method{flush()} or \method{close()} method is called.
13131313
\end{methoddesc}
13141314

1315-
\begin{methoddesc}[file]{writelines}{list}
1316-
Write a list of strings to the file. There is no return value.
1315+
\begin{methoddesc}[file]{writelines}{sequence}
1316+
Write a sequence of strings to the file. The sequence can be any
1317+
iterable object producing strings, typically a list of strings.
1318+
There is no return value.
13171319
(The name is intended to match \method{readlines()};
13181320
\method{writelines()} does not add line separators.)
13191321
\end{methoddesc}

Lib/test/test_iter.py

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -641,6 +641,59 @@ def test_indexOf(self):
641641
self.assertEqual(indexOf(iclass, i), i)
642642
self.assertRaises(ValueError, indexOf, iclass, -1)
643643

644+
# Test iterators with file.writelines().
645+
def test_writelines(self):
646+
f = file(TESTFN, "w")
647+
648+
try:
649+
self.assertRaises(TypeError, f.writelines, None)
650+
self.assertRaises(TypeError, f.writelines, 42)
651+
652+
f.writelines(["1\n", "2\n"])
653+
f.writelines(("3\n", "4\n"))
654+
f.writelines({'5\n': None})
655+
f.writelines({})
656+
657+
# Try a big chunk too.
658+
class Iterator:
659+
def __init__(self, start, finish):
660+
self.start = start
661+
self.finish = finish
662+
self.i = self.start
663+
664+
def next(self):
665+
if self.i >= self.finish:
666+
raise StopIteration
667+
result = str(self.i) + '\n'
668+
self.i += 1
669+
return result
670+
671+
def __iter__(self):
672+
return self
673+
674+
class Whatever:
675+
def __init__(self, start, finish):
676+
self.start = start
677+
self.finish = finish
678+
679+
def __iter__(self):
680+
return Iterator(self.start, self.finish)
681+
682+
f.writelines(Whatever(6, 6+2000))
683+
f.close()
684+
685+
f = file(TESTFN)
686+
expected = [str(i) + "\n" for i in range(1, 2006)]
687+
self.assertEqual(list(f), expected)
688+
689+
finally:
690+
f.close()
691+
try:
692+
unlink(TESTFN)
693+
except OSError:
694+
pass
695+
696+
644697
# Test iterators on RHS of unpacking assignments.
645698
def test_unpack_iter(self):
646699
a, b = 1, 2

Misc/NEWS

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ What's New in Python 2.2a4?
33

44
Core
55

6+
- file.writelines() now accepts any iterable object producing strings.
7+
68
- PyUnicode_FromEncodedObject() now works very much like
79
PyObject_Str(obj) in that it tries to use __str__/tp_str
810
on the object if the object is not a string or buffer. This

Objects/fileobject.c

Lines changed: 31 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1164,55 +1164,54 @@ file_write(PyFileObject *f, PyObject *args)
11641164
}
11651165

11661166
static PyObject *
1167-
file_writelines(PyFileObject *f, PyObject *args)
1167+
file_writelines(PyFileObject *f, PyObject *seq)
11681168
{
11691169
#define CHUNKSIZE 1000
11701170
PyObject *list, *line;
1171+
PyObject *it; /* iter(seq) */
11711172
PyObject *result;
11721173
int i, j, index, len, nwritten, islist;
11731174

1175+
assert(seq != NULL);
11741176
if (f->f_fp == NULL)
11751177
return err_closed();
1176-
if (args == NULL || !PySequence_Check(args)) {
1177-
PyErr_SetString(PyExc_TypeError,
1178-
"writelines() argument must be a sequence of strings");
1179-
return NULL;
1180-
}
1181-
islist = PyList_Check(args);
11821178

1183-
/* Strategy: slurp CHUNKSIZE lines into a private list,
1184-
checking that they are all strings, then write that list
1185-
without holding the interpreter lock, then come back for more. */
1186-
index = 0;
1187-
if (islist)
1188-
list = NULL;
1179+
result = NULL;
1180+
list = NULL;
1181+
islist = PyList_Check(seq);
1182+
if (islist)
1183+
it = NULL;
11891184
else {
1185+
it = PyObject_GetIter(seq);
1186+
if (it == NULL) {
1187+
PyErr_SetString(PyExc_TypeError,
1188+
"writelines() requires an iterable argument");
1189+
return NULL;
1190+
}
1191+
/* From here on, fail by going to error, to reclaim "it". */
11901192
list = PyList_New(CHUNKSIZE);
11911193
if (list == NULL)
1192-
return NULL;
1194+
goto error;
11931195
}
1194-
result = NULL;
11951196

1196-
for (;;) {
1197+
/* Strategy: slurp CHUNKSIZE lines into a private list,
1198+
checking that they are all strings, then write that list
1199+
without holding the interpreter lock, then come back for more. */
1200+
for (index = 0; ; index += CHUNKSIZE) {
11971201
if (islist) {
11981202
Py_XDECREF(list);
1199-
list = PyList_GetSlice(args, index, index+CHUNKSIZE);
1203+
list = PyList_GetSlice(seq, index, index+CHUNKSIZE);
12001204
if (list == NULL)
1201-
return NULL;
1205+
goto error;
12021206
j = PyList_GET_SIZE(list);
12031207
}
12041208
else {
12051209
for (j = 0; j < CHUNKSIZE; j++) {
1206-
line = PySequence_GetItem(args, index+j);
1210+
line = PyIter_Next(it);
12071211
if (line == NULL) {
1208-
if (PyErr_ExceptionMatches(
1209-
PyExc_IndexError)) {
1210-
PyErr_Clear();
1211-
break;
1212-
}
1213-
/* Some other error occurred.
1214-
XXX We may lose some output. */
1215-
goto error;
1212+
if (PyErr_Occurred())
1213+
goto error;
1214+
break;
12161215
}
12171216
PyList_SetItem(list, j, line);
12181217
}
@@ -1271,14 +1270,15 @@ file_writelines(PyFileObject *f, PyObject *args)
12711270

12721271
if (j < CHUNKSIZE)
12731272
break;
1274-
index += CHUNKSIZE;
12751273
}
12761274

12771275
Py_INCREF(Py_None);
12781276
result = Py_None;
12791277
error:
12801278
Py_XDECREF(list);
1279+
Py_XDECREF(it);
12811280
return result;
1281+
#undef CHUNKSIZE
12821282
}
12831283

12841284
static char readline_doc[] =
@@ -1342,10 +1342,10 @@ static char xreadlines_doc[] =
13421342
"often quicker, due to reading ahead internally.";
13431343

13441344
static char writelines_doc[] =
1345-
"writelines(list of strings) -> None. Write the strings to the file.\n"
1345+
"writelines(sequence_of_strings) -> None. Write the strings to the file.\n"
13461346
"\n"
1347-
"Note that newlines are not added. This is equivalent to calling write()\n"
1348-
"for each string in the list.";
1347+
"Note that newlines are not added. The sequence can be any iterable object\n"
1348+
"producing strings. This is equivalent to calling write() for each string.";
13491349

13501350
static char flush_doc[] =
13511351
"flush() -> None. Flush the internal I/O buffer.";

Tools/scripts/ndiff.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -118,8 +118,7 @@ def main(args):
118118

119119
def restore(which):
120120
restored = difflib.restore(sys.stdin.readlines(), which)
121-
for line in restored:
122-
print line,
121+
sys.stdout.writelines(restored)
123122

124123
if __name__ == '__main__':
125124
args = sys.argv[1:]

0 commit comments

Comments
 (0)