summaryrefslogtreecommitdiffstats
path: root/Modules/main.c
diff options
context:
space:
mode:
Diffstat (limited to 'Modules/main.c')
-rw-r--r--Modules/main.c1122
1 files changed, 778 insertions, 344 deletions
diff --git a/Modules/main.c b/Modules/main.c
index 54abbcc..6391ba7 100644
--- a/Modules/main.c
+++ b/Modules/main.c
@@ -2,6 +2,7 @@
#include "Python.h"
#include "osdefs.h"
+#include "internal/pystate.h"
#include <locale.h>
@@ -37,7 +38,7 @@ extern "C" {
/* For Py_GetArgcArgv(); set by main() */
static wchar_t **orig_argv;
-static int orig_argc;
+static int orig_argc;
/* command line options */
#define BASE_OPTS L"bBc:dEhiIJm:OqRsStuvVW:xX:?"
@@ -109,13 +110,13 @@ static const char usage_6[] =
" coercion behavior. Use PYTHONCOERCECLOCALE=warn to request display of\n"
" locale coercion and locale compatibility warnings on stderr.\n";
-static int
-usage(int exitcode, const wchar_t* program)
+static void
+usage(int error, const wchar_t* program)
{
- FILE *f = exitcode ? stderr : stdout;
+ FILE *f = error ? stderr : stdout;
fprintf(f, usage_line, program);
- if (exitcode)
+ if (error)
fprintf(f, "Try `python -h' for more information.\n");
else {
fputs(usage_1, f);
@@ -125,50 +126,57 @@ usage(int exitcode, const wchar_t* program)
fprintf(f, usage_5, (wint_t)DELIM, PYTHONHOMEHELP);
fputs(usage_6, f);
}
- return exitcode;
}
-static void RunStartupFile(PyCompilerFlags *cf)
+static void
+pymain_run_statup(PyCompilerFlags *cf)
{
char *startup = Py_GETENV("PYTHONSTARTUP");
- if (startup != NULL && startup[0] != '\0') {
- FILE *fp = _Py_fopen(startup, "r");
- if (fp != NULL) {
- (void) PyRun_SimpleFileExFlags(fp, startup, 0, cf);
- PyErr_Clear();
- fclose(fp);
- } else {
- int save_errno;
-
- save_errno = errno;
- PySys_WriteStderr("Could not open PYTHONSTARTUP\n");
- errno = save_errno;
- PyErr_SetFromErrnoWithFilename(PyExc_OSError,
- startup);
- PyErr_Print();
- PyErr_Clear();
- }
+ if (startup == NULL || startup[0] == '\0') {
+ return;
+ }
+
+ FILE *fp = _Py_fopen(startup, "r");
+ if (fp == NULL) {
+ int save_errno = errno;
+ PySys_WriteStderr("Could not open PYTHONSTARTUP\n");
+ errno = save_errno;
+
+ PyErr_SetFromErrnoWithFilename(PyExc_OSError,
+ startup);
+ PyErr_Print();
+ PyErr_Clear();
+ return;
}
+
+ (void) PyRun_SimpleFileExFlags(fp, startup, 0, cf);
+ PyErr_Clear();
+ fclose(fp);
}
-static void RunInteractiveHook(void)
+static void
+pymain_run_interactive_hook(void)
{
PyObject *sys, *hook, *result;
sys = PyImport_ImportModule("sys");
- if (sys == NULL)
+ if (sys == NULL) {
goto error;
+ }
+
hook = PyObject_GetAttrString(sys, "__interactivehook__");
Py_DECREF(sys);
- if (hook == NULL)
+ if (hook == NULL) {
PyErr_Clear();
- else {
- result = _PyObject_CallNoArg(hook);
- Py_DECREF(hook);
- if (result == NULL)
- goto error;
- else
- Py_DECREF(result);
+ return;
+ }
+
+ result = _PyObject_CallNoArg(hook);
+ Py_DECREF(hook);
+ if (result == NULL) {
+ goto error;
}
+ Py_DECREF(result);
+
return;
error:
@@ -178,7 +186,8 @@ error:
}
-static int RunModule(wchar_t *modname, int set_argv0)
+static int
+pymain_run_module(wchar_t *modname, int set_argv0)
{
PyObject *module, *runpy, *runmodule, *runargs, *result;
runpy = PyImport_ImportModule("runpy");
@@ -228,23 +237,26 @@ static int RunModule(wchar_t *modname, int set_argv0)
}
static PyObject *
-AsImportPathEntry(wchar_t *filename)
+pymain_get_importer(wchar_t *filename)
{
PyObject *sys_path0 = NULL, *importer;
sys_path0 = PyUnicode_FromWideChar(filename, wcslen(filename));
- if (sys_path0 == NULL)
+ if (sys_path0 == NULL) {
goto error;
+ }
importer = PyImport_GetImporter(sys_path0);
- if (importer == NULL)
+ if (importer == NULL) {
goto error;
+ }
if (importer == Py_None) {
Py_DECREF(sys_path0);
Py_DECREF(importer);
return NULL;
}
+
Py_DECREF(importer);
return sys_path0;
@@ -258,49 +270,25 @@ error:
static int
-RunMainFromImporter(PyObject *sys_path0)
-{
- PyObject *sys_path;
- int sts;
-
- /* Assume sys_path0 has already been checked by AsImportPathEntry,
- * so put it in sys.path[0] and import __main__ */
- sys_path = PySys_GetObject("path");
- if (sys_path == NULL) {
- PyErr_SetString(PyExc_RuntimeError, "unable to get sys.path");
- goto error;
- }
- sts = PyList_Insert(sys_path, 0, sys_path0);
- if (sts) {
- sys_path0 = NULL;
- goto error;
- }
-
- sts = RunModule(L"__main__", 0);
- return sts != 0;
-
-error:
- Py_XDECREF(sys_path0);
- PyErr_Print();
- return 1;
-}
-
-static int
-run_command(wchar_t *command, PyCompilerFlags *cf)
+pymain_run_command(wchar_t *command, PyCompilerFlags *cf)
{
PyObject *unicode, *bytes;
int ret;
unicode = PyUnicode_FromWideChar(command, -1);
- if (unicode == NULL)
+ if (unicode == NULL) {
goto error;
+ }
+
bytes = PyUnicode_AsUTF8String(unicode);
Py_DECREF(unicode);
- if (bytes == NULL)
+ if (bytes == NULL) {
goto error;
+ }
+
ret = PyRun_SimpleStringFlags(PyBytes_AsString(bytes), cf);
Py_DECREF(bytes);
- return ret != 0;
+ return (ret != 0);
error:
PySys_WriteStderr("Unable to decode the command from the command line:\n");
@@ -308,8 +296,9 @@ error:
return 1;
}
+
static int
-run_file(FILE *fp, const wchar_t *filename, PyCompilerFlags *p_cf)
+pymain_run_file(FILE *fp, const wchar_t *filename, PyCompilerFlags *p_cf)
{
PyObject *unicode, *bytes = NULL;
const char *filename_str;
@@ -327,15 +316,17 @@ run_file(FILE *fp, const wchar_t *filename, PyCompilerFlags *p_cf)
bytes = PyUnicode_EncodeFSDefault(unicode);
Py_DECREF(unicode);
}
- if (bytes != NULL)
+ if (bytes != NULL) {
filename_str = PyBytes_AsString(bytes);
+ }
else {
PyErr_Clear();
filename_str = "<encoding error>";
}
}
- else
+ else {
filename_str = "<stdin>";
+ }
run = PyRun_AnyFileExFlags(fp, filename_str, filename != NULL, p_cf);
Py_XDECREF(bytes);
@@ -348,10 +339,15 @@ run_file(FILE *fp, const wchar_t *filename, PyCompilerFlags *p_cf)
/*TODO: Add arg processing to PEP 432 as a new configuration setup API
*/
typedef struct {
+ size_t len;
+ wchar_t **options;
+} _Py_OptList;
+
+typedef struct {
wchar_t *filename; /* Trailing arg without -c or -m */
wchar_t *command; /* -c argument */
wchar_t *module; /* -m argument */
- PyObject *warning_options; /* -W options */
+ _Py_OptList warning_options; /* -W options */
PyObject *extra_options; /* -X options */
int print_help; /* -h, -? options */
int print_version; /* -V option */
@@ -368,36 +364,189 @@ typedef struct {
int verbosity; /* Py_VerboseFlag */
int quiet_flag; /* Py_QuietFlag */
int skip_first_line; /* -x option */
+ _Py_OptList xoptions; /* -X options */
} _Py_CommandLineDetails;
-#define _Py_CommandLineDetails_INIT \
- {NULL, NULL, NULL, NULL, NULL, \
- 0, 0, 0, 0, 0, 0, 0, 0, \
- 0, 0, 0, 0, 0, 0, 0}
+/* Structure used by Py_Main() to pass data to subfunctions */
+typedef struct {
+ /* Exit status ("exit code") */
+ int status;
+ PyCompilerFlags cf;
+ /* non-zero is stdin is a TTY or if -i option is used */
+ int stdin_is_interactive;
+ _PyCoreConfig core_config;
+ _Py_CommandLineDetails cmdline;
+ PyObject *main_importer_path;
+ /* non-zero if filename, command (-c) or module (-m) is set
+ on the command line */
+ int run_code;
+ wchar_t *program_name;
+ /* Error message if a function failed */
+ _PyInitError err;
+ /* PYTHONWARNINGS env var */
+ _Py_OptList env_warning_options;
+ int argc;
+ wchar_t **argv;
+} _PyMain;
+
+/* .cmdline is initialized to zeros */
+#define _PyMain_INIT \
+ {.status = 0, \
+ .cf = {.cf_flags = 0}, \
+ .core_config = _PyCoreConfig_INIT, \
+ .main_importer_path = NULL, \
+ .run_code = -1, \
+ .program_name = NULL, \
+ .err = _Py_INIT_OK(), \
+ .env_warning_options = {0, NULL}}
+
+
+#define INIT_NO_MEMORY() _Py_INIT_ERR("memory allocation failed")
+
+
+static void
+pymain_optlist_clear(_Py_OptList *list)
+{
+ for (size_t i=0; i < list->len; i++) {
+ PyMem_RawFree(list->options[i]);
+ }
+ PyMem_RawFree(list->options);
+ list->len = 0;
+ list->options = NULL;
+}
+
+static void
+pymain_free_impl(_PyMain *pymain)
+{
+ _Py_CommandLineDetails *cmdline = &pymain->cmdline;
+ pymain_optlist_clear(&cmdline->warning_options);
+ pymain_optlist_clear(&cmdline->xoptions);
+ PyMem_RawFree(cmdline->command);
+
+ pymain_optlist_clear(&pymain->env_warning_options);
+ Py_CLEAR(pymain->main_importer_path);
+ PyMem_RawFree(pymain->program_name);
+
+#ifdef __INSURE__
+ /* Insure++ is a memory analysis tool that aids in discovering
+ * memory leaks and other memory problems. On Python exit, the
+ * interned string dictionaries are flagged as being in use at exit
+ * (which it is). Under normal circumstances, this is fine because
+ * the memory will be automatically reclaimed by the system. Under
+ * memory debugging, it's a huge source of useless noise, so we
+ * trade off slower shutdown for less distraction in the memory
+ * reports. -baw
+ */
+ _Py_ReleaseInternedUnicodeStrings();
+#endif /* __INSURE__ */
+}
+
+static void
+pymain_free(_PyMain *pymain)
+{
+ /* Call pymain_free() with the memory allocator used by pymain_init() */
+ PyMemAllocatorEx old_alloc, raw_alloc;
+ PyMem_GetAllocator(PYMEM_DOMAIN_RAW, &old_alloc);
+ _PyMem_GetDefaultRawAllocator(&raw_alloc);
+ PyMem_SetAllocator(PYMEM_DOMAIN_RAW, &raw_alloc);
+
+ pymain_free_impl(pymain);
+
+ PyMem_SetAllocator(PYMEM_DOMAIN_RAW, &old_alloc);
+}
+
+
+static int
+pymain_run_main_from_importer(_PyMain *pymain)
+{
+ PyObject *sys_path0 = pymain->main_importer_path;
+ PyObject *sys_path;
+ int sts;
+
+ /* Assume sys_path0 has already been checked by pymain_get_importer(),
+ * so put it in sys.path[0] and import __main__ */
+ sys_path = PySys_GetObject("path");
+ if (sys_path == NULL) {
+ PyErr_SetString(PyExc_RuntimeError, "unable to get sys.path");
+ goto error;
+ }
+
+ sts = PyList_Insert(sys_path, 0, sys_path0);
+ if (sts) {
+ sys_path0 = NULL;
+ goto error;
+ }
+
+ sts = pymain_run_module(L"__main__", 0);
+ return sts != 0;
+
+error:
+ Py_CLEAR(pymain->main_importer_path);
+ PyErr_Print();
+ return 1;
+}
+
+
+static wchar_t*
+pymain_strdup(wchar_t *str)
+{
+ size_t len = wcslen(str) + 1; /* +1 for NUL character */
+ wchar_t *str2 = PyMem_RawMalloc(sizeof(wchar_t) * len);
+ if (str2 == NULL) {
+ return NULL;
+ }
+ memcpy(str2, str, len * sizeof(wchar_t));
+ return str2;
+}
+
+
+static int
+pymain_optlist_append(_Py_OptList *list, wchar_t *str)
+{
+ wchar_t *str2 = pymain_strdup(str);
+ if (str2 == NULL) {
+ return -1;
+ }
+
+ size_t size = (list->len + 1) * sizeof(list[0]);
+ wchar_t **options2 = (wchar_t **)PyMem_RawRealloc(list->options, size);
+ if (options2 == NULL) {
+ PyMem_RawFree(str2);
+ return -1;
+ }
+ options2[list->len] = str2;
+ list->options = options2;
+ list->len++;
+ return 0;
+}
+
+/* Parse the command line arguments
+ Return 0 on success.
+ Return 1 if parsing failed.
+ Set pymain->err and return -1 on other errors. */
static int
-read_command_line(int argc, wchar_t **argv, _Py_CommandLineDetails *cmdline)
+pymain_parse_cmdline(_PyMain *pymain)
{
- PyObject *warning_option = NULL;
- wchar_t *command = NULL;
- wchar_t *module = NULL;
- int c;
+ _Py_CommandLineDetails *cmdline = &pymain->cmdline;
_PyOS_ResetGetOpt();
+ do {
+ int c = _PyOS_GetOpt(pymain->argc, pymain->argv, PROGRAM_OPTS);
+ if (c == EOF) {
+ break;
+ }
- while ((c = _PyOS_GetOpt(argc, argv, PROGRAM_OPTS)) != EOF) {
if (c == 'c') {
- size_t len;
/* -c is the last option; following arguments
that look like options are left for the
command to interpret. */
-
- len = wcslen(_PyOS_optarg) + 1 + 1;
- command = (wchar_t *)PyMem_RawMalloc(sizeof(wchar_t) * len);
- if (command == NULL)
- Py_FatalError(
- "not enough memory to copy -c argument");
- wcscpy(command, _PyOS_optarg);
+ size_t len = wcslen(_PyOS_optarg) + 1 + 1;
+ wchar_t *command = PyMem_RawMalloc(sizeof(wchar_t) * len);
+ if (command == NULL) {
+ goto out_of_memory;
+ }
+ memcpy(command, _PyOS_optarg, len * sizeof(wchar_t));
command[len - 2] = '\n';
command[len - 1] = 0;
cmdline->command = command;
@@ -408,8 +557,7 @@ read_command_line(int argc, wchar_t **argv, _Py_CommandLineDetails *cmdline)
/* -m is the last option; following arguments
that look like options are left for the
module to interpret. */
- module = _PyOS_optarg;
- cmdline->module = module;
+ cmdline->module = _PyOS_optarg;
break;
}
@@ -428,6 +576,7 @@ read_command_line(int argc, wchar_t **argv, _Py_CommandLineDetails *cmdline)
break;
case 'I':
+ pymain->core_config.ignore_environment++;
cmdline->isolated++;
cmdline->no_user_site_directory++;
break;
@@ -451,7 +600,7 @@ read_command_line(int argc, wchar_t **argv, _Py_CommandLineDetails *cmdline)
break;
case 'E':
- /* Handled prior to core initialization */
+ pymain->core_config.ignore_environment++;
break;
case 't':
@@ -480,21 +629,17 @@ read_command_line(int argc, wchar_t **argv, _Py_CommandLineDetails *cmdline)
break;
case 'W':
- if (cmdline->warning_options == NULL)
- cmdline->warning_options = PyList_New(0);
- if (cmdline->warning_options == NULL)
- Py_FatalError("failure in handling of -W argument");
- warning_option = PyUnicode_FromWideChar(_PyOS_optarg, -1);
- if (warning_option == NULL)
- Py_FatalError("failure in handling of -W argument");
- if (PyList_Append(cmdline->warning_options, warning_option) == -1)
- Py_FatalError("failure in handling of -W argument");
- Py_DECREF(warning_option);
+ if (pymain_optlist_append(&cmdline->warning_options,
+ _PyOS_optarg) < 0) {
+ goto out_of_memory;
+ }
break;
case 'X':
- /* TODO: Delay addition of X options to sys module */
- PySys_AddXOption(_PyOS_optarg);
+ if (pymain_optlist_append(&cmdline->xoptions,
+ _PyOS_optarg) < 0) {
+ goto out_of_memory;
+ }
break;
case 'q':
@@ -508,20 +653,26 @@ read_command_line(int argc, wchar_t **argv, _Py_CommandLineDetails *cmdline)
/* This space reserved for other options */
default:
- return -1;
- /*NOTREACHED*/
-
+ /* unknown argument: parsing failed */
+ return 1;
}
- }
+ } while (1);
- if (command == NULL && module == NULL && _PyOS_optind < argc &&
- wcscmp(argv[_PyOS_optind], L"-") != 0)
+ if (cmdline->command == NULL && cmdline->module == NULL
+ && _PyOS_optind < pymain->argc
+ && wcscmp(pymain->argv[_PyOS_optind], L"-") != 0)
{
- cmdline->filename = argv[_PyOS_optind];
+ cmdline->filename = pymain->argv[_PyOS_optind];
}
+
return 0;
+
+out_of_memory:
+ pymain->err = INIT_NO_MEMORY();
+ return -1;
}
+
static void
maybe_set_flag(int *flag, int value)
{
@@ -534,146 +685,142 @@ maybe_set_flag(int *flag, int value)
}
}
+
static int
-apply_command_line_and_environment(_Py_CommandLineDetails *cmdline)
+pymain_add_xoptions(_PyMain *pymain)
{
- maybe_set_flag(&Py_BytesWarningFlag, cmdline->bytes_warning);
- maybe_set_flag(&Py_DebugFlag, cmdline->debug);
- maybe_set_flag(&Py_InspectFlag, cmdline->inspect);
- maybe_set_flag(&Py_InteractiveFlag, cmdline->interactive);
- maybe_set_flag(&Py_IsolatedFlag, cmdline->isolated);
- maybe_set_flag(&Py_OptimizeFlag, cmdline->optimization_level);
- maybe_set_flag(&Py_DontWriteBytecodeFlag, cmdline->dont_write_bytecode);
- maybe_set_flag(&Py_NoUserSiteDirectory, cmdline->no_user_site_directory);
- maybe_set_flag(&Py_NoSiteFlag, cmdline->no_site_import);
- maybe_set_flag(&Py_UnbufferedStdioFlag, cmdline->use_unbuffered_io);
- maybe_set_flag(&Py_VerboseFlag, cmdline->verbosity);
- maybe_set_flag(&Py_QuietFlag, cmdline->quiet_flag);
-
- /* TODO: Apply PYTHONWARNINGS & -W options to sys module here */
- /* TODO: Apply -X options to sys module here */
+ _Py_OptList *options = &pymain->cmdline.xoptions;
+ for (size_t i=0; i < options->len; i++) {
+ wchar_t *option = options->options[i];
+ if (_PySys_AddXOptionWithError(option) < 0) {
+ pymain->err = INIT_NO_MEMORY();
+ return -1;
+ }
+ }
return 0;
}
-int
-Py_Main(int argc, wchar_t **argv)
-{
- int c;
- int sts;
- FILE *fp = stdin;
- char *p;
-#ifdef MS_WINDOWS
- wchar_t *wp;
-#endif
- int stdin_is_interactive = 0;
- _Py_CommandLineDetails cmdline = _Py_CommandLineDetails_INIT;
- _PyCoreConfig core_config = _PyCoreConfig_INIT;
- PyCompilerFlags cf;
- PyObject *main_importer_path = NULL;
-
- cf.cf_flags = 0;
- orig_argc = argc; /* For Py_GetArgcArgv() */
- orig_argv = argv;
-
- /* Hash randomization needed early for all string operations
- (including -W and -X options). */
- _PyOS_opterr = 0; /* prevent printing the error in 1st pass */
- while ((c = _PyOS_GetOpt(argc, argv, PROGRAM_OPTS)) != EOF) {
- if (c == 'm' || c == 'c') {
- /* -c / -m is the last option: following arguments are
- not interpreter options. */
- break;
+static int
+pymain_add_warnings_optlist(_Py_OptList *warnings)
+{
+ for (size_t i = 0; i < warnings->len; i++) {
+ PyObject *option = PyUnicode_FromWideChar(warnings->options[i], -1);
+ if (option == NULL) {
+ return -1;
}
- if (c == 'E' || c == 'I') {
- core_config.ignore_environment++;
- break;
+ if (_PySys_AddWarnOptionWithError(option)) {
+ Py_DECREF(option);
+ return -1;
}
+ Py_DECREF(option);
}
+ return 0;
+}
- /* Initialize the core language runtime */
- Py_IgnoreEnvironmentFlag = core_config.ignore_environment;
- core_config._disable_importlib = 0;
- core_config.allocator = Py_GETENV("PYTHONMALLOC");
- _Py_InitializeCore(&core_config);
+static int
+pymain_add_warnings_options(_PyMain *pymain)
+{
+ PySys_ResetWarnOptions();
- /* Reprocess the command line with the language runtime available */
- if (read_command_line(argc, argv, &cmdline)) {
- return usage(2, argv[0]);
+ if (pymain_add_warnings_optlist(&pymain->env_warning_options) < 0) {
+ pymain->err = INIT_NO_MEMORY();
+ return -1;
}
-
- if (cmdline.print_help) {
- return usage(0, argv[0]);
+ if (pymain_add_warnings_optlist(&pymain->cmdline.warning_options) < 0) {
+ pymain->err = INIT_NO_MEMORY();
+ return -1;
}
+ return 0;
+}
- if (cmdline.print_version) {
- printf("Python %s\n", cmdline.print_version >= 2 ? Py_GetVersion() : PY_VERSION);
+
+/* Get warning options from PYTHONWARNINGS environment variable.
+ Return 0 on success.
+ Set pymain->err and return -1 on error. */
+static int
+pymain_warnings_envvar(_PyMain *pymain)
+{
+ if (Py_IgnoreEnvironmentFlag) {
return 0;
}
- PySys_ResetWarnOptions();
- apply_command_line_and_environment(&cmdline);
-
#ifdef MS_WINDOWS
- if (!Py_IgnoreEnvironmentFlag && (wp = _wgetenv(L"PYTHONWARNINGS")) &&
- *wp != L'\0') {
+ wchar_t *wp;
+
+ if ((wp = _wgetenv(L"PYTHONWARNINGS")) && *wp != L'\0') {
wchar_t *buf, *warning, *context = NULL;
buf = (wchar_t *)PyMem_RawMalloc((wcslen(wp) + 1) * sizeof(wchar_t));
- if (buf == NULL)
- Py_FatalError(
- "not enough memory to copy PYTHONWARNINGS");
+ if (buf == NULL) {
+ goto out_of_memory;
+ }
wcscpy(buf, wp);
for (warning = wcstok_s(buf, L",", &context);
warning != NULL;
warning = wcstok_s(NULL, L",", &context)) {
- PySys_AddWarnOption(warning);
+
+ if (pymain_optlist_append(&pymain->env_warning_options,
+ warning) < 0) {
+ PyMem_RawFree(buf);
+ goto out_of_memory;
+ }
}
PyMem_RawFree(buf);
}
#else
+ char *p;
+
if ((p = Py_GETENV("PYTHONWARNINGS")) && *p != '\0') {
char *buf, *oldloc;
- PyObject *unicode;
/* settle for strtok here as there's no one standard
C89 wcstok */
buf = (char *)PyMem_RawMalloc(strlen(p) + 1);
- if (buf == NULL)
- Py_FatalError(
- "not enough memory to copy PYTHONWARNINGS");
+ if (buf == NULL) {
+ goto out_of_memory;
+ }
strcpy(buf, p);
oldloc = _PyMem_RawStrdup(setlocale(LC_ALL, NULL));
setlocale(LC_ALL, "");
for (p = strtok(buf, ","); p != NULL; p = strtok(NULL, ",")) {
-#ifdef __APPLE__
- /* Use utf-8 on Mac OS X */
- unicode = PyUnicode_FromString(p);
-#else
- unicode = PyUnicode_DecodeLocale(p, "surrogateescape");
-#endif
- if (unicode == NULL) {
- /* ignore errors */
- PyErr_Clear();
- continue;
+ size_t len;
+ wchar_t *warning = Py_DecodeLocale(p, &len);
+ if (warning == NULL) {
+ if (len == (size_t)-2) {
+ pymain->err = _Py_INIT_ERR("failed to decode "
+ "PYTHONWARNINGS");
+ return -1;
+ }
+ else {
+ goto out_of_memory;
+ }
}
- PySys_AddWarnOptionUnicode(unicode);
- Py_DECREF(unicode);
+ if (pymain_optlist_append(&pymain->env_warning_options,
+ warning) < 0) {
+ PyMem_RawFree(warning);
+ goto out_of_memory;
+ }
+ PyMem_RawFree(warning);
}
setlocale(LC_ALL, oldloc);
PyMem_RawFree(oldloc);
PyMem_RawFree(buf);
}
#endif
- if (cmdline.warning_options != NULL) {
- Py_ssize_t i;
- for (i = 0; i < PyList_GET_SIZE(cmdline.warning_options); i++) {
- PySys_AddWarnOptionUnicode(PyList_GET_ITEM(cmdline.warning_options, i));
- }
- Py_DECREF(cmdline.warning_options);
- }
+ return 0;
- stdin_is_interactive = Py_FdIsInteractive(stdin, (char *)0);
+out_of_memory:
+ pymain->err = INIT_NO_MEMORY();
+ return -1;
+}
+
+
+static void
+pymain_init_stdio(_PyMain *pymain)
+{
+ pymain->stdin_is_interactive = (isatty(fileno(stdin))
+ || Py_InteractiveFlag);
#if defined(MS_WINDOWS) || defined(__CYGWIN__)
/* don't translate newlines (\r\n <=> \n) */
@@ -706,8 +853,20 @@ Py_Main(int argc, wchar_t **argv)
#endif /* !MS_WINDOWS */
/* Leave stderr alone - it should be unbuffered anyway. */
}
+}
+
+
+/* Get the program name: use PYTHONEXECUTABLE and __PYVENV_LAUNCHER__
+ environment variables on macOS if available, use argv[0] by default.
+ Return 0 on success.
+ Set pymain->err and return -1 on error. */
+static int
+pymain_get_program_name(_PyMain *pymain)
+{
+ assert(pymain->program_name == NULL);
#ifdef __APPLE__
+ char *p;
/* On MacOS X, when the Python interpreter is embedded in an
application bundle, it gets executed by a bootstrapping script
that does os.execve() with an argv[0] that's different from the
@@ -723,171 +882,295 @@ Py_Main(int argc, wchar_t **argv)
buffer = PyMem_RawMalloc(len * sizeof(wchar_t));
if (buffer == NULL) {
- Py_FatalError(
- "not enough memory to copy PYTHONEXECUTABLE");
+ goto out_of_memory;
}
mbstowcs(buffer, p, len);
- Py_SetProgramName(buffer);
- /* buffer is now handed off - do not free */
- } else {
+ pymain->program_name = buffer;
+ }
#ifdef WITH_NEXT_FRAMEWORK
+ else {
char* pyvenv_launcher = getenv("__PYVENV_LAUNCHER__");
-
if (pyvenv_launcher && *pyvenv_launcher) {
/* Used by Mac/Tools/pythonw.c to forward
* the argv0 of the stub executable
*/
- wchar_t* wbuf = Py_DecodeLocale(pyvenv_launcher, NULL);
-
+ size_t len;
+ wchar_t* wbuf = Py_DecodeLocale(pyvenv_launcher, &len);
if (wbuf == NULL) {
- Py_FatalError("Cannot decode __PYVENV_LAUNCHER__");
+ if (len == (size_t)-2) {
+ pymain->err = _Py_INIT_ERR("failed to decode "
+ "__PYVENV_LAUNCHER__");
+ return -1;
+ }
+ else {
+ goto out_of_memory;
+ }
}
- Py_SetProgramName(wbuf);
-
- /* Don't free wbuf, the argument to Py_SetProgramName
- * must remain valid until Py_FinalizeEx is called.
- */
- } else {
- Py_SetProgramName(argv[0]);
+ pymain->program_name = wbuf;
}
-#else
- Py_SetProgramName(argv[0]);
-#endif
}
-#else
- Py_SetProgramName(argv[0]);
-#endif
- /* Replaces previous call to Py_Initialize()
- *
- * TODO: Move environment queries (etc) into Py_ReadConfig
- */
- {
- _PyMainInterpreterConfig config = _PyMainInterpreterConfig_INIT;
+#endif /* WITH_NEXT_FRAMEWORK */
+#endif /* __APPLE__ */
+
+ if (pymain->program_name == NULL) {
+ /* Use argv[0] by default */
+ pymain->program_name = pymain_strdup(pymain->argv[0]);
+ if (pymain->program_name == NULL) {
+ goto out_of_memory;
+ }
+ }
+ return 0;
+
+out_of_memory:
+ pymain->err = INIT_NO_MEMORY();
+ return -1;
+}
- /* TODO: Moar config options! */
- config.install_signal_handlers = 1;
- /* TODO: Print any exceptions raised by these operations */
- if (_Py_ReadMainInterpreterConfig(&config))
- Py_FatalError("Py_Main: Py_ReadMainInterpreterConfig failed");
- if (_Py_InitializeMainInterpreter(&config))
- Py_FatalError("Py_Main: Py_InitializeMainInterpreter failed");
+
+/* Initialize the main interpreter.
+ *
+ * Replaces previous call to Py_Initialize()
+ *
+ * TODO: Move environment queries (etc) into Py_ReadConfig
+ *
+ * Return 0 on success.
+ * Set pymain->err and return -1 on error.
+ */
+static int
+pymain_init_main_interpreter(_PyMain *pymain)
+{
+ _PyMainInterpreterConfig config = _PyMainInterpreterConfig_INIT;
+ _PyInitError err;
+
+ /* TODO: Moar config options! */
+ config.install_signal_handlers = 1;
+
+ /* TODO: Print any exceptions raised by these operations */
+ err = _Py_ReadMainInterpreterConfig(&config);
+ if (_Py_INIT_FAILED(err)) {
+ pymain->err = err;
+ return -1;
}
+ err = _Py_InitializeMainInterpreter(&config);
+ if (_Py_INIT_FAILED(err)) {
+ pymain->err = err;
+ return -1;
+ }
+ return 0;
+}
+
+
+static void
+pymain_header(_PyMain *pymain)
+{
/* TODO: Move this to _PyRun_PrepareMain */
- if (!Py_QuietFlag && (Py_VerboseFlag ||
- (cmdline.command == NULL && cmdline.filename == NULL &&
- cmdline.module == NULL && stdin_is_interactive))) {
- fprintf(stderr, "Python %s on %s\n",
- Py_GetVersion(), Py_GetPlatform());
- if (!Py_NoSiteFlag)
- fprintf(stderr, "%s\n", COPYRIGHT);
+ if (Py_QuietFlag) {
+ return;
+ }
+
+ if (!Py_VerboseFlag && (pymain->run_code || !pymain->stdin_is_interactive)) {
+ return;
}
+ fprintf(stderr, "Python %s on %s\n", Py_GetVersion(), Py_GetPlatform());
+ if (!Py_NoSiteFlag) {
+ fprintf(stderr, "%s\n", COPYRIGHT);
+ }
+}
+
+
+static void
+pymain_init_argv(_PyMain *pymain)
+{
+ _Py_CommandLineDetails *cmdline = &pymain->cmdline;
+
/* TODO: Move this to _Py_InitializeMainInterpreter */
- if (cmdline.command != NULL) {
+ if (cmdline->command != NULL) {
/* Backup _PyOS_optind and force sys.argv[0] = '-c' */
_PyOS_optind--;
- argv[_PyOS_optind] = L"-c";
+ pymain->argv[_PyOS_optind] = L"-c";
}
- if (cmdline.module != NULL) {
+ if (cmdline->module != NULL) {
/* Backup _PyOS_optind and force sys.argv[0] = '-m'*/
_PyOS_optind--;
- argv[_PyOS_optind] = L"-m";
+ pymain->argv[_PyOS_optind] = L"-m";
}
- if (cmdline.filename != NULL) {
- main_importer_path = AsImportPathEntry(cmdline.filename);
+ if (cmdline->filename != NULL) {
+ pymain->main_importer_path = pymain_get_importer(cmdline->filename);
}
- if (main_importer_path != NULL) {
- /* Let RunMainFromImporter adjust sys.path[0] later */
- PySys_SetArgvEx(argc-_PyOS_optind, argv+_PyOS_optind, 0);
+ int update_path;
+ if (pymain->main_importer_path != NULL) {
+ /* Let pymain_run_main_from_importer() adjust sys.path[0] later */
+ update_path = 0;
} else {
/* Use config settings to decide whether or not to update sys.path[0] */
- PySys_SetArgv(argc-_PyOS_optind, argv+_PyOS_optind);
+ update_path = (Py_IsolatedFlag == 0);
}
+ PySys_SetArgvEx(pymain->argc - _PyOS_optind,
+ pymain->argv + _PyOS_optind,
+ update_path);
+}
- if ((Py_InspectFlag || (cmdline.command == NULL &&
- cmdline.filename == NULL &&
- cmdline.module == NULL)) &&
- isatty(fileno(stdin)) &&
- !Py_IsolatedFlag) {
- PyObject *v;
- v = PyImport_ImportModule("readline");
- if (v == NULL)
- PyErr_Clear();
- else
- Py_DECREF(v);
+
+/* Set Py_XXX global configuration variables */
+static void
+pymain_set_global_config(_PyMain *pymain)
+{
+ _Py_CommandLineDetails *cmdline = &pymain->cmdline;
+ maybe_set_flag(&Py_BytesWarningFlag, cmdline->bytes_warning);
+ maybe_set_flag(&Py_DebugFlag, cmdline->debug);
+ maybe_set_flag(&Py_InspectFlag, cmdline->inspect);
+ maybe_set_flag(&Py_InteractiveFlag, cmdline->interactive);
+ maybe_set_flag(&Py_IsolatedFlag, cmdline->isolated);
+ maybe_set_flag(&Py_OptimizeFlag, cmdline->optimization_level);
+ maybe_set_flag(&Py_DontWriteBytecodeFlag, cmdline->dont_write_bytecode);
+ maybe_set_flag(&Py_NoUserSiteDirectory, cmdline->no_user_site_directory);
+ maybe_set_flag(&Py_NoSiteFlag, cmdline->no_site_import);
+ maybe_set_flag(&Py_UnbufferedStdioFlag, cmdline->use_unbuffered_io);
+ maybe_set_flag(&Py_VerboseFlag, cmdline->verbosity);
+ maybe_set_flag(&Py_QuietFlag, cmdline->quiet_flag);
+
+ maybe_set_flag(&Py_IgnoreEnvironmentFlag, pymain->core_config.ignore_environment);
+}
+
+
+/* Propagate options parsed from the command line and environment variables
+ to the Python runtime.
+
+ Return 0 on success, or set pymain->err and return -1 on error. */
+static int
+pymain_configure_pyruntime(_PyMain *pymain)
+{
+ Py_SetProgramName(pymain->program_name);
+ /* Don't free program_name here: the argument to Py_SetProgramName
+ must remain valid until Py_FinalizeEx is called. The string is freed
+ by pymain_free(). */
+
+ if (pymain_add_xoptions(pymain)) {
+ return -1;
+ }
+ if (pymain_add_warnings_options(pymain)) {
+ return -1;
+ }
+ return 0;
+}
+
+
+static void
+pymain_import_readline(_PyMain *pymain)
+{
+ if (Py_IsolatedFlag) {
+ return;
+ }
+ if (!Py_InspectFlag && pymain->run_code) {
+ return;
+ }
+ if (!isatty(fileno(stdin))) {
+ return;
}
- if (cmdline.command) {
- sts = run_command(cmdline.command, &cf);
- PyMem_RawFree(cmdline.command);
- } else if (cmdline.module) {
- sts = (RunModule(cmdline.module, 1) != 0);
+ PyObject *mod = PyImport_ImportModule("readline");
+ if (mod == NULL) {
+ PyErr_Clear();
}
else {
+ Py_DECREF(mod);
+ }
+}
- if (cmdline.filename == NULL && stdin_is_interactive) {
- Py_InspectFlag = 0; /* do exit on SystemExit */
- RunStartupFile(&cf);
- RunInteractiveHook();
- }
- /* XXX */
-
- sts = -1; /* keep track of whether we've already run __main__ */
- if (main_importer_path != NULL) {
- sts = RunMainFromImporter(main_importer_path);
- }
+static FILE*
+pymain_open_filename(_PyMain *pymain)
+{
+ _Py_CommandLineDetails *cmdline = &pymain->cmdline;
+ FILE* fp;
+
+ fp = _Py_wfopen(cmdline->filename, L"r");
+ if (fp == NULL) {
+ char *cfilename_buffer;
+ const char *cfilename;
+ int err = errno;
+ cfilename_buffer = Py_EncodeLocale(cmdline->filename, NULL);
+ if (cfilename_buffer != NULL)
+ cfilename = cfilename_buffer;
+ else
+ cfilename = "<unprintable file name>";
+ fprintf(stderr, "%ls: can't open file '%s': [Errno %d] %s\n",
+ pymain->argv[0], cfilename, err, strerror(err));
+ PyMem_Free(cfilename_buffer);
+ pymain->status = 2;
+ return NULL;
+ }
- if (sts==-1 && cmdline.filename != NULL) {
- fp = _Py_wfopen(cmdline.filename, L"r");
- if (fp == NULL) {
- char *cfilename_buffer;
- const char *cfilename;
- int err = errno;
- cfilename_buffer = Py_EncodeLocale(cmdline.filename, NULL);
- if (cfilename_buffer != NULL)
- cfilename = cfilename_buffer;
- else
- cfilename = "<unprintable file name>";
- fprintf(stderr, "%ls: can't open file '%s': [Errno %d] %s\n",
- argv[0], cfilename, err, strerror(err));
- if (cfilename_buffer)
- PyMem_Free(cfilename_buffer);
- return 2;
- }
- else if (cmdline.skip_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)) {
- fprintf(stderr,
- "%ls: '%ls' is a directory, cannot continue\n",
- argv[0], cmdline.filename);
- fclose(fp);
- return 1;
- }
+ if (cmdline->skip_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)) {
+ fprintf(stderr,
+ "%ls: '%ls' is a directory, cannot continue\n",
+ pymain->argv[0], cmdline->filename);
+ fclose(fp);
+ pymain->status = 1;
+ return NULL;
+ }
+
+ return fp;
+}
- if (sts == -1)
- sts = run_file(fp, cmdline.filename, &cf);
+
+static void
+pymain_run(_PyMain *pymain)
+{
+ _Py_CommandLineDetails *cmdline = &pymain->cmdline;
+
+ if (cmdline->filename == NULL && pymain->stdin_is_interactive) {
+ Py_InspectFlag = 0; /* do exit on SystemExit */
+ pymain_run_statup(&pymain->cf);
+ pymain_run_interactive_hook();
+ }
+ /* XXX */
+
+ if (pymain->main_importer_path != NULL) {
+ pymain->status = pymain_run_main_from_importer(pymain);
+ return;
+ }
+
+ FILE *fp;
+ if (cmdline->filename != NULL) {
+ fp = pymain_open_filename(pymain);
+ if (fp == NULL) {
+ return;
+ }
+ }
+ else {
+ fp = stdin;
}
+ pymain->status = pymain_run_file(fp, cmdline->filename, &pymain->cf);
+}
+
+
+static void
+pymain_repl(_PyMain *pymain)
+{
+ char *p;
+
/* Check this environment variable at the end, to give programs the
* opportunity to set it from Python.
*/
@@ -897,34 +1180,185 @@ Py_Main(int argc, wchar_t **argv)
Py_InspectFlag = 1;
}
- if (Py_InspectFlag && stdin_is_interactive &&
- (cmdline.filename != NULL || cmdline.command != NULL || cmdline.module != NULL)) {
+ if (Py_InspectFlag && pymain->stdin_is_interactive && pymain->run_code) {
Py_InspectFlag = 0;
- RunInteractiveHook();
+ pymain_run_interactive_hook();
/* XXX */
- sts = PyRun_AnyFileFlags(stdin, "<stdin>", &cf) != 0;
+ int res = PyRun_AnyFileFlags(stdin, "<stdin>", &pymain->cf);
+ pymain->status = (res != 0);
+ }
+}
+
+
+/* Parse the command line.
+ Handle --version and --help options directly.
+
+ Return 1 if Python must exit.
+ Return 0 on success.
+ Set pymain->err and return -1 on failure. */
+static int
+pymain_init_cmdline(_PyMain *pymain)
+{
+ _Py_CommandLineDetails *cmdline = &pymain->cmdline;
+
+ int res = pymain_parse_cmdline(pymain);
+ if (res < 0) {
+ return -1;
+ }
+ if (res) {
+ usage(1, pymain->argv[0]);
+ pymain->status = 2;
+ return 1;
+ }
+
+ if (cmdline->print_help) {
+ usage(0, pymain->argv[0]);
+ pymain->status = 0;
+ return 1;
+ }
+
+ if (cmdline->print_version) {
+ printf("Python %s\n",
+ (cmdline->print_version >= 2) ? Py_GetVersion() : PY_VERSION);
+ return 1;
+ }
+
+ pymain->run_code = (cmdline->command != NULL || cmdline->filename != NULL
+ || cmdline->module != NULL);
+
+ return 0;
+}
+
+
+/* Initialize Py_Main().
+ This code must not use Python runtime apart PyMem_Raw memory allocator.
+
+ Return 0 on success.
+ Return 1 if Python is done and must exit.
+ Set pymain->err and return -1 on error. */
+static int
+pymain_init_impl(_PyMain *pymain)
+{
+ _PyCoreConfig *core_config = &pymain->core_config;
+ core_config->_disable_importlib = 0;
+
+ orig_argc = pymain->argc; /* For Py_GetArgcArgv() */
+ orig_argv = pymain->argv;
+
+ /* Parse the command line */
+ int res = pymain_init_cmdline(pymain);
+ if (res < 0) {
+ return -1;
+ }
+ if (res > 0) {
+ return 1;
+ }
+
+ pymain_set_global_config(pymain);
+ pymain_init_stdio(pymain);
+
+ /* Get environment variables */
+ if (pymain_warnings_envvar(pymain) < 0) {
+ return -1;
+ }
+ if (pymain_get_program_name(pymain) < 0) {
+ return -1;
}
+ core_config->allocator = Py_GETENV("PYTHONMALLOC");
+
+ return 0;
+}
+
+
+static int
+pymain_init(_PyMain *pymain)
+{
+ pymain->err = _PyRuntime_Initialize();
+ if (_Py_INIT_FAILED(pymain->err)) {
+ return -1;
+ }
+
+ /* Make sure that all memory allocated in pymain_init() is allocated
+ by malloc() */
+ PyMemAllocatorEx old_alloc, raw_alloc;
+ PyMem_GetAllocator(PYMEM_DOMAIN_RAW, &old_alloc);
+ _PyMem_GetDefaultRawAllocator(&raw_alloc);
+ PyMem_SetAllocator(PYMEM_DOMAIN_RAW, &raw_alloc);
+
+ int res = pymain_init_impl(pymain);
+
+ /* Restore the old memory allocator */
+ PyMem_SetAllocator(PYMEM_DOMAIN_RAW, &old_alloc);
+
+ return res;
+}
+
+static int
+pymain_core(_PyMain *pymain)
+{
+ _Py_CommandLineDetails *cmdline = &pymain->cmdline;
+
+ pymain->err = _Py_InitializeCore(&pymain->core_config);
+ if (_Py_INIT_FAILED(pymain->err)) {
+ return -1;
+ }
+
+ if (pymain_configure_pyruntime(pymain)) {
+ return -1;
+ }
+
+ if (pymain_init_main_interpreter(pymain)) {
+ return -1;
+ }
+
+ pymain_header(pymain);
+ pymain_import_readline(pymain);
+
+ pymain_init_argv(pymain);
+
+ if (cmdline->command) {
+ pymain->status = pymain_run_command(cmdline->command, &pymain->cf);
+ }
+ else if (cmdline->module) {
+ pymain->status = (pymain_run_module(cmdline->module, 1) != 0);
+ }
+ else {
+ pymain_run(pymain);
+ }
+ pymain_repl(pymain);
if (Py_FinalizeEx() < 0) {
/* Value unlikely to be confused with a non-error exit status or
other special meaning */
- sts = 120;
+ pymain->status = 120;
}
-#ifdef __INSURE__
- /* Insure++ is a memory analysis tool that aids in discovering
- * memory leaks and other memory problems. On Python exit, the
- * interned string dictionaries are flagged as being in use at exit
- * (which it is). Under normal circumstances, this is fine because
- * the memory will be automatically reclaimed by the system. Under
- * memory debugging, it's a huge source of useless noise, so we
- * trade off slower shutdown for less distraction in the memory
- * reports. -baw
- */
- _Py_ReleaseInternedUnicodeStrings();
-#endif /* __INSURE__ */
+ return 0;
+}
+
+
+int
+Py_Main(int argc, wchar_t **argv)
+{
+ _PyMain pymain = _PyMain_INIT;
+ memset(&pymain.cmdline, 0, sizeof(pymain.cmdline));
+ pymain.argc = argc;
+ pymain.argv = argv;
+
+ int res = pymain_init(&pymain);
+ if (res < 0) {
+ _Py_FatalInitError(pymain.err);
+ }
+ if (res == 0) {
+ res = pymain_core(&pymain);
+ if (res < 0) {
+ _Py_FatalInitError(pymain.err);
+ }
+ }
+
+ pymain_free(&pymain);
- return sts;
+ return pymain.status;
}
/* this is gonna seem *real weird*, but if you put some other code between