diff options
author | AlexSm <alex@ydb.tech> | 2024-03-05 10:40:59 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-03-05 12:40:59 +0300 |
commit | 1ac13c847b5358faba44dbb638a828e24369467b (patch) | |
tree | 07672b4dd3604ad3dee540a02c6494cb7d10dc3d /contrib/tools/python3/Modules/main.c | |
parent | ffcca3e7f7958ddc6487b91d3df8c01054bd0638 (diff) | |
download | ydb-1ac13c847b5358faba44dbb638a828e24369467b.tar.gz |
Library import 16 (#2433)
Co-authored-by: robot-piglet <robot-piglet@yandex-team.com>
Co-authored-by: deshevoy <deshevoy@yandex-team.com>
Co-authored-by: robot-contrib <robot-contrib@yandex-team.com>
Co-authored-by: thegeorg <thegeorg@yandex-team.com>
Co-authored-by: robot-ya-builder <robot-ya-builder@yandex-team.com>
Co-authored-by: svidyuk <svidyuk@yandex-team.com>
Co-authored-by: shadchin <shadchin@yandex-team.com>
Co-authored-by: robot-ratatosk <robot-ratatosk@yandex-team.com>
Co-authored-by: innokentii <innokentii@yandex-team.com>
Co-authored-by: arkady-e1ppa <arkady-e1ppa@yandex-team.com>
Co-authored-by: snermolaev <snermolaev@yandex-team.com>
Co-authored-by: dimdim11 <dimdim11@yandex-team.com>
Co-authored-by: kickbutt <kickbutt@yandex-team.com>
Co-authored-by: abdullinsaid <abdullinsaid@yandex-team.com>
Co-authored-by: korsunandrei <korsunandrei@yandex-team.com>
Co-authored-by: petrk <petrk@yandex-team.com>
Co-authored-by: miroslav2 <miroslav2@yandex-team.com>
Co-authored-by: serjflint <serjflint@yandex-team.com>
Co-authored-by: akhropov <akhropov@yandex-team.com>
Co-authored-by: prettyboy <prettyboy@yandex-team.com>
Co-authored-by: ilikepugs <ilikepugs@yandex-team.com>
Co-authored-by: hiddenpath <hiddenpath@yandex-team.com>
Co-authored-by: mikhnenko <mikhnenko@yandex-team.com>
Co-authored-by: spreis <spreis@yandex-team.com>
Co-authored-by: andreyshspb <andreyshspb@yandex-team.com>
Co-authored-by: dimaandreev <dimaandreev@yandex-team.com>
Co-authored-by: rashid <rashid@yandex-team.com>
Co-authored-by: robot-ydb-importer <robot-ydb-importer@yandex-team.com>
Co-authored-by: r-vetrov <r-vetrov@yandex-team.com>
Co-authored-by: ypodlesov <ypodlesov@yandex-team.com>
Co-authored-by: zaverden <zaverden@yandex-team.com>
Co-authored-by: vpozdyayev <vpozdyayev@yandex-team.com>
Co-authored-by: robot-cozmo <robot-cozmo@yandex-team.com>
Co-authored-by: v-korovin <v-korovin@yandex-team.com>
Co-authored-by: arikon <arikon@yandex-team.com>
Co-authored-by: khoden <khoden@yandex-team.com>
Co-authored-by: psydmm <psydmm@yandex-team.com>
Co-authored-by: robot-javacom <robot-javacom@yandex-team.com>
Co-authored-by: dtorilov <dtorilov@yandex-team.com>
Co-authored-by: sennikovmv <sennikovmv@yandex-team.com>
Co-authored-by: hcpp <hcpp@ydb.tech>
Diffstat (limited to 'contrib/tools/python3/Modules/main.c')
-rw-r--r-- | contrib/tools/python3/Modules/main.c | 769 |
1 files changed, 769 insertions, 0 deletions
diff --git a/contrib/tools/python3/Modules/main.c b/contrib/tools/python3/Modules/main.c new file mode 100644 index 0000000000..40dafa2c7b --- /dev/null +++ b/contrib/tools/python3/Modules/main.c @@ -0,0 +1,769 @@ +/* Python interpreter main program */ + +#include "Python.h" +#include "pycore_call.h" // _PyObject_CallNoArgs() +#include "pycore_initconfig.h" // _PyArgv +#include "pycore_interp.h" // _PyInterpreterState.sysdict +#include "pycore_pathconfig.h" // _PyPathConfig_ComputeSysPath0() +#include "pycore_pylifecycle.h" // _Py_PreInitializeFromPyArgv() +#include "pycore_pystate.h" // _PyInterpreterState_GET() + +/* Includes for exit_sigint() */ +#include <stdio.h> // perror() +#ifdef HAVE_SIGNAL_H +# include <signal.h> // SIGINT +#endif +#if defined(HAVE_GETPID) && defined(HAVE_UNISTD_H) +# include <unistd.h> // getpid() +#endif +#ifdef MS_WINDOWS +# include <windows.h> // STATUS_CONTROL_C_EXIT +#endif +/* End of includes for exit_sigint() */ + +#define COPYRIGHT \ + "Type \"help\", \"copyright\", \"credits\" or \"license\" " \ + "for more information." + +#ifdef __cplusplus +extern "C" { +#endif + +/* --- pymain_init() ---------------------------------------------- */ + +static PyStatus +pymain_init(const _PyArgv *args) +{ + PyStatus status; + + status = _PyRuntime_Initialize(); + if (_PyStatus_EXCEPTION(status)) { + return status; + } + + PyPreConfig preconfig; + PyPreConfig_InitPythonConfig(&preconfig); + + status = _Py_PreInitializeFromPyArgv(&preconfig, args); + if (_PyStatus_EXCEPTION(status)) { + return status; + } + + PyConfig config; + PyConfig_InitPythonConfig(&config); + config.pathconfig_warnings = 0; /* Suppress errors from getpath.c */ + + /* pass NULL as the config: config is read from command line arguments, + environment variables, configuration files */ + if (args->use_bytes_argv) { + status = PyConfig_SetBytesArgv(&config, args->argc, args->bytes_argv); + } + else { + status = PyConfig_SetArgv(&config, args->argc, args->wchar_argv); + } + if (_PyStatus_EXCEPTION(status)) { + goto done; + } + + status = Py_InitializeFromConfig(&config); + if (_PyStatus_EXCEPTION(status)) { + goto done; + } + status = _PyStatus_OK(); + +done: + PyConfig_Clear(&config); + return status; +} + + +/* --- pymain_run_python() ---------------------------------------- */ + +/* Non-zero if filename, command (-c) or module (-m) is set + on the command line */ +static inline int config_run_code(const PyConfig *config) +{ + return (config->run_command != NULL + || config->run_filename != NULL + || config->run_module != NULL); +} + + +/* Return non-zero if stdin is a TTY or if -i command line option is used */ +static int +stdin_is_interactive(const PyConfig *config) +{ + return (isatty(fileno(stdin)) || config->interactive); +} + + +/* Display the current Python exception and return an exitcode */ +static int +pymain_err_print(int *exitcode_p) +{ + int exitcode; + if (_Py_HandleSystemExit(&exitcode)) { + *exitcode_p = exitcode; + return 1; + } + + PyErr_Print(); + return 0; +} + + +static int +pymain_exit_err_print(void) +{ + int exitcode = 1; + pymain_err_print(&exitcode); + return exitcode; +} + + +/* Write an exitcode into *exitcode and return 1 if we have to exit Python. + Return 0 otherwise. */ +static int +pymain_get_importer(const wchar_t *filename, PyObject **importer_p, int *exitcode) +{ + PyObject *sys_path0 = NULL, *importer; + + sys_path0 = PyUnicode_FromWideChar(filename, wcslen(filename)); + if (sys_path0 == NULL) { + goto error; + } + + importer = PyImport_GetImporter(sys_path0); + if (importer == NULL) { + goto error; + } + + if (importer == Py_None) { + Py_DECREF(sys_path0); + Py_DECREF(importer); + return 0; + } + + Py_DECREF(importer); + *importer_p = sys_path0; + return 0; + +error: + Py_XDECREF(sys_path0); + + PySys_WriteStderr("Failed checking if argv[0] is an import path entry\n"); + return pymain_err_print(exitcode); +} + + +static int +pymain_sys_path_add_path0(PyInterpreterState *interp, PyObject *path0) +{ + PyObject *sys_path; + PyObject *sysdict = interp->sysdict; + if (sysdict != NULL) { + sys_path = PyDict_GetItemWithError(sysdict, &_Py_ID(path)); + if (sys_path == NULL && PyErr_Occurred()) { + return -1; + } + } + else { + sys_path = NULL; + } + if (sys_path == NULL) { + PyErr_SetString(PyExc_RuntimeError, "unable to get sys.path"); + return -1; + } + + if (PyList_Insert(sys_path, 0, path0)) { + return -1; + } + return 0; +} + + +static void +pymain_header(const PyConfig *config) +{ + if (config->quiet) { + return; + } + + if (!config->verbose && (config_run_code(config) || !stdin_is_interactive(config))) { + return; + } + + fprintf(stderr, "Python %s on %s\n", Py_GetVersion(), Py_GetPlatform()); + if (config->site_import) { + fprintf(stderr, "%s\n", COPYRIGHT); + } +} + + +static void +pymain_import_readline(const PyConfig *config) +{ + if (config->isolated) { + return; + } + if (!config->inspect && config_run_code(config)) { + return; + } + if (!isatty(fileno(stdin))) { + return; + } + + PyObject *mod = PyImport_ImportModule("readline"); + if (mod == NULL) { + PyErr_Clear(); + } + else { + Py_DECREF(mod); + } + mod = PyImport_ImportModule("rlcompleter"); + if (mod == NULL) { + PyErr_Clear(); + } + else { + Py_DECREF(mod); + } +} + + +static int +pymain_run_command(wchar_t *command) +{ + PyObject *unicode, *bytes; + int ret; + + unicode = PyUnicode_FromWideChar(command, -1); + if (unicode == NULL) { + goto error; + } + + if (PySys_Audit("cpython.run_command", "O", unicode) < 0) { + return pymain_exit_err_print(); + } + + bytes = PyUnicode_AsUTF8String(unicode); + Py_DECREF(unicode); + if (bytes == NULL) { + goto error; + } + + PyCompilerFlags cf = _PyCompilerFlags_INIT; + cf.cf_flags |= PyCF_IGNORE_COOKIE; + ret = PyRun_SimpleStringFlags(PyBytes_AsString(bytes), &cf); + Py_DECREF(bytes); + return (ret != 0); + +error: + PySys_WriteStderr("Unable to decode the command from the command line:\n"); + return pymain_exit_err_print(); +} + + +static int +pymain_run_module(const wchar_t *modname, int set_argv0) +{ + PyObject *module, *runpy, *runmodule, *runargs, *result; + if (PySys_Audit("cpython.run_module", "u", modname) < 0) { + return pymain_exit_err_print(); + } + runpy = PyImport_ImportModule("runpy"); + if (runpy == NULL) { + fprintf(stderr, "Could not import runpy module\n"); + return pymain_exit_err_print(); + } + runmodule = PyObject_GetAttrString(runpy, "_run_module_as_main"); + if (runmodule == NULL) { + fprintf(stderr, "Could not access runpy._run_module_as_main\n"); + Py_DECREF(runpy); + return pymain_exit_err_print(); + } + module = PyUnicode_FromWideChar(modname, wcslen(modname)); + if (module == NULL) { + fprintf(stderr, "Could not convert module name to unicode\n"); + Py_DECREF(runpy); + Py_DECREF(runmodule); + return pymain_exit_err_print(); + } + runargs = PyTuple_Pack(2, module, set_argv0 ? Py_True : Py_False); + if (runargs == NULL) { + fprintf(stderr, + "Could not create arguments for runpy._run_module_as_main\n"); + Py_DECREF(runpy); + Py_DECREF(runmodule); + Py_DECREF(module); + return pymain_exit_err_print(); + } + _PyRuntime.signals.unhandled_keyboard_interrupt = 0; + result = PyObject_Call(runmodule, runargs, NULL); + if (!result && PyErr_Occurred() == PyExc_KeyboardInterrupt) { + _PyRuntime.signals.unhandled_keyboard_interrupt = 1; + } + Py_DECREF(runpy); + Py_DECREF(runmodule); + Py_DECREF(module); + Py_DECREF(runargs); + if (result == NULL) { + return pymain_exit_err_print(); + } + Py_DECREF(result); + return 0; +} + + +static int +pymain_run_file_obj(PyObject *program_name, PyObject *filename, + int skip_source_first_line) +{ + if (PySys_Audit("cpython.run_file", "O", filename) < 0) { + return pymain_exit_err_print(); + } + + FILE *fp = _Py_fopen_obj(filename, "rb"); + if (fp == NULL) { + // Ignore the OSError + PyErr_Clear(); + PySys_FormatStderr("%S: can't open file %R: [Errno %d] %s\n", + program_name, filename, errno, strerror(errno)); + return 2; + } + + if (skip_source_first_line) { + int ch; + /* Push back first newline so line numbers remain the same */ + while ((ch = getc(fp)) != EOF) { + if (ch == '\n') { + (void)ungetc(ch, fp); + break; + } + } + } + + struct _Py_stat_struct sb; + if (_Py_fstat_noraise(fileno(fp), &sb) == 0 && S_ISDIR(sb.st_mode)) { + PySys_FormatStderr("%S: %R is a directory, cannot continue\n", + program_name, filename); + fclose(fp); + return 1; + } + + // Call pending calls like signal handlers (SIGINT) + if (Py_MakePendingCalls() == -1) { + fclose(fp); + return pymain_exit_err_print(); + } + + /* PyRun_AnyFileExFlags(closeit=1) calls fclose(fp) before running code */ + PyCompilerFlags cf = _PyCompilerFlags_INIT; + int run = _PyRun_AnyFileObject(fp, filename, 1, &cf); + return (run != 0); +} + +static int +pymain_run_file(const PyConfig *config) +{ + PyObject *filename = PyUnicode_FromWideChar(config->run_filename, -1); + if (filename == NULL) { + PyErr_Print(); + return -1; + } + PyObject *program_name = PyUnicode_FromWideChar(config->program_name, -1); + if (program_name == NULL) { + Py_DECREF(filename); + PyErr_Print(); + return -1; + } + + int res = pymain_run_file_obj(program_name, filename, + config->skip_source_first_line); + Py_DECREF(filename); + Py_DECREF(program_name); + return res; +} + + +static int +pymain_run_startup(PyConfig *config, int *exitcode) +{ + int ret; + if (!config->use_environment) { + return 0; + } + PyObject *startup = NULL; +#ifdef MS_WINDOWS + const wchar_t *env = _wgetenv(L"PYTHONSTARTUP"); + if (env == NULL || env[0] == L'\0') { + return 0; + } + startup = PyUnicode_FromWideChar(env, wcslen(env)); + if (startup == NULL) { + goto error; + } +#else + const char *env = _Py_GetEnv(config->use_environment, "PYTHONSTARTUP"); + if (env == NULL) { + return 0; + } + startup = PyUnicode_DecodeFSDefault(env); + if (startup == NULL) { + goto error; + } +#endif + if (PySys_Audit("cpython.run_startup", "O", startup) < 0) { + goto error; + } + + FILE *fp = _Py_fopen_obj(startup, "r"); + if (fp == NULL) { + int save_errno = errno; + PyErr_Clear(); + PySys_WriteStderr("Could not open PYTHONSTARTUP\n"); + + errno = save_errno; + PyErr_SetFromErrnoWithFilenameObjects(PyExc_OSError, startup, NULL); + goto error; + } + + PyCompilerFlags cf = _PyCompilerFlags_INIT; + (void) _PyRun_SimpleFileObject(fp, startup, 0, &cf); + PyErr_Clear(); + fclose(fp); + ret = 0; + +done: + Py_XDECREF(startup); + return ret; + +error: + ret = pymain_err_print(exitcode); + goto done; +} + + +/* Write an exitcode into *exitcode and return 1 if we have to exit Python. + Return 0 otherwise. */ +static int +pymain_run_interactive_hook(int *exitcode) +{ + PyObject *sys, *hook, *result; + sys = PyImport_ImportModule("sys"); + if (sys == NULL) { + goto error; + } + + hook = PyObject_GetAttrString(sys, "__interactivehook__"); + Py_DECREF(sys); + if (hook == NULL) { + PyErr_Clear(); + return 0; + } + + if (PySys_Audit("cpython.run_interactivehook", "O", hook) < 0) { + goto error; + } + + result = _PyObject_CallNoArgs(hook); + Py_DECREF(hook); + if (result == NULL) { + goto error; + } + Py_DECREF(result); + + return 0; + +error: + PySys_WriteStderr("Failed calling sys.__interactivehook__\n"); + return pymain_err_print(exitcode); +} + + +static void +pymain_set_inspect(PyConfig *config, int inspect) +{ + config->inspect = inspect; +_Py_COMP_DIAG_PUSH +_Py_COMP_DIAG_IGNORE_DEPR_DECLS + Py_InspectFlag = inspect; +_Py_COMP_DIAG_POP +} + + +static int +pymain_run_stdin(PyConfig *config) +{ + if (stdin_is_interactive(config)) { + // do exit on SystemExit + pymain_set_inspect(config, 0); + + int exitcode; + if (pymain_run_startup(config, &exitcode)) { + return exitcode; + } + + if (pymain_run_interactive_hook(&exitcode)) { + return exitcode; + } + } + + /* call pending calls like signal handlers (SIGINT) */ + if (Py_MakePendingCalls() == -1) { + return pymain_exit_err_print(); + } + + if (PySys_Audit("cpython.run_stdin", NULL) < 0) { + return pymain_exit_err_print(); + } + + PyCompilerFlags cf = _PyCompilerFlags_INIT; + int run = PyRun_AnyFileExFlags(stdin, "<stdin>", 0, &cf); + return (run != 0); +} + + +static void +pymain_repl(PyConfig *config, int *exitcode) +{ + /* Check this environment variable at the end, to give programs the + opportunity to set it from Python. */ + if (!config->inspect && _Py_GetEnv(config->use_environment, "PYTHONINSPECT")) { + pymain_set_inspect(config, 1); + } + + if (!(config->inspect && stdin_is_interactive(config) && config_run_code(config))) { + return; + } + + pymain_set_inspect(config, 0); + if (pymain_run_interactive_hook(exitcode)) { + return; + } + + PyCompilerFlags cf = _PyCompilerFlags_INIT; + int res = PyRun_AnyFileFlags(stdin, "<stdin>", &cf); + *exitcode = (res != 0); +} + + +static void +pymain_run_python(int *exitcode) +{ + PyObject *main_importer_path = NULL; + PyInterpreterState *interp = _PyInterpreterState_GET(); + /* pymain_run_stdin() modify the config */ + PyConfig *config = (PyConfig*)_PyInterpreterState_GetConfig(interp); + + /* ensure path config is written into global variables */ + if (_PyStatus_EXCEPTION(_PyPathConfig_UpdateGlobal(config))) { + goto error; + } + + assert(interp->runtime->sys_path_0 == NULL); + + if (config->run_filename != NULL) { + /* If filename is a package (ex: directory or ZIP file) which contains + __main__.py, main_importer_path is set to filename and will be + prepended to sys.path. + + Otherwise, main_importer_path is left unchanged. */ + if (pymain_get_importer(config->run_filename, &main_importer_path, + exitcode)) { + return; + } + } + + // import readline and rlcompleter before script dir is added to sys.path + pymain_import_readline(config); + + PyObject *path0 = NULL; + if (main_importer_path != NULL) { + path0 = Py_NewRef(main_importer_path); + } + else if (!config->safe_path) { + int res = _PyPathConfig_ComputeSysPath0(&config->argv, &path0); + if (res < 0) { + goto error; + } + else if (res == 0) { + Py_CLEAR(path0); + } + } + if (path0 != NULL) { + wchar_t *wstr = PyUnicode_AsWideCharString(path0, NULL); + if (wstr == NULL) { + Py_DECREF(path0); + goto error; + } + PyMemAllocatorEx old_alloc; + _PyMem_SetDefaultAllocator(PYMEM_DOMAIN_RAW, &old_alloc); + interp->runtime->sys_path_0 = _PyMem_RawWcsdup(wstr); + PyMem_SetAllocator(PYMEM_DOMAIN_RAW, &old_alloc); + PyMem_Free(wstr); + if (interp->runtime->sys_path_0 == NULL) { + Py_DECREF(path0); + goto error; + } + int res = pymain_sys_path_add_path0(interp, path0); + Py_DECREF(path0); + if (res < 0) { + goto error; + } + } + + pymain_header(config); + + _PyInterpreterState_SetRunningMain(interp); + assert(!PyErr_Occurred()); + + if (config->run_command) { + *exitcode = pymain_run_command(config->run_command); + } + else if (config->run_module) { + *exitcode = pymain_run_module(config->run_module, 1); + } + else if (main_importer_path != NULL) { + *exitcode = pymain_run_module(L"__main__", 0); + } + else if (config->run_filename != NULL) { + *exitcode = pymain_run_file(config); + } + else { + *exitcode = pymain_run_stdin(config); + } + + pymain_repl(config, exitcode); + goto done; + +error: + *exitcode = pymain_exit_err_print(); + +done: + _PyInterpreterState_SetNotRunningMain(interp); + Py_XDECREF(main_importer_path); +} + + +/* --- pymain_main() ---------------------------------------------- */ + +static void +pymain_free(void) +{ + _PyImport_Fini2(); + + /* Free global variables which cannot be freed in Py_Finalize(): + configuration options set before Py_Initialize() which should + remain valid after Py_Finalize(), since + Py_Initialize()-Py_Finalize() can be called multiple times. */ + _PyPathConfig_ClearGlobal(); + _Py_ClearStandardStreamEncoding(); + _Py_ClearArgcArgv(); + _PyRuntime_Finalize(); +} + + +static int +exit_sigint(void) +{ + /* bpo-1054041: We need to exit via the + * SIG_DFL handler for SIGINT if KeyboardInterrupt went unhandled. + * If we don't, a calling process such as a shell may not know + * about the user's ^C. https://www.cons.org/cracauer/sigint.html */ +#if defined(HAVE_GETPID) && defined(HAVE_KILL) && !defined(MS_WINDOWS) + if (PyOS_setsig(SIGINT, SIG_DFL) == SIG_ERR) { + perror("signal"); /* Impossible in normal environments. */ + } else { + kill(getpid(), SIGINT); + } + /* If setting SIG_DFL failed, or kill failed to terminate us, + * there isn't much else we can do aside from an error code. */ +#endif /* HAVE_GETPID && !MS_WINDOWS */ +#ifdef MS_WINDOWS + /* cmd.exe detects this, prints ^C, and offers to terminate. */ + /* https://msdn.microsoft.com/en-us/library/cc704588.aspx */ + return STATUS_CONTROL_C_EXIT; +#else + return SIGINT + 128; +#endif /* !MS_WINDOWS */ +} + + +static void _Py_NO_RETURN +pymain_exit_error(PyStatus status) +{ + if (_PyStatus_IS_EXIT(status)) { + /* If it's an error rather than a regular exit, leave Python runtime + alive: Py_ExitStatusException() uses the current exception and use + sys.stdout in this case. */ + pymain_free(); + } + Py_ExitStatusException(status); +} + + +int +Py_RunMain(void) +{ + int exitcode = 0; + + pymain_run_python(&exitcode); + + if (Py_FinalizeEx() < 0) { + /* Value unlikely to be confused with a non-error exit status or + other special meaning */ + exitcode = 120; + } + + pymain_free(); + + if (_PyRuntime.signals.unhandled_keyboard_interrupt) { + exitcode = exit_sigint(); + } + + return exitcode; +} + + +static int +pymain_main(_PyArgv *args) +{ + PyStatus status = pymain_init(args); + if (_PyStatus_IS_EXIT(status)) { + pymain_free(); + return status.exitcode; + } + if (_PyStatus_EXCEPTION(status)) { + pymain_exit_error(status); + } + + return Py_RunMain(); +} + + +int +Py_Main(int argc, wchar_t **argv) +{ + _PyArgv args = { + .argc = argc, + .use_bytes_argv = 0, + .bytes_argv = NULL, + .wchar_argv = argv}; + return pymain_main(&args); +} + + +int +Py_BytesMain(int argc, char **argv) +{ + _PyArgv args = { + .argc = argc, + .use_bytes_argv = 1, + .bytes_argv = argv, + .wchar_argv = NULL}; + return pymain_main(&args); +} + +#ifdef __cplusplus +} +#endif |