mirror of
https://github.com/jart/cosmopolitan.git
synced 2025-05-23 22:02:27 +00:00
Begin incorporating Python unit tests into build
We now build a separate APE binary for each test so they can run in parallel. We've got 148 tests running fast and stable so far.
This commit is contained in:
parent
51904e2687
commit
b5f743cdc3
121 changed files with 4995 additions and 4767 deletions
329
third_party/python/Python/initstdio.c
vendored
Normal file
329
third_party/python/Python/initstdio.c
vendored
Normal file
|
@ -0,0 +1,329 @@
|
|||
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:4;tab-width:8;coding:utf-8 -*-│
|
||||
│vi: set net ft=c ts=4 sts=4 sw=4 fenc=utf-8 :vi│
|
||||
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||
│ Python 3 │
|
||||
│ https://docs.python.org/3/license.html │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/internal.h"
|
||||
#include "libc/unicode/locale.h"
|
||||
#include "third_party/python/Include/abstract.h"
|
||||
#include "third_party/python/Include/boolobject.h"
|
||||
#include "third_party/python/Include/codecs.h"
|
||||
#include "third_party/python/Include/import.h"
|
||||
#include "third_party/python/Include/object.h"
|
||||
#include "third_party/python/Include/pydebug.h"
|
||||
#include "third_party/python/Include/pyerrors.h"
|
||||
#include "third_party/python/Include/pymem.h"
|
||||
#include "third_party/python/Include/sysmodule.h"
|
||||
#include "third_party/python/Include/yoink.h"
|
||||
/* clang-format off */
|
||||
|
||||
/* PYTHON_YOINK("io"); */
|
||||
/* PYTHON_YOINK("encodings.aliases"); */
|
||||
/* PYTHON_YOINK("encodings.latin_1"); */
|
||||
/* PYTHON_YOINK("encodings.utf_8"); */
|
||||
|
||||
_Py_IDENTIFIER(name);
|
||||
_Py_IDENTIFIER(stdin);
|
||||
_Py_IDENTIFIER(stdout);
|
||||
_Py_IDENTIFIER(stderr);
|
||||
|
||||
char *_Py_StandardStreamEncoding;
|
||||
char *_Py_StandardStreamErrors;
|
||||
|
||||
/* Check if a file descriptor is valid or not.
|
||||
Return 0 if the file descriptor is invalid, return non-zero otherwise. */
|
||||
static int
|
||||
is_valid_fd(int fd)
|
||||
{
|
||||
if (IsWindows()) {
|
||||
return __isfdopen(fd);
|
||||
}
|
||||
if (IsXnu()) {
|
||||
/* bpo-30225: On macOS Tiger, when stdout is redirected to a pipe
|
||||
and the other side of the pipe is closed, dup(1) succeed, whereas
|
||||
fstat(1, &st) fails with EBADF. Prefer fstat() over dup() to detect
|
||||
such error. */
|
||||
struct stat st;
|
||||
return (fstat(fd, &st) == 0);
|
||||
}
|
||||
int fd2;
|
||||
if (fd < 0)
|
||||
return 0;
|
||||
_Py_BEGIN_SUPPRESS_IPH
|
||||
/* Prefer dup() over fstat(). fstat() can require input/output whereas
|
||||
dup() doesn't, there is a low risk of EMFILE/ENFILE at Python
|
||||
startup. */
|
||||
fd2 = dup(fd);
|
||||
if (fd2 >= 0)
|
||||
close(fd2);
|
||||
_Py_END_SUPPRESS_IPH
|
||||
return fd2 >= 0;
|
||||
}
|
||||
|
||||
/* returns Py_None if the fd is not valid */
|
||||
static PyObject*
|
||||
create_stdio(PyObject* io,
|
||||
int fd, int write_mode, const char* name,
|
||||
const char* encoding, const char* errors)
|
||||
{
|
||||
PyObject *buf = NULL, *stream = NULL, *text = NULL, *raw = NULL, *res;
|
||||
const char* mode;
|
||||
const char* newline;
|
||||
PyObject *line_buffering;
|
||||
int buffering, isatty;
|
||||
_Py_IDENTIFIER(open);
|
||||
_Py_IDENTIFIER(isatty);
|
||||
_Py_IDENTIFIER(TextIOWrapper);
|
||||
_Py_IDENTIFIER(mode);
|
||||
|
||||
if (!is_valid_fd(fd))
|
||||
Py_RETURN_NONE;
|
||||
|
||||
/* stdin is always opened in buffered mode, first because it shouldn't
|
||||
make a difference in common use cases, second because TextIOWrapper
|
||||
depends on the presence of a read1() method which only exists on
|
||||
buffered streams.
|
||||
*/
|
||||
if (Py_UnbufferedStdioFlag && write_mode)
|
||||
buffering = 0;
|
||||
else
|
||||
buffering = -1;
|
||||
if (write_mode)
|
||||
mode = "wb";
|
||||
else
|
||||
mode = "rb";
|
||||
buf = _PyObject_CallMethodId(io, &PyId_open, "isiOOOi",
|
||||
fd, mode, buffering,
|
||||
Py_None, Py_None, /* encoding, errors */
|
||||
Py_None, 0); /* newline, closefd */
|
||||
if (buf == NULL)
|
||||
goto error;
|
||||
|
||||
if (buffering) {
|
||||
_Py_IDENTIFIER(raw);
|
||||
raw = _PyObject_GetAttrId(buf, &PyId_raw);
|
||||
if (raw == NULL)
|
||||
goto error;
|
||||
}
|
||||
else {
|
||||
raw = buf;
|
||||
Py_INCREF(raw);
|
||||
}
|
||||
|
||||
#ifdef MS_WINDOWS
|
||||
/* Windows console IO is always UTF-8 encoded */
|
||||
if (PyWindowsConsoleIO_Check(raw))
|
||||
encoding = "utf-8";
|
||||
#endif
|
||||
|
||||
text = PyUnicode_FromString(name);
|
||||
if (text == NULL || _PyObject_SetAttrId(raw, &PyId_name, text) < 0)
|
||||
goto error;
|
||||
res = _PyObject_CallMethodId(raw, &PyId_isatty, NULL);
|
||||
if (res == NULL)
|
||||
goto error;
|
||||
isatty = PyObject_IsTrue(res);
|
||||
Py_DECREF(res);
|
||||
if (isatty == -1)
|
||||
goto error;
|
||||
if (isatty || Py_UnbufferedStdioFlag)
|
||||
line_buffering = Py_True;
|
||||
else
|
||||
line_buffering = Py_False;
|
||||
|
||||
Py_CLEAR(raw);
|
||||
Py_CLEAR(text);
|
||||
|
||||
#ifdef MS_WINDOWS
|
||||
/* sys.stdin: enable universal newline mode, translate "\r\n" and "\r"
|
||||
newlines to "\n".
|
||||
sys.stdout and sys.stderr: translate "\n" to "\r\n". */
|
||||
newline = NULL;
|
||||
#else
|
||||
/* sys.stdin: split lines at "\n".
|
||||
sys.stdout and sys.stderr: don't translate newlines (use "\n"). */
|
||||
newline = "\n";
|
||||
#endif
|
||||
|
||||
stream = _PyObject_CallMethodId(io, &PyId_TextIOWrapper, "OsssO",
|
||||
buf, encoding, errors,
|
||||
newline, line_buffering);
|
||||
Py_CLEAR(buf);
|
||||
if (stream == NULL)
|
||||
goto error;
|
||||
|
||||
if (write_mode)
|
||||
mode = "w";
|
||||
else
|
||||
mode = "r";
|
||||
text = PyUnicode_FromString(mode);
|
||||
if (!text || _PyObject_SetAttrId(stream, &PyId_mode, text) < 0)
|
||||
goto error;
|
||||
Py_CLEAR(text);
|
||||
return stream;
|
||||
|
||||
error:
|
||||
Py_XDECREF(buf);
|
||||
Py_XDECREF(stream);
|
||||
Py_XDECREF(text);
|
||||
Py_XDECREF(raw);
|
||||
|
||||
if (PyErr_ExceptionMatches(PyExc_OSError) && !is_valid_fd(fd)) {
|
||||
/* Issue #24891: the file descriptor was closed after the first
|
||||
is_valid_fd() check was called. Ignore the OSError and set the
|
||||
stream to None. */
|
||||
PyErr_Clear();
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Initialize sys.stdin, stdout, stderr and builtins.open */
|
||||
int
|
||||
_Py_InitStdio(void)
|
||||
{
|
||||
PyObject *iomod = NULL, *wrapper;
|
||||
PyObject *bimod = NULL;
|
||||
PyObject *m;
|
||||
PyObject *std = NULL;
|
||||
int status = 0, fd;
|
||||
PyObject * encoding_attr;
|
||||
char *pythonioencoding = NULL, *encoding, *errors;
|
||||
|
||||
/* Hack to avoid a nasty recursion issue when Python is invoked
|
||||
in verbose mode: pre-import the Latin-1 and UTF-8 codecs */
|
||||
if ((m = PyImport_ImportModule("encodings.utf_8")) == NULL) {
|
||||
goto error;
|
||||
}
|
||||
Py_DECREF(m);
|
||||
|
||||
if (!(m = PyImport_ImportModule("encodings.latin_1"))) {
|
||||
goto error;
|
||||
}
|
||||
Py_DECREF(m);
|
||||
|
||||
if (!(bimod = PyImport_ImportModule("builtins"))) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (!(iomod = PyImport_ImportModule("io"))) {
|
||||
goto error;
|
||||
}
|
||||
if (!(wrapper = PyObject_GetAttrString(iomod, "OpenWrapper"))) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
/* Set builtins.open */
|
||||
if (PyObject_SetAttrString(bimod, "open", wrapper) == -1) {
|
||||
Py_DECREF(wrapper);
|
||||
goto error;
|
||||
}
|
||||
Py_DECREF(wrapper);
|
||||
|
||||
encoding = _Py_StandardStreamEncoding;
|
||||
errors = _Py_StandardStreamErrors;
|
||||
if (!encoding || !errors) {
|
||||
pythonioencoding = Py_GETENV("PYTHONIOENCODING");
|
||||
if (pythonioencoding) {
|
||||
char *err;
|
||||
pythonioencoding = _PyMem_Strdup(pythonioencoding);
|
||||
if (pythonioencoding == NULL) {
|
||||
PyErr_NoMemory();
|
||||
goto error;
|
||||
}
|
||||
err = strchr(pythonioencoding, ':');
|
||||
if (err) {
|
||||
*err = '\0';
|
||||
err++;
|
||||
if (*err && !errors) {
|
||||
errors = err;
|
||||
}
|
||||
}
|
||||
if (*pythonioencoding && !encoding) {
|
||||
encoding = pythonioencoding;
|
||||
}
|
||||
}
|
||||
if (!errors && !(pythonioencoding && *pythonioencoding)) {
|
||||
/* When the LC_CTYPE locale is the POSIX locale ("C locale"),
|
||||
stdin and stdout use the surrogateescape error handler by
|
||||
default, instead of the strict error handler. */
|
||||
char *loc = setlocale(LC_CTYPE, NULL);
|
||||
if (loc != NULL && strcmp(loc, "C") == 0)
|
||||
errors = "surrogateescape";
|
||||
}
|
||||
}
|
||||
|
||||
/* Set sys.stdin */
|
||||
fd = fileno(stdin);
|
||||
/* Under some conditions stdin, stdout and stderr may not be connected
|
||||
* and fileno() may point to an invalid file descriptor. For example
|
||||
* GUI apps don't have valid standard streams by default.
|
||||
*/
|
||||
std = create_stdio(iomod, fd, 0, "<stdin>", encoding, errors);
|
||||
if (std == NULL)
|
||||
goto error;
|
||||
PySys_SetObject("__stdin__", std);
|
||||
_PySys_SetObjectId(&PyId_stdin, std);
|
||||
Py_DECREF(std);
|
||||
|
||||
/* Set sys.stdout */
|
||||
fd = fileno(stdout);
|
||||
std = create_stdio(iomod, fd, 1, "<stdout>", encoding, errors);
|
||||
if (std == NULL)
|
||||
goto error;
|
||||
PySys_SetObject("__stdout__", std);
|
||||
_PySys_SetObjectId(&PyId_stdout, std);
|
||||
Py_DECREF(std);
|
||||
|
||||
#if 1 /* Disable this if you have trouble debugging bootstrap stuff */
|
||||
/* Set sys.stderr, replaces the preliminary stderr */
|
||||
fd = fileno(stderr);
|
||||
std = create_stdio(iomod, fd, 1, "<stderr>", encoding, "backslashreplace");
|
||||
if (std == NULL)
|
||||
goto error;
|
||||
|
||||
/* Same as hack above, pre-import stderr's codec to avoid recursion
|
||||
when import.c tries to write to stderr in verbose mode. */
|
||||
encoding_attr = PyObject_GetAttrString(std, "encoding");
|
||||
if (encoding_attr != NULL) {
|
||||
const char * std_encoding;
|
||||
std_encoding = PyUnicode_AsUTF8(encoding_attr);
|
||||
if (std_encoding != NULL) {
|
||||
PyObject *codec_info = _PyCodec_Lookup(std_encoding);
|
||||
Py_XDECREF(codec_info);
|
||||
}
|
||||
Py_DECREF(encoding_attr);
|
||||
}
|
||||
PyErr_Clear(); /* Not a fatal error if codec isn't available */
|
||||
|
||||
if (PySys_SetObject("__stderr__", std) < 0) {
|
||||
Py_DECREF(std);
|
||||
goto error;
|
||||
}
|
||||
if (_PySys_SetObjectId(&PyId_stderr, std) < 0) {
|
||||
Py_DECREF(std);
|
||||
goto error;
|
||||
}
|
||||
Py_DECREF(std);
|
||||
#endif
|
||||
|
||||
if (0) {
|
||||
error:
|
||||
status = -1;
|
||||
}
|
||||
|
||||
/* We won't need them anymore. */
|
||||
if (_Py_StandardStreamEncoding) {
|
||||
PyMem_RawFree(_Py_StandardStreamEncoding);
|
||||
_Py_StandardStreamEncoding = NULL;
|
||||
}
|
||||
if (_Py_StandardStreamErrors) {
|
||||
PyMem_RawFree(_Py_StandardStreamErrors);
|
||||
_Py_StandardStreamErrors = NULL;
|
||||
}
|
||||
PyMem_Free(pythonioencoding);
|
||||
Py_XDECREF(bimod);
|
||||
Py_XDECREF(iomod);
|
||||
return status;
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue