Checking in the new, improve file.writelines() code.
authorGuido van Rossum <guido@python.org>
Mon, 13 Mar 2000 16:27:06 +0000 (16:27 +0000)
committerGuido van Rossum <guido@python.org>
Mon, 13 Mar 2000 16:27:06 +0000 (16:27 +0000)
This (1) avoids thread unsafety whereby another thread could zap the
list while we were using it, and (2) now supports writing arbitrary
sequences of strings.

Objects/fileobject.c

index 60c113ff39e71a3b7129303f5e2f499362d3cac5..c8b083e5f238ff46e7b23e554930cc452fae0939 100644 (file)
@@ -890,40 +890,94 @@ file_writelines(f, args)
        PyFileObject *f;
        PyObject *args;
 {
-       int i, n;
+#define CHUNKSIZE 1000
+       PyObject *list, *line;
+       PyObject *result;
+       int i, j, index, len, nwritten, islist;
+
        if (f->f_fp == NULL)
                return err_closed();
-       if (args == NULL || !PyList_Check(args)) {
+       if (args == NULL || !PySequence_Check(args)) {
                PyErr_SetString(PyExc_TypeError,
-                          "writelines() requires list of strings");
+                          "writelines() requires sequence of strings");
                return NULL;
        }
-       n = PyList_Size(args);
-       f->f_softspace = 0;
-       Py_BEGIN_ALLOW_THREADS
-       errno = 0;
-       for (i = 0; i < n; i++) {
-               PyObject *line = PyList_GetItem(args, i);
-               int len;
-               int nwritten;
-               if (!PyString_Check(line)) {
-                       Py_BLOCK_THREADS
-                       PyErr_SetString(PyExc_TypeError,
-                                  "writelines() requires list of strings");
+       islist = PyList_Check(args);
+
+       /* Strategy: slurp CHUNKSIZE lines into a private list,
+          checking that they are all strings, then write that list
+          without holding the interpreter lock, then come back for more. */
+       index = 0;
+       if (islist)
+               list = NULL;
+       else {
+               list = PyList_New(CHUNKSIZE);
+               if (list == NULL)
                        return NULL;
+       }
+       result = NULL;
+
+       for (;;) {
+               if (islist) {
+                       Py_XDECREF(list);
+                       list = PyList_GetSlice(args, index, index+CHUNKSIZE);
+                       if (list == NULL)
+                               return NULL;
+                       j = PyList_GET_SIZE(list);
                }
-               len = PyString_Size(line);
-               nwritten = fwrite(PyString_AsString(line), 1, len, f->f_fp);
-               if (nwritten != len) {
-                       Py_BLOCK_THREADS
-                       PyErr_SetFromErrno(PyExc_IOError);
-                       clearerr(f->f_fp);
-                       return NULL;
+               else {
+                       for (j = 0; j < CHUNKSIZE; j++) {
+                               line = PySequence_GetItem(args, index+j);
+                               if (line == NULL) {
+                                       if (PyErr_ExceptionMatches(
+                                               PyExc_IndexError))
+                                       {
+                                               PyErr_Clear();
+                                               break;
+                                       }
+                                       /* Some other error occurred.
+                                          XXX We may lose some output. */
+                                       goto error;
+                               }
+                               if (!PyString_Check(line)) {
+                                       Py_DECREF(line);
+                                       PyErr_SetString(PyExc_TypeError,
+                                "writelines() requires sequences of strings");
+                                       goto error;
+                               }
+                               PyList_SetItem(list, j, line);
+                       }
                }
+               if (j == 0)
+                       break;
+
+               Py_BEGIN_ALLOW_THREADS
+               f->f_softspace = 0;
+               errno = 0;
+               for (i = 0; i < j; i++) {
+                       line = PyList_GET_ITEM(list, i);
+                       len = PyString_GET_SIZE(line);
+                       nwritten = fwrite(PyString_AS_STRING(line),
+                                         1, len, f->f_fp);
+                       if (nwritten != len) {
+                               Py_BLOCK_THREADS
+                               PyErr_SetFromErrno(PyExc_IOError);
+                               clearerr(f->f_fp);
+                               goto error;
+                       }
+               }
+               Py_END_ALLOW_THREADS
+
+               if (j < CHUNKSIZE)
+                       break;
+               index += CHUNKSIZE;
        }
-       Py_END_ALLOW_THREADS
+
        Py_INCREF(Py_None);
-       return Py_None;
+       result = Py_None;
+  error:
+       Py_XDECREF(list);
+       return result;
 }
 
 static PyMethodDef file_methods[] = {