this repo has no description
1// Copyright (c) Facebook, Inc. and its affiliates. (http://www.facebook.com)
2#include <getopt.h>
3
4#include <cerrno>
5#include <cstdio>
6#include <cstdlib>
7#include <cwchar>
8#include <memory>
9
10#include "cpython-data.h"
11#include "cpython-func.h"
12
13#include "builtins-module.h"
14#include "exception-builtins.h"
15#include "marshal.h"
16#include "os.h"
17#include "runtime.h"
18#include "thread.h"
19#include "vector.h"
20#include "version.h"
21#include "view.h"
22
23namespace py {
24
25extern Vector<const char*> warn_options;
26
27static const char* const kInteractiveHelp =
28 R"(Type "help", "copyright", "credits" or "license" for more information.)";
29
30static const char* const kSupportedOpts = "+bBc:dEthiIm:OqsSuvVW:xX:";
31static const struct option kSupportedLongOpts[] = {
32 {"help", no_argument, nullptr, 'h'},
33 {"version", no_argument, nullptr, 'V'},
34 {nullptr, 0, nullptr, 0}};
35
36static void failArgConversion(const char* message, int argi) {
37 std::fprintf(stderr, "Fatal python error: %s #%i\n", message, argi);
38 std::abort();
39}
40
41static void decodeArgv(int argc, const char* const* argv, wchar_t** wargv) {
42 for (int i = 0; i < argc; i++) {
43 wargv[i] = Py_DecodeLocale(argv[i], nullptr);
44 if (wargv[i] == nullptr) {
45 failArgConversion("unable to decode the command line argument", i + 1);
46 }
47 }
48}
49
50static void encodeWargv(int argc, const wchar_t* const* wargv, char** argv) {
51 // TODO(T57811636): Support UTF-8 arguments on macOS.
52 // We don't have UTF-8 decoding machinery that is decoupled from the
53 // runtime. That's why we can't use Py_EncodeLocale() here.
54 for (int i = 0; i < argc; i++) {
55 const wchar_t* wc_str = wargv[i];
56 size_t size = std::wcslen(wc_str);
57 char* c_str = static_cast<char*>(PyMem_Malloc((size + 1) * sizeof(char)));
58 for (size_t p = 0; p < size; p++) {
59 wchar_t ch = wc_str[p];
60 if (ch & 0x80) {
61 UNIMPLEMENTED("UTF-8 argument support unimplemented");
62 }
63 c_str[p] = static_cast<char>(ch);
64 }
65 c_str[size] = '\0';
66 argv[i] = c_str;
67 }
68}
69
70static void runInteractiveHook() {
71 Thread* thread = Thread::current();
72 RawObject result = thread->invokeFunction0(ID(sys), ID(__interactivehook__));
73 if (result.isErrorException()) {
74 std::fprintf(stderr, "Failed calling sys.__interactivehook__\n");
75 printPendingExceptionWithSysLastVars(thread);
76 thread->clearPendingException();
77 }
78}
79
80static int runModule(const char* modname_cstr, bool set_argv0) {
81 Thread* thread = Thread::current();
82 Runtime* runtime = thread->runtime();
83 HandleScope scope(thread);
84
85 Str runpy(&scope, runtime->symbols()->at(ID(runpy)));
86 Object result(&scope,
87 thread->invokeFunction1(ID(builtins), ID(__import__), runpy));
88 if (result.isError()) {
89 std::fprintf(stderr, "Could not import runpy module\n");
90 printPendingException(thread);
91 return -1;
92 }
93
94 Str modname(&scope, runtime->newStrFromCStr(modname_cstr));
95 Bool alter_argv(&scope, Bool::fromBool(set_argv0));
96 result = thread->invokeFunction2(ID(runpy), ID(_run_module_as_main), modname,
97 alter_argv);
98 if (result.isError()) {
99 printPendingException(thread);
100 return -1;
101 }
102 return 0;
103}
104
105static int tryRunPackage(Thread* thread, const char* path_cst) {
106 Runtime* runtime = thread->runtime();
107 HandleScope scope(thread);
108 Str path(&scope, runtime->newStrFromCStr(path_cst));
109 Object result(&scope, thread->invokeFunction1(ID(builtins),
110 ID(_try_run_package), path));
111 if (result.isErrorException()) {
112 PyErr_Print();
113 return -1;
114 }
115 // path was not a package.
116 if (result.isNoneType()) return 0;
117 // package executed fine.
118 return 1;
119}
120
121static void runStartupFile(PyCompilerFlags* cf) {
122 const char* startupfile = std::getenv("PYTHONSTARTUP");
123 if (startupfile == nullptr || startupfile[0] == '\0') return;
124 FILE* fp = std::fopen(startupfile, "r");
125 if (fp != nullptr) {
126 PyRun_SimpleFileExFlags(fp, startupfile, 0, cf);
127 std::fclose(fp);
128 } else {
129 int saved_errno = errno;
130 PySys_WriteStderr("Could not open PYTHONSTARTUP\n");
131 errno = saved_errno;
132 PyErr_SetFromErrnoWithFilename(PyExc_IOError, startupfile);
133 PyErr_Print();
134 }
135 PyErr_Clear();
136}
137
138PY_EXPORT int Py_BytesMain(int argc, char** argv) {
139 int print_version = 0;
140 bool print_help = false;
141 const char* command = nullptr;
142 const char* module = nullptr;
143
144 optind = 1;
145
146 DCHECK(warn_options.empty(), "warn options should be empty");
147 int option;
148 while ((option = getopt_long(argc, argv, kSupportedOpts, kSupportedLongOpts,
149 nullptr)) != -1) {
150 // -c and -m mark the end of interpreter options - all further
151 // arguments are passed to the script
152 if (option == 'c') {
153 command = optarg;
154 break;
155 }
156 if (option == 'm') {
157 module = optarg;
158 break;
159 }
160
161 wchar_t* woptarg;
162 switch (option) {
163 case 'b':
164 Py_BytesWarningFlag++;
165 break;
166 case 'd':
167 Py_DebugFlag++;
168 break;
169 case 'i':
170 Py_InspectFlag++;
171 Py_InteractiveFlag++;
172 break;
173 case 'I':
174 Py_IsolatedFlag++;
175 Py_NoUserSiteDirectory++;
176 Py_IgnoreEnvironmentFlag++;
177 break;
178 case 'O':
179 Py_OptimizeFlag++;
180 break;
181 case 'B':
182 Py_DontWriteBytecodeFlag++;
183 break;
184 case 's':
185 Py_NoUserSiteDirectory++;
186 break;
187 case 'S':
188 Py_NoSiteFlag++;
189 break;
190 case 'E':
191 Py_IgnoreEnvironmentFlag++;
192 break;
193 case 't':
194 // ignored for backwards compatibility.
195 break;
196 case 'u':
197 Py_UnbufferedStdioFlag = 1;
198 break;
199 case 'v':
200 Py_VerboseFlag++;
201 break;
202 case 'x':
203 UNIMPLEMENTED("skip first line");
204 break;
205 case 'h':
206 case '?':
207 print_help = true;
208 break;
209 case 'V':
210 print_version++;
211 break;
212 case 'W':
213 warn_options.push_back(optarg);
214 break;
215 case 'X':
216 woptarg = Py_DecodeLocale(optarg, nullptr);
217 PySys_AddXOption(woptarg);
218 PyMem_RawFree(woptarg);
219 woptarg = nullptr;
220 break;
221 case 'q':
222 Py_QuietFlag++;
223 break;
224 default:
225 UNREACHABLE("Unexpected value returned from getopt_long()");
226 }
227 }
228
229 if (print_help) {
230 UNIMPLEMENTED("command line help");
231 }
232
233 if (print_version) {
234 std::printf("Python %s\n", print_version >= 2 ? Py_GetVersion() : kVersion);
235 return 0;
236 }
237
238 char* filename = nullptr;
239 if (command == nullptr && module == nullptr && optind < argc &&
240 std::strcmp(argv[optind], "-") != 0) {
241 filename = argv[optind];
242 }
243
244 bool is_interactive = Py_FdIsInteractive(stdin, nullptr);
245
246 wchar_t* prog_name = Py_DecodeLocale(argv[0], nullptr);
247 if (prog_name == nullptr) {
248 failArgConversion("unable to decode the program name", 0);
249 }
250 Py_SetProgramName(prog_name);
251 PyMem_RawFree(prog_name);
252
253 Py_Initialize();
254
255 if (!Py_QuietFlag &&
256 (Py_VerboseFlag || (command == nullptr && filename == nullptr &&
257 module == nullptr && is_interactive))) {
258 std::fprintf(stderr, "Python %s on %s\n", Py_GetVersion(),
259 Py_GetPlatform());
260 if (!Py_NoSiteFlag) {
261 std::fprintf(stderr, "%s\n", kInteractiveHelp);
262 }
263 }
264
265 int wargc;
266 wchar_t** wargv;
267 if (command != nullptr || module != nullptr) {
268 // Start arg list with "-c" or "-m" and omit command/module arg
269 wargc = argc - optind + 1;
270 const char** argv_copy =
271 static_cast<const char**>(PyMem_RawCalloc(wargc, sizeof(*argv_copy)));
272 argv_copy[0] = command != nullptr ? "-c" : "-m";
273 for (int i = optind; i < argc; i++) {
274 argv_copy[i - optind + 1] = argv[i];
275 }
276 wargv = static_cast<wchar_t**>(PyMem_RawCalloc(wargc, sizeof(*wargv)));
277 decodeArgv(wargc, argv_copy, wargv);
278 PyMem_RawFree(argv_copy);
279 } else {
280 wargc = argc - optind;
281 wargv = static_cast<wchar_t**>(PyMem_RawCalloc(wargc, sizeof(*wargv)));
282 decodeArgv(wargc, argv + optind, wargv);
283 }
284 PySys_SetArgv(wargc, wargv);
285 for (int i = 0; i < wargc; i++) {
286 PyMem_RawFree(wargv[i]);
287 }
288 PyMem_RawFree(wargv);
289
290 PyCompilerFlags flags = _PyCompilerFlags_INIT;
291
292 int returncode;
293 if (command != nullptr) {
294 returncode = PyRun_SimpleStringFlags(command, &flags) == 0 ? EXIT_SUCCESS
295 : EXIT_FAILURE;
296 } else if (module != nullptr) {
297 returncode = runModule(module, true) == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
298 } else if (filename != nullptr) {
299 Thread* thread = Thread::current();
300 int run_package_result = tryRunPackage(thread, filename);
301 if (run_package_result == 1) {
302 returncode = EXIT_SUCCESS;
303 } else if (run_package_result < 0) {
304 returncode = EXIT_FAILURE;
305 } else {
306 // `filename` was not a package, run as single file.
307 FILE* fp = std::fopen(filename, "r");
308 if (fp == nullptr) {
309 std::fprintf(stderr, "%s: can't open file '%s': [Errno %d] %s\n",
310 argv[0], filename, errno, std::strerror(errno));
311 return 2;
312 }
313 returncode =
314 PyRun_AnyFileExFlags(fp, filename, /*closeit=*/1, &flags) == 0
315 ? EXIT_SUCCESS
316 : EXIT_FAILURE;
317 }
318 } else {
319 // filename == nullptr: read input from stdin
320 if (is_interactive) {
321 Py_InspectFlag = 0; // do exit on SystemExit
322 runStartupFile(&flags);
323 runInteractiveHook();
324 }
325 returncode =
326 PyRun_AnyFileExFlags(stdin, "<stdin>", /*closeit=*/0, &flags) == 0
327 ? EXIT_SUCCESS
328 : EXIT_FAILURE;
329 }
330
331 if (Py_InspectFlag && is_interactive) {
332 Py_InspectFlag = 0;
333 runInteractiveHook();
334 returncode = PyRun_AnyFileExFlags(stdin, "<stdin>", 0, &flags) == 0
335 ? EXIT_SUCCESS
336 : EXIT_FAILURE;
337 }
338
339 Py_Finalize();
340
341 return returncode;
342}
343
344PY_EXPORT int Py_Main(int argc, wchar_t** wargv) {
345 std::fprintf(stderr,
346 "Py_Main(int, wchar_t**) is intended for Windows applications; "
347 "consider using Py_BytesMain(int, char**) on POSIX");
348 char** argv = static_cast<char**>(PyMem_RawCalloc(argc, sizeof(*argv)));
349 encodeWargv(argc, wargv, argv);
350 int res = Py_BytesMain(argc, argv);
351 for (int i = 0; i < argc; i++) {
352 PyMem_Free(argv[i]);
353 }
354 PyMem_RawFree(argv);
355 return res;
356}
357
358} // namespace py