Unparenting BZ2File, as discussed in SF patch #661796.
* Modules/bz2module.c
(BZ2FileObject): Now the structure includes a pointer to a file object,
instead of "inheriting" one. Also, some members were copied from the
PyFileObject structure to avoid dealing with the internals of that
structure from outside fileobject.c.
(Util_GetLine,Util_DropReadAhead,Util_ReadAhead,Util_ReadAheadGetLineSkip,
BZ2File_write,BZ2File_writelines,BZ2File_init,BZ2File_dealloc,
BZ2Comp_dealloc,BZ2Decomp_dealloc):
These functions were adapted to the change above.
(BZ2File_seek,BZ2File_close): Use PyObject_CallMethod instead of
getting the function attribute locally.
(BZ2File_notsup): Removed, since it's not necessary anymore to overload
truncate(), and readinto() with dummy functions.
(BZ2File_methods): Added xreadlines() as an alias to BZ2File_getiter,
and removed truncate() and readinto().
(BZ2File_get_newlines,BZ2File_get_closed,BZ2File_get_mode,BZ2File_get_name,
BZ2File_getset):
Implemented getters for "newlines", "mode", and "name".
(BZ2File_members): Implemented "softspace" member.
(BZ2File_init): Reworked to create a file instance instead of initializing
itself as a file subclass. Also, pass "name" object untouched to the
file constructor, and use PyObject_CallFunction instead of building the
argument tuple locally.
(BZ2File_Type): Set tp_new to PyType_GenericNew, tp_members to
BZ2File_members, and tp_getset to BZ2File_getset.
(initbz2): Do not set BZ2File_Type.tp_base nor BZ2File_Type.tp_new.
* Doc/lib/libbz2.tex
Do not mention that BZ2File inherits from the file type.
diff --git a/Modules/bz2module.c b/Modules/bz2module.c
index f358de7..114070f 100644
--- a/Modules/bz2module.c
+++ b/Modules/bz2module.c
@@ -62,7 +62,21 @@
/* Structure definitions. */
typedef struct {
- PyFileObject file;
+ PyObject_HEAD
+ PyObject *file;
+
+ char* f_buf; /* Allocated readahead buffer */
+ char* f_bufend; /* Points after last occupied position */
+ char* f_bufptr; /* Current buffer position */
+
+ int f_softspace; /* Flag used by 'print' command */
+
+#ifdef WITH_UNIVERSAL_NEWLINES
+ int f_univ_newline; /* Handle any newline convention */
+ int f_newlinetypes; /* Types of newlines seen */
+ int f_skipnextlf; /* Skip next \n */
+#endif
+
BZFILE *fp;
int mode;
long pos;
@@ -179,7 +193,7 @@
/* This is a hacked version of Python's fileobject.c:get_line(). */
static PyObject *
-Util_GetLine(BZ2FileObject *self, int n)
+Util_GetLine(BZ2FileObject *f, int n)
{
char c;
char *buf, *end;
@@ -189,9 +203,9 @@
PyObject *v;
int bzerror;
#ifdef WITH_UNIVERSAL_NEWLINES
- int newlinetypes = ((PyFileObject*)self)->f_newlinetypes;
- int skipnextlf = ((PyFileObject*)self)->f_skipnextlf;
- int univ_newline = ((PyFileObject*)self)->f_univ_newline;
+ int newlinetypes = f->f_newlinetypes;
+ int skipnextlf = f->f_skipnextlf;
+ int univ_newline = f->f_univ_newline;
#endif
total_v_size = n > 0 ? n : 100;
@@ -207,8 +221,8 @@
#ifdef WITH_UNIVERSAL_NEWLINES
if (univ_newline) {
while (1) {
- BZ2_bzRead(&bzerror, self->fp, &c, 1);
- self->pos++;
+ BZ2_bzRead(&bzerror, f->fp, &c, 1);
+ f->pos++;
if (bzerror != BZ_OK || buf == end)
break;
if (skipnextlf) {
@@ -219,7 +233,7 @@
* saw a \r before.
*/
newlinetypes |= NEWLINE_CRLF;
- BZ2_bzRead(&bzerror, self->fp,
+ BZ2_bzRead(&bzerror, f->fp,
&c, 1);
if (bzerror != BZ_OK)
break;
@@ -240,18 +254,18 @@
} else /* If not universal newlines use the normal loop */
#endif
do {
- BZ2_bzRead(&bzerror, self->fp, &c, 1);
- self->pos++;
+ BZ2_bzRead(&bzerror, f->fp, &c, 1);
+ f->pos++;
*buf++ = c;
} while (bzerror == BZ_OK && c != '\n' && buf != end);
Py_END_ALLOW_THREADS
#ifdef WITH_UNIVERSAL_NEWLINES
- ((PyFileObject*)self)->f_newlinetypes = newlinetypes;
- ((PyFileObject*)self)->f_skipnextlf = skipnextlf;
+ f->f_newlinetypes = newlinetypes;
+ f->f_skipnextlf = skipnextlf;
#endif
if (bzerror == BZ_STREAM_END) {
- self->size = self->pos;
- self->mode = MODE_READ_EOF;
+ f->size = f->pos;
+ f->mode = MODE_READ_EOF;
break;
} else if (bzerror != BZ_OK) {
Util_CatchBZ2Error(bzerror);
@@ -291,10 +305,9 @@
* fileobject.c:Py_UniversalNewlineFread(). */
size_t
Util_UnivNewlineRead(int *bzerror, BZFILE *stream,
- char* buf, size_t n, BZ2FileObject *fobj)
+ char* buf, size_t n, BZ2FileObject *f)
{
char *dst = buf;
- PyFileObject *f = (PyFileObject *)fobj;
int newlinetypes, skipnextlf;
assert(buf != NULL);
@@ -359,9 +372,8 @@
/* This is a hacked version of Python's fileobject.c:drop_readahead(). */
static void
-Util_DropReadAhead(BZ2FileObject *self)
+Util_DropReadAhead(BZ2FileObject *f)
{
- PyFileObject *f = (PyFileObject*)self;
if (f->f_buf != NULL) {
PyMem_Free(f->f_buf);
f->f_buf = NULL;
@@ -370,35 +382,34 @@
/* This is a hacked version of Python's fileobject.c:readahead(). */
static int
-Util_ReadAhead(BZ2FileObject *self, int bufsize)
+Util_ReadAhead(BZ2FileObject *f, int bufsize)
{
int chunksize;
int bzerror;
- PyFileObject *f = (PyFileObject*)self;
if (f->f_buf != NULL) {
if((f->f_bufend - f->f_bufptr) >= 1)
return 0;
else
- Util_DropReadAhead(self);
+ Util_DropReadAhead(f);
}
- if (self->mode == MODE_READ_EOF) {
+ if (f->mode == MODE_READ_EOF) {
return -1;
}
if ((f->f_buf = PyMem_Malloc(bufsize)) == NULL) {
return -1;
}
Py_BEGIN_ALLOW_THREADS
- chunksize = Util_UnivNewlineRead(&bzerror, self->fp, f->f_buf,
- bufsize, self);
+ chunksize = Util_UnivNewlineRead(&bzerror, f->fp, f->f_buf,
+ bufsize, f);
Py_END_ALLOW_THREADS
- self->pos += chunksize;
+ f->pos += chunksize;
if (bzerror == BZ_STREAM_END) {
- self->size = self->pos;
- self->mode = MODE_READ_EOF;
+ f->size = f->pos;
+ f->mode = MODE_READ_EOF;
} else if (bzerror != BZ_OK) {
Util_CatchBZ2Error(bzerror);
- Util_DropReadAhead(self);
+ Util_DropReadAhead(f);
return -1;
}
f->f_bufptr = f->f_buf;
@@ -409,16 +420,15 @@
/* This is a hacked version of Python's
* fileobject.c:readahead_get_line_skip(). */
static PyStringObject *
-Util_ReadAheadGetLineSkip(BZ2FileObject *bf, int skip, int bufsize)
+Util_ReadAheadGetLineSkip(BZ2FileObject *f, int skip, int bufsize)
{
- PyFileObject *f = (PyFileObject*)bf;
PyStringObject* s;
char *bufptr;
char *buf;
int len;
if (f->f_buf == NULL)
- if (Util_ReadAhead(bf, bufsize) < 0)
+ if (Util_ReadAhead(f, bufsize) < 0)
return NULL;
len = f->f_bufend - f->f_bufptr;
@@ -436,13 +446,13 @@
memcpy(PyString_AS_STRING(s)+skip, f->f_bufptr, len);
f->f_bufptr = bufptr;
if (bufptr == f->f_bufend)
- Util_DropReadAhead(bf);
+ Util_DropReadAhead(f);
} else {
bufptr = f->f_bufptr;
buf = f->f_buf;
f->f_buf = NULL; /* Force new readahead buffer */
- s = Util_ReadAheadGetLineSkip(
- bf, skip+len, bufsize + (bufsize>>2) );
+ s = Util_ReadAheadGetLineSkip(f, skip+len,
+ bufsize + (bufsize>>2));
if (s == NULL) {
PyMem_Free(buf);
return NULL;
@@ -743,6 +753,13 @@
return list;
}
+PyDoc_STRVAR(BZ2File_xreadlines__doc__,
+"xreadlines() -> self\n\
+\n\
+For backward compatibility. BZ2File objects now include the performance\n\
+optimizations previously implemented in the xreadlines module.\n\
+");
+
PyDoc_STRVAR(BZ2File_write__doc__,
"write(data) -> None\n\
\n\
@@ -778,7 +795,7 @@
goto cleanup;;
}
- PyFile_SoftSpace((PyObject*)self, 0);
+ self->f_softspace = 0;
Py_BEGIN_ALLOW_THREADS
BZ2_bzWrite (&bzerror, self->fp, buf, len);
@@ -884,7 +901,7 @@
}
}
- PyFile_SoftSpace((PyObject*)self, 0);
+ self->f_softspace = 0;
/* Since we are releasing the global lock, the
following code may *not* execute Python code. */
@@ -943,7 +960,6 @@
int chunksize;
int bzerror;
int rewind = 0;
- PyObject *func;
PyObject *ret = NULL;
if (!PyArg_ParseTuple(args, "l|i:seek", &offset, &where))
@@ -1012,25 +1028,17 @@
if (rewind) {
BZ2_bzReadClose(&bzerror, self->fp);
- func = Py_FindMethod(PyFile_Type.tp_methods, (PyObject*)self,
- "seek");
if (bzerror != BZ_OK) {
Util_CatchBZ2Error(bzerror);
goto cleanup;
}
- if (!func) {
- PyErr_SetString(PyExc_RuntimeError,
- "can't find file.seek method");
- goto cleanup;
- }
- ret = PyObject_CallFunction(func, "(i)", 0);
+ ret = PyObject_CallMethod(self->file, "seek", "(i)", 0);
if (!ret)
goto cleanup;
Py_DECREF(ret);
ret = NULL;
self->pos = 0;
- self->fp = BZ2_bzReadOpen(&bzerror,
- PyFile_AsFile((PyObject*)self),
+ self->fp = BZ2_bzReadOpen(&bzerror, PyFile_AsFile(self->file),
0, 0, NULL, 0);
if (bzerror != BZ_OK) {
Util_CatchBZ2Error(bzerror);
@@ -1101,17 +1109,6 @@
return ret;
}
-PyDoc_STRVAR(BZ2File_notsup__doc__,
-"Operation not supported.\n\
-");
-
-static PyObject *
-BZ2File_notsup(BZ2FileObject *self, PyObject *args)
-{
- PyErr_SetString(PyExc_IOError, "operation not supported");
- return NULL;
-}
-
PyDoc_STRVAR(BZ2File_close__doc__,
"close() -> None or (perhaps) an integer\n\
\n\
@@ -1123,7 +1120,6 @@
static PyObject *
BZ2File_close(BZ2FileObject *self)
{
- PyObject *file_close;
PyObject *ret = NULL;
int bzerror = BZ_OK;
@@ -1139,63 +1135,132 @@
break;
}
self->mode = MODE_CLOSED;
- file_close = Py_FindMethod(PyFile_Type.tp_methods, (PyObject*)self,
- "close");
- if (!file_close) {
- PyErr_SetString(PyExc_RuntimeError,
- "can't find file.close method");
- goto cleanup;
- }
- ret = PyObject_CallObject(file_close, NULL);
+ ret = PyObject_CallMethod(self->file, "close", NULL);
if (bzerror != BZ_OK) {
Util_CatchBZ2Error(bzerror);
Py_XDECREF(ret);
ret = NULL;
- goto cleanup;
}
-cleanup:
RELEASE_LOCK(self);
return ret;
}
+static PyObject *BZ2File_getiter(BZ2FileObject *self);
+
static PyMethodDef BZ2File_methods[] = {
{"read", (PyCFunction)BZ2File_read, METH_VARARGS, BZ2File_read__doc__},
{"readline", (PyCFunction)BZ2File_readline, METH_VARARGS, BZ2File_readline__doc__},
{"readlines", (PyCFunction)BZ2File_readlines, METH_VARARGS, BZ2File_readlines__doc__},
+ {"xreadlines", (PyCFunction)BZ2File_getiter, METH_VARARGS, BZ2File_xreadlines__doc__},
{"write", (PyCFunction)BZ2File_write, METH_VARARGS, BZ2File_write__doc__},
{"writelines", (PyCFunction)BZ2File_writelines, METH_O, BZ2File_writelines__doc__},
{"seek", (PyCFunction)BZ2File_seek, METH_VARARGS, BZ2File_seek__doc__},
{"tell", (PyCFunction)BZ2File_tell, METH_NOARGS, BZ2File_tell__doc__},
- {"truncate", (PyCFunction)BZ2File_notsup, METH_VARARGS, BZ2File_notsup__doc__},
- {"readinto", (PyCFunction)BZ2File_notsup, METH_VARARGS, BZ2File_notsup__doc__},
{"close", (PyCFunction)BZ2File_close, METH_NOARGS, BZ2File_close__doc__},
{NULL, NULL} /* sentinel */
};
/* ===================================================================== */
+/* Getters and setters of BZ2File. */
+
+#ifdef WITH_UNIVERSAL_NEWLINES
+/* This is a hacked version of Python's fileobject.c:get_newlines(). */
+static PyObject *
+BZ2File_get_newlines(BZ2FileObject *self, void *closure)
+{
+ switch (self->f_newlinetypes) {
+ case NEWLINE_UNKNOWN:
+ Py_INCREF(Py_None);
+ return Py_None;
+ case NEWLINE_CR:
+ return PyString_FromString("\r");
+ case NEWLINE_LF:
+ return PyString_FromString("\n");
+ case NEWLINE_CR|NEWLINE_LF:
+ return Py_BuildValue("(ss)", "\r", "\n");
+ case NEWLINE_CRLF:
+ return PyString_FromString("\r\n");
+ case NEWLINE_CR|NEWLINE_CRLF:
+ return Py_BuildValue("(ss)", "\r", "\r\n");
+ case NEWLINE_LF|NEWLINE_CRLF:
+ return Py_BuildValue("(ss)", "\n", "\r\n");
+ case NEWLINE_CR|NEWLINE_LF|NEWLINE_CRLF:
+ return Py_BuildValue("(sss)", "\r", "\n", "\r\n");
+ default:
+ PyErr_Format(PyExc_SystemError,
+ "Unknown newlines value 0x%x\n",
+ self->f_newlinetypes);
+ return NULL;
+ }
+}
+#endif
+
+static PyObject *
+BZ2File_get_closed(BZ2FileObject *self, void *closure)
+{
+ return PyInt_FromLong(self->mode == MODE_CLOSED);
+}
+
+static PyObject *
+BZ2File_get_mode(BZ2FileObject *self, void *closure)
+{
+ return PyObject_GetAttrString(self->file, "mode");
+}
+
+static PyObject *
+BZ2File_get_name(BZ2FileObject *self, void *closure)
+{
+ return PyObject_GetAttrString(self->file, "name");
+}
+
+static PyGetSetDef BZ2File_getset[] = {
+ {"closed", (getter)BZ2File_get_closed, NULL,
+ "True if the file is closed"},
+#ifdef WITH_UNIVERSAL_NEWLINES
+ {"newlines", (getter)BZ2File_get_newlines, NULL,
+ "end-of-line convention used in this file"},
+#endif
+ {"mode", (getter)BZ2File_get_mode, NULL,
+ "file mode ('r', 'w', or 'U')"},
+ {"name", (getter)BZ2File_get_name, NULL,
+ "file name"},
+ {NULL} /* Sentinel */
+};
+
+
+/* ===================================================================== */
+/* Members of BZ2File_Type. */
+
+#undef OFF
+#define OFF(x) offsetof(BZ2FileObject, x)
+
+static PyMemberDef BZ2File_members[] = {
+ {"softspace", T_INT, OFF(f_softspace), 0,
+ "flag indicating that a space needs to be printed; used by print"},
+ {NULL} /* Sentinel */
+};
+
+/* ===================================================================== */
/* Slot definitions for BZ2File_Type. */
static int
BZ2File_init(BZ2FileObject *self, PyObject *args, PyObject *kwargs)
{
- PyObject *file_args = NULL;
static char *kwlist[] = {"filename", "mode", "buffering",
"compresslevel", 0};
- char *name = NULL;
+ PyObject *name;
char *mode = "r";
int buffering = -1;
int compresslevel = 9;
int bzerror;
int mode_char = 0;
- int univ_newline = 0;
self->size = -1;
- if (!PyArg_ParseTupleAndKeywords(args, kwargs, "et|sii:BZ2File",
- kwlist, Py_FileSystemDefaultEncoding,
- &name, &mode, &buffering,
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O|sii:BZ2File",
+ kwlist, &name, &mode, &buffering,
&compresslevel))
return -1;
@@ -1219,7 +1284,7 @@
break;
case 'U':
- univ_newline = 1;
+ self->f_univ_newline = 1;
break;
default:
@@ -1236,21 +1301,16 @@
break;
}
- if (mode_char == 'r')
- mode = univ_newline ? "rbU" : "rb";
- else
- mode = univ_newline ? "wbU" : "wb";
+ mode = (mode_char == 'r') ? "rb" : "wb";
- file_args = Py_BuildValue("(ssi)", name, mode, buffering);
- if (!file_args)
+ self->file = PyObject_CallFunction((PyObject*)&PyFile_Type, "(Osi)",
+ name, mode, buffering);
+ if (self->file == NULL)
return -1;
/* From now on, we have stuff to dealloc, so jump to error label
* instead of returning */
- if (PyFile_Type.tp_init((PyObject *)self, file_args, NULL) < 0)
- goto error;
-
#ifdef WITH_THREAD
self->lock = PyThread_allocate_lock();
if (!self->lock)
@@ -1259,11 +1319,11 @@
if (mode_char == 'r')
self->fp = BZ2_bzReadOpen(&bzerror,
- PyFile_AsFile((PyObject*)self),
+ PyFile_AsFile(self->file),
0, 0, NULL, 0);
else
self->fp = BZ2_bzWriteOpen(&bzerror,
- PyFile_AsFile((PyObject*)self),
+ PyFile_AsFile(self->file),
compresslevel, 0, 0);
if (bzerror != BZ_OK) {
@@ -1273,17 +1333,14 @@
self->mode = (mode_char == 'r') ? MODE_READ : MODE_WRITE;
- Py_XDECREF(file_args);
- PyMem_Free(name);
return 0;
error:
+ Py_DECREF(self->file);
#ifdef WITH_THREAD
if (self->lock)
PyThread_free_lock(self->lock);
#endif
- Py_XDECREF(file_args);
- PyMem_Free(name);
return -1;
}
@@ -1306,7 +1363,8 @@
break;
}
Util_DropReadAhead(self);
- ((PyObject*)self)->ob_type->tp_free((PyObject *)self);
+ Py_DECREF(self->file);
+ self->ob_type->tp_free((PyObject *)self);
}
/* This is a hacked version of Python's fileobject.c:file_getiter(). */
@@ -1399,8 +1457,8 @@
(getiterfunc)BZ2File_getiter, /*tp_iter*/
(iternextfunc)BZ2File_iternext, /*tp_iternext*/
BZ2File_methods, /*tp_methods*/
- 0, /*tp_members*/
- 0, /*tp_getset*/
+ BZ2File_members, /*tp_members*/
+ BZ2File_getset, /*tp_getset*/
0, /*tp_base*/
0, /*tp_dict*/
0, /*tp_descr_get*/
@@ -1408,7 +1466,7 @@
0, /*tp_dictoffset*/
(initproc)BZ2File_init, /*tp_init*/
PyType_GenericAlloc, /*tp_alloc*/
- 0, /*tp_new*/
+ PyType_GenericNew, /*tp_new*/
_PyObject_Del, /*tp_free*/
0, /*tp_is_gc*/
};
@@ -1618,7 +1676,7 @@
PyThread_free_lock(self->lock);
#endif
BZ2_bzCompressEnd(&self->bzs);
- ((PyObject*)self)->ob_type->tp_free((PyObject *)self);
+ self->ob_type->tp_free((PyObject *)self);
}
@@ -1682,6 +1740,7 @@
/* ===================================================================== */
/* Members of BZ2Decomp. */
+#undef OFF
#define OFF(x) offsetof(BZ2DecompObject, x)
static PyMemberDef BZ2Decomp_members[] = {
@@ -1836,7 +1895,7 @@
#endif
Py_XDECREF(self->unused_data);
BZ2_bzDecompressEnd(&self->bzs);
- ((PyObject*)self)->ob_type->tp_free((PyObject *)self);
+ self->ob_type->tp_free((PyObject *)self);
}
@@ -2087,9 +2146,6 @@
PyObject *m;
BZ2File_Type.ob_type = &PyType_Type;
- BZ2File_Type.tp_base = &PyFile_Type;
- BZ2File_Type.tp_new = PyFile_Type.tp_new;
-
BZ2Comp_Type.ob_type = &PyType_Type;
BZ2Decomp_Type.ob_type = &PyType_Type;