mirror of
https://github.com/python/cpython.git
synced 2025-09-25 01:43:11 +00:00
SF bug [#460467] file objects should be subclassable.
Preliminary support. What's here works, but needs fine-tuning.
This commit is contained in:
parent
93a696f491
commit
59c9a645e2
7 changed files with 170 additions and 53 deletions
|
@ -9,7 +9,8 @@ extern "C" {
|
||||||
|
|
||||||
extern DL_IMPORT(PyTypeObject) PyFile_Type;
|
extern DL_IMPORT(PyTypeObject) PyFile_Type;
|
||||||
|
|
||||||
#define PyFile_Check(op) ((op)->ob_type == &PyFile_Type)
|
#define PyFile_Check(op) PyObject_TypeCheck(op, &PyFile_Type)
|
||||||
|
#define PyFile_CheckExact(op) ((op)->ob_type == &PyFile_Type)
|
||||||
|
|
||||||
extern DL_IMPORT(PyObject *) PyFile_FromString(char *, char *);
|
extern DL_IMPORT(PyObject *) PyFile_FromString(char *, char *);
|
||||||
extern DL_IMPORT(void) PyFile_SetBufSize(PyObject *, int);
|
extern DL_IMPORT(void) PyFile_SetBufSize(PyObject *, int);
|
||||||
|
|
|
@ -10,6 +10,7 @@ extern "C" {
|
||||||
extern DL_IMPORT(PyTypeObject) PyModule_Type;
|
extern DL_IMPORT(PyTypeObject) PyModule_Type;
|
||||||
|
|
||||||
#define PyModule_Check(op) PyObject_TypeCheck(op, &PyModule_Type)
|
#define PyModule_Check(op) PyObject_TypeCheck(op, &PyModule_Type)
|
||||||
|
#define PyModule_CheckExact(op) ((op)->ob_type == &PyModule_Type)
|
||||||
|
|
||||||
extern DL_IMPORT(PyObject *) PyModule_New(char *);
|
extern DL_IMPORT(PyObject *) PyModule_New(char *);
|
||||||
extern DL_IMPORT(PyObject *) PyModule_GetDict(PyObject *);
|
extern DL_IMPORT(PyObject *) PyModule_GetDict(PyObject *);
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
# Test descriptor-related enhancements
|
# Test descriptor-related enhancements
|
||||||
|
|
||||||
from test_support import verify, verbose, TestFailed
|
from test_support import verify, verbose, TestFailed, TESTFN
|
||||||
from copy import deepcopy
|
from copy import deepcopy
|
||||||
|
|
||||||
def testunop(a, res, expr="len(a)", meth="__len__"):
|
def testunop(a, res, expr="len(a)", meth="__len__"):
|
||||||
|
@ -1636,6 +1636,53 @@ def inherits():
|
||||||
verify(u[0:0].__class__ is unicode)
|
verify(u[0:0].__class__ is unicode)
|
||||||
verify(u[0:0] == u"")
|
verify(u[0:0] == u"")
|
||||||
|
|
||||||
|
class CountedInput(file):
|
||||||
|
"""Counts lines read by self.readline().
|
||||||
|
|
||||||
|
self.lineno is the 0-based ordinal of the last line read, up to
|
||||||
|
a maximum of one greater than the number of lines in the file.
|
||||||
|
|
||||||
|
self.ateof is true if and only if the final "" line has been read,
|
||||||
|
at which point self.lineno stops incrementing, and further calls
|
||||||
|
to readline() continue to return "".
|
||||||
|
"""
|
||||||
|
|
||||||
|
lineno = 0
|
||||||
|
ateof = 0
|
||||||
|
def readline(self):
|
||||||
|
if self.ateof:
|
||||||
|
return ""
|
||||||
|
s = file.readline(self)
|
||||||
|
# Next line works too.
|
||||||
|
# s = super(CountedInput, self).readline()
|
||||||
|
self.lineno += 1
|
||||||
|
if s == "":
|
||||||
|
self.ateof = 1
|
||||||
|
return s
|
||||||
|
|
||||||
|
f = open(TESTFN, 'w')
|
||||||
|
lines = ['a\n', 'b\n', 'c\n']
|
||||||
|
try:
|
||||||
|
f.writelines(lines)
|
||||||
|
f.close()
|
||||||
|
f = CountedInput(TESTFN)
|
||||||
|
for (i, expected) in zip(range(1, 5) + [4], lines + 2 * [""]):
|
||||||
|
got = f.readline()
|
||||||
|
verify(expected == got)
|
||||||
|
verify(f.lineno == i)
|
||||||
|
verify(f.ateof == (i > len(lines)))
|
||||||
|
f.close()
|
||||||
|
finally:
|
||||||
|
try:
|
||||||
|
f.close()
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
try:
|
||||||
|
import os
|
||||||
|
os.unlink(TESTFN)
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
def all():
|
def all():
|
||||||
lists()
|
lists()
|
||||||
dicts()
|
dicts()
|
||||||
|
|
|
@ -57,12 +57,7 @@ BuiltinFunctionType = type(len)
|
||||||
BuiltinMethodType = type([].append) # Same as BuiltinFunctionType
|
BuiltinMethodType = type([].append) # Same as BuiltinFunctionType
|
||||||
|
|
||||||
ModuleType = type(sys)
|
ModuleType = type(sys)
|
||||||
|
FileType = file
|
||||||
try:
|
|
||||||
FileType = type(sys.__stdin__)
|
|
||||||
except AttributeError:
|
|
||||||
# Not available in restricted mode
|
|
||||||
pass
|
|
||||||
XRangeType = type(xrange(0))
|
XRangeType = type(xrange(0))
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
|
|
@ -3,6 +3,11 @@ What's New in Python 2.2a4?
|
||||||
|
|
||||||
Core
|
Core
|
||||||
|
|
||||||
|
- The builtin file type can be subclassed now. In the usual pattern,
|
||||||
|
"file" is the name of the builtin type, and file() is a new builtin
|
||||||
|
constructor, with the same signature as the builtin open() function.
|
||||||
|
file() is now the preferred way to open a file.
|
||||||
|
|
||||||
- In 2.2a3, hash() applied to an instance of a subclass of str or unicode
|
- In 2.2a3, hash() applied to an instance of a subclass of str or unicode
|
||||||
always returned 0. This has been repaired.
|
always returned 0. This has been repaired.
|
||||||
|
|
||||||
|
|
|
@ -65,37 +65,33 @@ PyFile_Name(PyObject *f)
|
||||||
return ((PyFileObject *)f)->f_name;
|
return ((PyFileObject *)f)->f_name;
|
||||||
}
|
}
|
||||||
|
|
||||||
PyObject *
|
|
||||||
PyFile_FromFile(FILE *fp, char *name, char *mode, int (*close)(FILE *))
|
static PyObject *
|
||||||
|
fill_file_fields(PyFileObject *f, FILE *fp, char *name, char *mode,
|
||||||
|
int (*close)(FILE *))
|
||||||
{
|
{
|
||||||
PyFileObject *f = PyObject_NEW(PyFileObject, &PyFile_Type);
|
assert(f != NULL);
|
||||||
if (f == NULL)
|
assert(PyFile_Check(f));
|
||||||
return NULL;
|
|
||||||
f->f_fp = NULL;
|
f->f_fp = NULL;
|
||||||
f->f_name = PyString_FromString(name);
|
f->f_name = PyString_FromString(name);
|
||||||
f->f_mode = PyString_FromString(mode);
|
f->f_mode = PyString_FromString(mode);
|
||||||
f->f_close = close;
|
f->f_close = close;
|
||||||
f->f_softspace = 0;
|
f->f_softspace = 0;
|
||||||
if (strchr(mode,'b') != NULL)
|
f->f_binary = strchr(mode,'b') != NULL;
|
||||||
f->f_binary = 1;
|
if (f->f_name == NULL || f->f_mode == NULL)
|
||||||
else
|
|
||||||
f->f_binary = 0;
|
|
||||||
if (f->f_name == NULL || f->f_mode == NULL) {
|
|
||||||
Py_DECREF(f);
|
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
|
||||||
f->f_fp = fp;
|
f->f_fp = fp;
|
||||||
return (PyObject *) f;
|
return (PyObject *) f;
|
||||||
}
|
}
|
||||||
|
|
||||||
PyObject *
|
static PyObject *
|
||||||
PyFile_FromString(char *name, char *mode)
|
open_the_file(PyFileObject *f, char *name, char *mode)
|
||||||
{
|
{
|
||||||
extern int fclose(FILE *);
|
assert(f != NULL);
|
||||||
PyFileObject *f;
|
assert(PyFile_Check(f));
|
||||||
f = (PyFileObject *) PyFile_FromFile((FILE *)NULL, name, mode, fclose);
|
assert(name != NULL);
|
||||||
if (f == NULL)
|
assert(mode != NULL);
|
||||||
return NULL;
|
|
||||||
#ifdef HAVE_FOPENRF
|
#ifdef HAVE_FOPENRF
|
||||||
if (*mode == '*') {
|
if (*mode == '*') {
|
||||||
FILE *fopenRF();
|
FILE *fopenRF();
|
||||||
|
@ -118,8 +114,36 @@ PyFile_FromString(char *name, char *mode)
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
PyErr_SetFromErrnoWithFilename(PyExc_IOError, name);
|
PyErr_SetFromErrnoWithFilename(PyExc_IOError, name);
|
||||||
|
f = NULL;
|
||||||
|
}
|
||||||
|
return (PyObject *)f;
|
||||||
|
}
|
||||||
|
|
||||||
|
PyObject *
|
||||||
|
PyFile_FromFile(FILE *fp, char *name, char *mode, int (*close)(FILE *))
|
||||||
|
{
|
||||||
|
PyFileObject *f = PyObject_NEW(PyFileObject, &PyFile_Type);
|
||||||
|
if (f != NULL) {
|
||||||
|
if (fill_file_fields(f, fp, name, mode, close) == NULL) {
|
||||||
Py_DECREF(f);
|
Py_DECREF(f);
|
||||||
return NULL;
|
f = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return (PyObject *) f;
|
||||||
|
}
|
||||||
|
|
||||||
|
PyObject *
|
||||||
|
PyFile_FromString(char *name, char *mode)
|
||||||
|
{
|
||||||
|
extern int fclose(FILE *);
|
||||||
|
PyFileObject *f;
|
||||||
|
|
||||||
|
f = (PyFileObject *)PyFile_FromFile((FILE *)NULL, name, mode, fclose);
|
||||||
|
if (f != NULL) {
|
||||||
|
if (open_the_file(f, name, mode) == NULL) {
|
||||||
|
Py_DECREF(f);
|
||||||
|
f = NULL;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return (PyObject *)f;
|
return (PyObject *)f;
|
||||||
}
|
}
|
||||||
|
@ -1293,6 +1317,52 @@ file_getiter(PyObject *f)
|
||||||
return PyObject_CallMethod(f, "xreadlines", "");
|
return PyObject_CallMethod(f, "xreadlines", "");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static PyObject *
|
||||||
|
file_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
|
||||||
|
{
|
||||||
|
/* XXX As for all XXX_new functions, file_new is called
|
||||||
|
with kwds=NULL by type_call(), so the kwlist is impotent. */
|
||||||
|
static char *kwlist[] = {"name", "mode", "buffering", 0};
|
||||||
|
char *name = NULL;
|
||||||
|
char *mode = "r";
|
||||||
|
int bufsize = -1;
|
||||||
|
PyObject *f;
|
||||||
|
extern int fclose(FILE *);
|
||||||
|
|
||||||
|
if (!PyArg_ParseTupleAndKeywords(args, kwds, "et|si:file", kwlist,
|
||||||
|
Py_FileSystemDefaultEncoding, &name,
|
||||||
|
&mode, &bufsize))
|
||||||
|
return NULL;
|
||||||
|
f = PyType_GenericAlloc(type, 0);
|
||||||
|
if (f != NULL) {
|
||||||
|
PyFileObject *g = (PyFileObject *)f;
|
||||||
|
if (fill_file_fields(g, NULL, name, mode, fclose) == NULL) {
|
||||||
|
Py_DECREF(f);
|
||||||
|
f = NULL;
|
||||||
|
}
|
||||||
|
if (f != NULL && open_the_file(g, name, mode) == NULL) {
|
||||||
|
Py_DECREF(f);
|
||||||
|
f = NULL;
|
||||||
|
}
|
||||||
|
if (f != NULL)
|
||||||
|
PyFile_SetBufSize(f, bufsize);
|
||||||
|
}
|
||||||
|
PyMem_Free(name); /* free the encoded string */
|
||||||
|
return f;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* XXX Keep this in synch with open_doc in bltinmodule.c. */
|
||||||
|
static char file_doc[] =
|
||||||
|
"file(name[, mode[, buffering]]) -> file 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 'b' to the mode for binary files.\n"
|
||||||
|
"Add a '+' to the mode to allow simultaneous reading and writing.\n"
|
||||||
|
"If the buffering argument is given, 0 means unbuffered, 1 means line\n"
|
||||||
|
"buffered, and larger numbers specify the buffer size.";
|
||||||
|
|
||||||
PyTypeObject PyFile_Type = {
|
PyTypeObject PyFile_Type = {
|
||||||
PyObject_HEAD_INIT(&PyType_Type)
|
PyObject_HEAD_INIT(&PyType_Type)
|
||||||
0,
|
0,
|
||||||
|
@ -1314,8 +1384,9 @@ PyTypeObject PyFile_Type = {
|
||||||
PyObject_GenericGetAttr, /* tp_getattro */
|
PyObject_GenericGetAttr, /* tp_getattro */
|
||||||
0, /* tp_setattro */
|
0, /* tp_setattro */
|
||||||
0, /* tp_as_buffer */
|
0, /* tp_as_buffer */
|
||||||
Py_TPFLAGS_DEFAULT, /* tp_flags */
|
Py_TPFLAGS_DEFAULT |
|
||||||
0, /* tp_doc */
|
Py_TPFLAGS_BASETYPE, /* tp_flags */
|
||||||
|
file_doc, /* tp_doc */
|
||||||
0, /* tp_traverse */
|
0, /* tp_traverse */
|
||||||
0, /* tp_clear */
|
0, /* tp_clear */
|
||||||
0, /* tp_richcompare */
|
0, /* tp_richcompare */
|
||||||
|
@ -1327,6 +1398,12 @@ PyTypeObject PyFile_Type = {
|
||||||
file_getsetlist, /* tp_getset */
|
file_getsetlist, /* tp_getset */
|
||||||
0, /* tp_base */
|
0, /* tp_base */
|
||||||
0, /* tp_dict */
|
0, /* tp_dict */
|
||||||
|
0, /* tp_descr_get */
|
||||||
|
0, /* tp_descr_set */
|
||||||
|
0, /* tp_dictoffset */
|
||||||
|
0, /* tp_init */
|
||||||
|
0, /* tp_alloc */
|
||||||
|
file_new, /* tp_new */
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Interface for the 'soft space' between print items. */
|
/* Interface for the 'soft space' between print items. */
|
||||||
|
|
|
@ -1192,31 +1192,20 @@ Return the octal representation of an integer or long integer.";
|
||||||
static PyObject *
|
static PyObject *
|
||||||
builtin_open(PyObject *self, PyObject *args)
|
builtin_open(PyObject *self, PyObject *args)
|
||||||
{
|
{
|
||||||
char *name = NULL;
|
return PyFile_Type.tp_new(&PyFile_Type, args, NULL);
|
||||||
char *mode = "r";
|
|
||||||
int bufsize = -1;
|
|
||||||
PyObject *f;
|
|
||||||
|
|
||||||
if (!PyArg_ParseTuple(args, "et|si:open", Py_FileSystemDefaultEncoding,
|
|
||||||
&name, &mode, &bufsize))
|
|
||||||
return NULL;
|
|
||||||
f = PyFile_FromString(name, mode);
|
|
||||||
PyMem_Free(name); /* free the encoded string */
|
|
||||||
if (f != NULL)
|
|
||||||
PyFile_SetBufSize(f, bufsize);
|
|
||||||
return f;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* XXX Keep this in synch with file_doc in fileobject.c. */
|
||||||
static char open_doc[] =
|
static char open_doc[] =
|
||||||
"open(filename[, mode[, buffering]]) -> file object\n\
|
"open(name[, mode[, buffering]]) -> file object\n"
|
||||||
\n\
|
"\n"
|
||||||
Open a file. The mode can be 'r', 'w' or 'a' for reading (default),\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\
|
"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\
|
"when opened for writing or appending; it will be truncated when\n"
|
||||||
opened for writing. Add a 'b' to the mode for binary files.\n\
|
"opened for writing. Add a 'b' to the mode for binary files.\n"
|
||||||
Add a '+' to the mode to allow simultaneous reading and writing.\n\
|
"Add a '+' to the mode to allow simultaneous reading and writing.\n"
|
||||||
If the buffering argument is given, 0 means unbuffered, 1 means line\n\
|
"If the buffering argument is given, 0 means unbuffered, 1 means line\n"
|
||||||
buffered, and larger numbers specify the buffer size.";
|
"buffered, and larger numbers specify the buffer size.";
|
||||||
|
|
||||||
|
|
||||||
static PyObject *
|
static PyObject *
|
||||||
|
@ -1894,6 +1883,8 @@ _PyBuiltin_Init(void)
|
||||||
return NULL;
|
return NULL;
|
||||||
if (PyDict_SetItemString(dict, "type", (PyObject *) &PyType_Type) < 0)
|
if (PyDict_SetItemString(dict, "type", (PyObject *) &PyType_Type) < 0)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
if (PyDict_SetItemString(dict, "file", (PyObject *) &PyFile_Type) < 0)
|
||||||
|
return NULL;
|
||||||
#ifdef Py_USING_UNICODE
|
#ifdef Py_USING_UNICODE
|
||||||
if (PyDict_SetItemString(dict, "unicode",
|
if (PyDict_SetItemString(dict, "unicode",
|
||||||
(PyObject *) &PyUnicode_Type) < 0)
|
(PyObject *) &PyUnicode_Type) < 0)
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue