Skip to content

Commit ce3a72a

Browse files
committed
Patch 1267 by Christian Heimes.
Move the initialization of sys.std{in,out,err} and __builtin__.open to C code. This solves the problem that "python -S" wouldn't work.
1 parent 75a902d commit ce3a72a

File tree

11 files changed

+175
-31
lines changed

11 files changed

+175
-31
lines changed

Doc/c-api/concrete.rst

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2425,6 +2425,12 @@ change in future releases of Python.
24252425
pointer, *fp*. The function *close* will be called when the file should be
24262426
closed. Return *NULL* on failure.
24272427

2428+
.. cfunction:: PyFile_FromFileEx(FILE *fp, char *name, char *mode, int (*close)(FILE *), int buffering, char *encoding, char *newline)
2429+
2430+
Create a new :ctype:`PyFileObject` from the already-open standard C file
2431+
pointer, *fp*. The functions works similar to *PyFile_FromFile* but takes
2432+
optional arguments for *buffering*, *encoding* and *newline*. Use -1 resp.
2433+
*NULL* for default values.
24282434

24292435
.. cfunction:: FILE* PyFile_AsFile(PyObject *p)
24302436

Doc/data/refcounts.dat

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -338,6 +338,15 @@ PyFile_FromFile:char*:name::
338338
PyFile_FromFile:char*:mode::
339339
PyFile_FromFile:int(*:close)::
340340

341+
PyFile_FromFileEx:PyObject*::+1:
342+
PyFile_FromFileEx:FILE*:fp::
343+
PyFile_FromFileEx:char*:name::
344+
PyFile_FromFileEx:char*:mode::
345+
PyFile_FromFileEx:int(*:close)::
346+
PyFile_FromFileEx:int:buffering::
347+
PyFile_FromFileEx:char*:encoding::
348+
PyFile_FromFileEx:char*:newline::
349+
341350
PyFile_FromString:PyObject*::+1:
342351
PyFile_FromString:char*:name::
343352
PyFile_FromString:char*:mode::

Include/fileobject.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,9 @@ extern "C" {
99
#define PY_STDIOTEXTMODE "b"
1010

1111
PyAPI_FUNC(PyObject *) PyFile_FromFile(FILE *, char *, char *, int (*)(FILE*));
12+
PyAPI_FUNC(PyObject *) PyFile_FromFileEx(FILE *, char *, char *,
13+
int (*)(FILE *), int, char *,
14+
char *);
1215
PyAPI_FUNC(PyObject *) PyFile_GetLine(PyObject *, int);
1316
PyAPI_FUNC(int) PyFile_WriteObject(PyObject *, PyObject *, int);
1417
PyAPI_FUNC(int) PyFile_WriteString(const char *, PyObject *);

Lib/io.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -178,6 +178,18 @@ def open(file, mode="r", buffering=None, encoding=None, newline=None):
178178
return text
179179

180180

181+
class OpenWrapper:
182+
"""Wrapper for __builtin__.open
183+
184+
Trick so that open won't become a bound method when stored
185+
as a class variable (as dumbdbm does).
186+
187+
See initstdio() in Python/pythonrun.c.
188+
"""
189+
def __new__(cls, *args, **kwargs):
190+
return open(*args, **kwargs)
191+
192+
181193
class UnsupportedOperation(ValueError, IOError):
182194
pass
183195

Lib/site.py

Lines changed: 0 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -402,23 +402,6 @@ def execsitecustomize():
402402
(err.__class__.__name__, err))
403403

404404

405-
def installnewio():
406-
"""Install new I/O library as default."""
407-
import io
408-
# Hack to avoid a nasty recursion issue when Python is invoked
409-
# in verbose mode: pre-import the Latin-1 and UTF-8 codecs
410-
from encodings import latin_1, utf_8
411-
# Trick so that open won't become a bound method when stored
412-
# as a class variable (as dumbdbm does)
413-
class open:
414-
def __new__(cls, *args, **kwds):
415-
return io.open(*args, **kwds)
416-
__builtin__.open = open
417-
sys.__stdin__ = sys.stdin = io.open(0, "r", newline='\n')
418-
sys.__stdout__ = sys.stdout = io.open(1, "w", newline='\n')
419-
sys.__stderr__ = sys.stderr = io.open(2, "w", newline='\n')
420-
421-
422405
def main():
423406
abs__file__()
424407
paths_in_sys = removeduppaths()
@@ -433,7 +416,6 @@ def main():
433416
sethelper()
434417
aliasmbcs()
435418
setencoding()
436-
installnewio()
437419
execsitecustomize()
438420
# Remove sys.setdefaultencoding() so that users cannot change the
439421
# encoding after initialization. The test for presence is needed when

Lib/test/test_imp.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,9 +38,16 @@ def testLock(self):
3838
self.fail("release_lock() without lock should raise "
3939
"RuntimeError")
4040

41+
class ImportTests(unittest.TestCase):
42+
43+
def test_find_module_encoding(self):
44+
fd = imp.find_module("heapq")[0]
45+
self.assertEqual(fd.encoding, "iso-8859-1")
46+
4147
def test_main():
4248
test_support.run_unittest(
4349
LockTests,
50+
ImportTests,
4451
)
4552

4653
if __name__ == "__main__":

Objects/fileobject.c

Lines changed: 19 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -28,22 +28,32 @@ extern "C" {
2828
PyObject *
2929
PyFile_FromFile(FILE *fp, char *name, char *mode, int (*close)(FILE *))
3030
{
31-
PyObject *io, *stream, *nameobj;
31+
return PyFile_FromFileEx(fp, name, mode, close, -1, NULL, NULL);
32+
}
33+
34+
PyObject *
35+
PyFile_FromFileEx(FILE *fp, char *name, char *mode, int (*close)(FILE *),
36+
int buffering, char *encoding, char *newline)
37+
{
38+
PyObject *io, *stream, *nameobj=NULL;
3239

3340
io = PyImport_ImportModule("io");
3441
if (io == NULL)
3542
return NULL;
36-
stream = PyObject_CallMethod(io, "open", "is", fileno(fp), mode);
37-
Py_DECREF(io);
43+
stream = PyObject_CallMethod(io, "open", "isiss", fileno(fp), mode,
44+
buffering, encoding, newline);
45+
Py_DECREF(io);
3846
if (stream == NULL)
3947
return NULL;
40-
nameobj = PyUnicode_FromString(name);
41-
if (nameobj == NULL)
42-
PyErr_Clear();
43-
else {
44-
if (PyObject_SetAttrString(stream, "name", nameobj) < 0)
48+
if (name != NULL) {
49+
nameobj = PyUnicode_FromString(name);
50+
if (nameobj == NULL)
4551
PyErr_Clear();
46-
Py_DECREF(nameobj);
52+
else {
53+
if (PyObject_SetAttrString(stream, "name", nameobj) < 0)
54+
PyErr_Clear();
55+
Py_DECREF(nameobj);
56+
}
4757
}
4858
return stream;
4959
}

Parser/tokenizer.c

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1601,7 +1601,28 @@ PyTokenizer_RestoreEncoding(struct tok_state* tok, int len, int *offset)
16011601
}
16021602
#endif
16031603

1604-
1604+
/* Get -*- encoding -*- from a Python file
1605+
1606+
PyTokenizer_FindEncoding returns NULL when it can't find the encoding in
1607+
the first or second line of the file. In this case the encoding is
1608+
PyUnicode_GetDefaultEncoding().
1609+
*/
1610+
char *
1611+
PyTokenizer_FindEncoding(FILE *fp) {
1612+
struct tok_state *tok;
1613+
char *p_start=NULL, *p_end=NULL;
1614+
1615+
if ((tok = PyTokenizer_FromFile(fp, NULL, NULL, NULL)) == NULL) {
1616+
rewind(fp);
1617+
return NULL;
1618+
}
1619+
while(((tok->lineno <= 2) && (tok->done == E_OK))) {
1620+
PyTokenizer_Get(tok, &p_start, &p_end);
1621+
}
1622+
1623+
rewind(fp);
1624+
return tok->encoding;
1625+
}
16051626

16061627
#ifdef Py_DEBUG
16071628

Parser/tokenizer.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ extern void PyTokenizer_Free(struct tok_state *);
6767
extern int PyTokenizer_Get(struct tok_state *, char **, char **);
6868
extern char * PyTokenizer_RestoreEncoding(struct tok_state* tok,
6969
int len, int *offset);
70+
extern char * PyTokenizer_FindEncoding(FILE *fp);
7071

7172
#ifdef __cplusplus
7273
}

Python/import.c

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,9 @@ static PyObject *extensions = NULL;
9191
/* This table is defined in config.c: */
9292
extern struct _inittab _PyImport_Inittab[];
9393

94+
/* Method from Parser/tokenizer.c */
95+
extern char * PyTokenizer_FindEncoding(FILE *fp);
96+
9497
struct _inittab *PyImport_Inittab = _PyImport_Inittab;
9598

9699
/* these tables define the module suffixes that Python recognizes */
@@ -2558,6 +2561,7 @@ call_find_module(char *name, PyObject *path)
25582561
struct filedescr *fdp;
25592562
char pathname[MAXPATHLEN+1];
25602563
FILE *fp = NULL;
2564+
char *encoding = NULL;
25612565

25622566
pathname[0] = '\0';
25632567
if (path == Py_None)
@@ -2566,7 +2570,14 @@ call_find_module(char *name, PyObject *path)
25662570
if (fdp == NULL)
25672571
return NULL;
25682572
if (fp != NULL) {
2569-
fob = PyFile_FromFile(fp, pathname, fdp->mode, fclose);
2573+
if (strchr(fdp->mode, 'b') == NULL) {
2574+
/* Python text file, get encoding from tokenizer */
2575+
encoding = PyTokenizer_FindEncoding(fp);
2576+
encoding = (encoding != NULL) ? encoding :
2577+
(char*)PyUnicode_GetDefaultEncoding();
2578+
}
2579+
fob = PyFile_FromFileEx(fp, pathname, fdp->mode, fclose, -1,
2580+
(char*)encoding, NULL);
25702581
if (fob == NULL) {
25712582
fclose(fp);
25722583
return NULL;

Python/pythonrun.c

Lines changed: 84 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ extern grammar _PyParser_Grammar; /* From graminit.c */
5151
/* Forward */
5252
static void initmain(void);
5353
static void initsite(void);
54+
static int initstdio(void);
5455
static PyObject *run_mod(mod_ty, const char *, PyObject *, PyObject *,
5556
PyCompilerFlags *, PyArena *);
5657
static PyObject *run_pyc_file(FILE *, const char *, PyObject *, PyObject *,
@@ -241,6 +242,9 @@ Py_InitializeEx(int install_sigs)
241242
initsigs(); /* Signal handling stuff, including initintr() */
242243

243244
initmain(); /* Module __main__ */
245+
if (initstdio() < 0)
246+
Py_FatalError(
247+
"Py_Initialize: can't initialize sys standard streams");
244248
if (!Py_NoSiteFlag)
245249
initsite(); /* Module site */
246250

@@ -676,6 +680,81 @@ initsite(void)
676680
}
677681
}
678682

683+
/* Initialize sys.stdin, stdout, stderr and __builtin__.open */
684+
static int
685+
initstdio(void)
686+
{
687+
PyObject *iomod = NULL, *wrapper;
688+
PyObject *bimod = NULL;
689+
PyObject *m;
690+
PyObject *std = NULL;
691+
int status = 0;
692+
693+
/* Hack to avoid a nasty recursion issue when Python is invoked
694+
in verbose mode: pre-import the Latin-1 and UTF-8 codecs */
695+
if ((m = PyImport_ImportModule("encodings.utf_8")) == NULL) {
696+
goto error;
697+
}
698+
Py_DECREF(m);
699+
700+
if (!(m = PyImport_ImportModule("encodings.latin_1"))) {
701+
goto error;
702+
}
703+
Py_DECREF(m);
704+
705+
if (!(bimod = PyImport_ImportModule("__builtin__"))) {
706+
goto error;
707+
}
708+
709+
if (!(iomod = PyImport_ImportModule("io"))) {
710+
goto error;
711+
}
712+
if (!(wrapper = PyObject_GetAttrString(iomod, "OpenWrapper"))) {
713+
goto error;
714+
}
715+
716+
/* Set __builtin__.open */
717+
if (PyObject_SetAttrString(bimod, "open", wrapper) == -1) {
718+
goto error;
719+
}
720+
721+
/* Set sys.stdin */
722+
if (!(std = PyFile_FromFileEx(stdin, "<stdin>", "r", fclose, -1,
723+
NULL, "\n"))) {
724+
goto error;
725+
}
726+
PySys_SetObject("__stdin__", std);
727+
PySys_SetObject("stdin", std);
728+
Py_DECREF(std);
729+
730+
/* Set sys.stdout */
731+
if (!(std = PyFile_FromFileEx(stdout, "<stdout>", "w", fclose, -1,
732+
NULL, "\n"))) {
733+
goto error;
734+
}
735+
PySys_SetObject("__stdout__", std);
736+
PySys_SetObject("stdout", std);
737+
Py_DECREF(std);
738+
739+
/* Set sys.stderr */
740+
if (!(std = PyFile_FromFileEx(stderr, "<stderr>", "w", fclose, -1,
741+
NULL, "\n"))) {
742+
goto error;
743+
}
744+
PySys_SetObject("__stderr__", std);
745+
PySys_SetObject("stderr", std);
746+
Py_DECREF(std);
747+
748+
if (0) {
749+
error:
750+
status = -1;
751+
}
752+
753+
Py_XDECREF(bimod);
754+
Py_XDECREF(iomod);
755+
return status;
756+
}
757+
679758
/* Parse input from a file and execute it */
680759

681760
int
@@ -1146,10 +1225,10 @@ PyErr_Display(PyObject *exception, PyObject *value, PyObject *tb)
11461225
int err = 0;
11471226
PyObject *f = PySys_GetObject("stderr");
11481227
Py_INCREF(value);
1149-
if (f == NULL)
1228+
if (f == NULL) {
11501229
_PyObject_Dump(value);
1151-
if (f == NULL)
11521230
fprintf(stderr, "lost sys.stderr\n");
1231+
}
11531232
else {
11541233
fflush(stdout);
11551234
if (tb && tb != Py_None)
@@ -1589,6 +1668,9 @@ void
15891668
Py_FatalError(const char *msg)
15901669
{
15911670
fprintf(stderr, "Fatal Python error: %s\n", msg);
1671+
if (PyErr_Occurred()) {
1672+
PyErr_Print();
1673+
}
15921674
#ifdef MS_WINDOWS
15931675
OutputDebugString("Fatal Python error: ");
15941676
OutputDebugString(msg);

0 commit comments

Comments
 (0)