this repo has no description
at trunk 358 lines 10 kB view raw
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