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

Skip to content

Commit 59c9a64

Browse files
committed
SF bug [#460467] file objects should be subclassable.
Preliminary support. What's here works, but needs fine-tuning.
1 parent 93a696f commit 59c9a64

7 files changed

Lines changed: 170 additions & 53 deletions

File tree

Include/fileobject.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,8 @@ extern "C" {
99

1010
extern DL_IMPORT(PyTypeObject) PyFile_Type;
1111

12-
#define PyFile_Check(op) ((op)->ob_type == &PyFile_Type)
12+
#define PyFile_Check(op) PyObject_TypeCheck(op, &PyFile_Type)
13+
#define PyFile_CheckExact(op) ((op)->ob_type == &PyFile_Type)
1314

1415
extern DL_IMPORT(PyObject *) PyFile_FromString(char *, char *);
1516
extern DL_IMPORT(void) PyFile_SetBufSize(PyObject *, int);

Include/moduleobject.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ extern "C" {
1010
extern DL_IMPORT(PyTypeObject) PyModule_Type;
1111

1212
#define PyModule_Check(op) PyObject_TypeCheck(op, &PyModule_Type)
13+
#define PyModule_CheckExact(op) ((op)->ob_type == &PyModule_Type)
1314

1415
extern DL_IMPORT(PyObject *) PyModule_New(char *);
1516
extern DL_IMPORT(PyObject *) PyModule_GetDict(PyObject *);

Lib/test/test_descr.py

Lines changed: 48 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# Test descriptor-related enhancements
22

3-
from test_support import verify, verbose, TestFailed
3+
from test_support import verify, verbose, TestFailed, TESTFN
44
from copy import deepcopy
55

66
def testunop(a, res, expr="len(a)", meth="__len__"):
@@ -1636,6 +1636,53 @@ def rev(self):
16361636
verify(u[0:0].__class__ is unicode)
16371637
verify(u[0:0] == u"")
16381638

1639+
class CountedInput(file):
1640+
"""Counts lines read by self.readline().
1641+
1642+
self.lineno is the 0-based ordinal of the last line read, up to
1643+
a maximum of one greater than the number of lines in the file.
1644+
1645+
self.ateof is true if and only if the final "" line has been read,
1646+
at which point self.lineno stops incrementing, and further calls
1647+
to readline() continue to return "".
1648+
"""
1649+
1650+
lineno = 0
1651+
ateof = 0
1652+
def readline(self):
1653+
if self.ateof:
1654+
return ""
1655+
s = file.readline(self)
1656+
# Next line works too.
1657+
# s = super(CountedInput, self).readline()
1658+
self.lineno += 1
1659+
if s == "":
1660+
self.ateof = 1
1661+
return s
1662+
1663+
f = open(TESTFN, 'w')
1664+
lines = ['a\n', 'b\n', 'c\n']
1665+
try:
1666+
f.writelines(lines)
1667+
f.close()
1668+
f = CountedInput(TESTFN)
1669+
for (i, expected) in zip(range(1, 5) + [4], lines + 2 * [""]):
1670+
got = f.readline()
1671+
verify(expected == got)
1672+
verify(f.lineno == i)
1673+
verify(f.ateof == (i > len(lines)))
1674+
f.close()
1675+
finally:
1676+
try:
1677+
f.close()
1678+
except:
1679+
pass
1680+
try:
1681+
import os
1682+
os.unlink(TESTFN)
1683+
except:
1684+
pass
1685+
16391686
def all():
16401687
lists()
16411688
dicts()

Lib/types.py

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -57,12 +57,7 @@ def _m(self): pass
5757
BuiltinMethodType = type([].append) # Same as BuiltinFunctionType
5858

5959
ModuleType = type(sys)
60-
61-
try:
62-
FileType = type(sys.__stdin__)
63-
except AttributeError:
64-
# Not available in restricted mode
65-
pass
60+
FileType = file
6661
XRangeType = type(xrange(0))
6762

6863
try:

Misc/NEWS

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

44
Core
55

6+
- The builtin file type can be subclassed now. In the usual pattern,
7+
"file" is the name of the builtin type, and file() is a new builtin
8+
constructor, with the same signature as the builtin open() function.
9+
file() is now the preferred way to open a file.
10+
611
- In 2.2a3, hash() applied to an instance of a subclass of str or unicode
712
always returned 0. This has been repaired.
813

Objects/fileobject.c

Lines changed: 100 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -65,37 +65,33 @@ PyFile_Name(PyObject *f)
6565
return ((PyFileObject *)f)->f_name;
6666
}
6767

68-
PyObject *
69-
PyFile_FromFile(FILE *fp, char *name, char *mode, int (*close)(FILE *))
68+
69+
static PyObject *
70+
fill_file_fields(PyFileObject *f, FILE *fp, char *name, char *mode,
71+
int (*close)(FILE *))
7072
{
71-
PyFileObject *f = PyObject_NEW(PyFileObject, &PyFile_Type);
72-
if (f == NULL)
73-
return NULL;
73+
assert(f != NULL);
74+
assert(PyFile_Check(f));
7475
f->f_fp = NULL;
7576
f->f_name = PyString_FromString(name);
7677
f->f_mode = PyString_FromString(mode);
7778
f->f_close = close;
7879
f->f_softspace = 0;
79-
if (strchr(mode,'b') != NULL)
80-
f->f_binary = 1;
81-
else
82-
f->f_binary = 0;
83-
if (f->f_name == NULL || f->f_mode == NULL) {
84-
Py_DECREF(f);
80+
f->f_binary = strchr(mode,'b') != NULL;
81+
if (f->f_name == NULL || f->f_mode == NULL)
8582
return NULL;
86-
}
8783
f->f_fp = fp;
8884
return (PyObject *) f;
8985
}
9086

91-
PyObject *
92-
PyFile_FromString(char *name, char *mode)
87+
static PyObject *
88+
open_the_file(PyFileObject *f, char *name, char *mode)
9389
{
94-
extern int fclose(FILE *);
95-
PyFileObject *f;
96-
f = (PyFileObject *) PyFile_FromFile((FILE *)NULL, name, mode, fclose);
97-
if (f == NULL)
98-
return NULL;
90+
assert(f != NULL);
91+
assert(PyFile_Check(f));
92+
assert(name != NULL);
93+
assert(mode != NULL);
94+
9995
#ifdef HAVE_FOPENRF
10096
if (*mode == '*') {
10197
FILE *fopenRF();
@@ -118,8 +114,36 @@ PyFile_FromString(char *name, char *mode)
118114
}
119115
#endif
120116
PyErr_SetFromErrnoWithFilename(PyExc_IOError, name);
121-
Py_DECREF(f);
122-
return NULL;
117+
f = NULL;
118+
}
119+
return (PyObject *)f;
120+
}
121+
122+
PyObject *
123+
PyFile_FromFile(FILE *fp, char *name, char *mode, int (*close)(FILE *))
124+
{
125+
PyFileObject *f = PyObject_NEW(PyFileObject, &PyFile_Type);
126+
if (f != NULL) {
127+
if (fill_file_fields(f, fp, name, mode, close) == NULL) {
128+
Py_DECREF(f);
129+
f = NULL;
130+
}
131+
}
132+
return (PyObject *) f;
133+
}
134+
135+
PyObject *
136+
PyFile_FromString(char *name, char *mode)
137+
{
138+
extern int fclose(FILE *);
139+
PyFileObject *f;
140+
141+
f = (PyFileObject *)PyFile_FromFile((FILE *)NULL, name, mode, fclose);
142+
if (f != NULL) {
143+
if (open_the_file(f, name, mode) == NULL) {
144+
Py_DECREF(f);
145+
f = NULL;
146+
}
123147
}
124148
return (PyObject *)f;
125149
}
@@ -1293,6 +1317,52 @@ file_getiter(PyObject *f)
12931317
return PyObject_CallMethod(f, "xreadlines", "");
12941318
}
12951319

1320+
static PyObject *
1321+
file_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
1322+
{
1323+
/* XXX As for all XXX_new functions, file_new is called
1324+
with kwds=NULL by type_call(), so the kwlist is impotent. */
1325+
static char *kwlist[] = {"name", "mode", "buffering", 0};
1326+
char *name = NULL;
1327+
char *mode = "r";
1328+
int bufsize = -1;
1329+
PyObject *f;
1330+
extern int fclose(FILE *);
1331+
1332+
if (!PyArg_ParseTupleAndKeywords(args, kwds, "et|si:file", kwlist,
1333+
Py_FileSystemDefaultEncoding, &name,
1334+
&mode, &bufsize))
1335+
return NULL;
1336+
f = PyType_GenericAlloc(type, 0);
1337+
if (f != NULL) {
1338+
PyFileObject *g = (PyFileObject *)f;
1339+
if (fill_file_fields(g, NULL, name, mode, fclose) == NULL) {
1340+
Py_DECREF(f);
1341+
f = NULL;
1342+
}
1343+
if (f != NULL && open_the_file(g, name, mode) == NULL) {
1344+
Py_DECREF(f);
1345+
f = NULL;
1346+
}
1347+
if (f != NULL)
1348+
PyFile_SetBufSize(f, bufsize);
1349+
}
1350+
PyMem_Free(name); /* free the encoded string */
1351+
return f;
1352+
}
1353+
1354+
/* XXX Keep this in synch with open_doc in bltinmodule.c. */
1355+
static char file_doc[] =
1356+
"file(name[, mode[, buffering]]) -> file object\n"
1357+
"\n"
1358+
"Open a file. The mode can be 'r', 'w' or 'a' for reading (default),\n"
1359+
"writing or appending. The file will be created if it doesn't exist\n"
1360+
"when opened for writing or appending; it will be truncated when\n"
1361+
"opened for writing. Add a 'b' to the mode for binary files.\n"
1362+
"Add a '+' to the mode to allow simultaneous reading and writing.\n"
1363+
"If the buffering argument is given, 0 means unbuffered, 1 means line\n"
1364+
"buffered, and larger numbers specify the buffer size.";
1365+
12961366
PyTypeObject PyFile_Type = {
12971367
PyObject_HEAD_INIT(&PyType_Type)
12981368
0,
@@ -1314,8 +1384,9 @@ PyTypeObject PyFile_Type = {
13141384
PyObject_GenericGetAttr, /* tp_getattro */
13151385
0, /* tp_setattro */
13161386
0, /* tp_as_buffer */
1317-
Py_TPFLAGS_DEFAULT, /* tp_flags */
1318-
0, /* tp_doc */
1387+
Py_TPFLAGS_DEFAULT |
1388+
Py_TPFLAGS_BASETYPE, /* tp_flags */
1389+
file_doc, /* tp_doc */
13191390
0, /* tp_traverse */
13201391
0, /* tp_clear */
13211392
0, /* tp_richcompare */
@@ -1327,6 +1398,12 @@ PyTypeObject PyFile_Type = {
13271398
file_getsetlist, /* tp_getset */
13281399
0, /* tp_base */
13291400
0, /* tp_dict */
1401+
0, /* tp_descr_get */
1402+
0, /* tp_descr_set */
1403+
0, /* tp_dictoffset */
1404+
0, /* tp_init */
1405+
0, /* tp_alloc */
1406+
file_new, /* tp_new */
13301407
};
13311408

13321409
/* Interface for the 'soft space' between print items. */

Python/bltinmodule.c

Lines changed: 13 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1192,31 +1192,20 @@ Return the octal representation of an integer or long integer.";
11921192
static PyObject *
11931193
builtin_open(PyObject *self, PyObject *args)
11941194
{
1195-
char *name = NULL;
1196-
char *mode = "r";
1197-
int bufsize = -1;
1198-
PyObject *f;
1199-
1200-
if (!PyArg_ParseTuple(args, "et|si:open", Py_FileSystemDefaultEncoding,
1201-
&name, &mode, &bufsize))
1202-
return NULL;
1203-
f = PyFile_FromString(name, mode);
1204-
PyMem_Free(name); /* free the encoded string */
1205-
if (f != NULL)
1206-
PyFile_SetBufSize(f, bufsize);
1207-
return f;
1195+
return PyFile_Type.tp_new(&PyFile_Type, args, NULL);
12081196
}
12091197

1198+
/* XXX Keep this in synch with file_doc in fileobject.c. */
12101199
static char open_doc[] =
1211-
"open(filename[, mode[, buffering]]) -> file object\n\
1212-
\n\
1213-
Open a file. The mode can be 'r', 'w' or 'a' for reading (default),\n\
1214-
writing or appending. The file will be created if it doesn't exist\n\
1215-
when opened for writing or appending; it will be truncated when\n\
1216-
opened for writing. Add a 'b' to the mode for binary files.\n\
1217-
Add a '+' to the mode to allow simultaneous reading and writing.\n\
1218-
If the buffering argument is given, 0 means unbuffered, 1 means line\n\
1219-
buffered, and larger numbers specify the buffer size.";
1200+
"open(name[, mode[, buffering]]) -> file object\n"
1201+
"\n"
1202+
"Open a file. The mode can be 'r', 'w' or 'a' for reading (default),\n"
1203+
"writing or appending. The file will be created if it doesn't exist\n"
1204+
"when opened for writing or appending; it will be truncated when\n"
1205+
"opened for writing. Add a 'b' to the mode for binary files.\n"
1206+
"Add a '+' to the mode to allow simultaneous reading and writing.\n"
1207+
"If the buffering argument is given, 0 means unbuffered, 1 means line\n"
1208+
"buffered, and larger numbers specify the buffer size.";
12201209

12211210

12221211
static PyObject *
@@ -1894,6 +1883,8 @@ _PyBuiltin_Init(void)
18941883
return NULL;
18951884
if (PyDict_SetItemString(dict, "type", (PyObject *) &PyType_Type) < 0)
18961885
return NULL;
1886+
if (PyDict_SetItemString(dict, "file", (PyObject *) &PyFile_Type) < 0)
1887+
return NULL;
18971888
#ifdef Py_USING_UNICODE
18981889
if (PyDict_SetItemString(dict, "unicode",
18991890
(PyObject *) &PyUnicode_Type) < 0)

0 commit comments

Comments
 (0)