this repo has no description
1// Copyright (c) Facebook, Inc. and its affiliates. (http://www.facebook.com)
2#include <cstdlib>
3#include <cstring>
4
5#include "Python.h"
6#include "gmock/gmock-matchers.h"
7#include "gtest/gtest.h"
8
9#include "capi-fixture.h"
10#include "capi-testing.h"
11
12namespace py {
13namespace testing {
14
15using PythonrunExtensionApiTest = ExtensionApi;
16
17TEST_F(PythonrunExtensionApiTest, CompileStringWithEmptyStrReturnsCode) {
18 PyObjectPtr result(Py_CompileString("", "<string>", Py_file_input));
19 EXPECT_NE(result, nullptr);
20 EXPECT_EQ(PyErr_Occurred(), nullptr);
21 EXPECT_TRUE(PyCode_Check(result));
22}
23
24TEST_F(PythonrunExtensionApiTest, CompileStringCompilesCode) {
25 PyObjectPtr result(Py_CompileString("a = 3", "<string>", Py_file_input));
26 EXPECT_NE(result, nullptr);
27 EXPECT_EQ(PyErr_Occurred(), nullptr);
28 EXPECT_TRUE(PyCode_Check(result));
29 PyObjectPtr locals(PyDict_New());
30 PyObjectPtr globals(PyDict_New());
31 EXPECT_NE(PyEval_EvalCode(result, globals, locals), nullptr);
32
33 EXPECT_EQ(PyDict_Size(locals), 1);
34 PyObjectPtr local(PyDict_GetItemString(locals, "a"));
35 EXPECT_TRUE(isLongEqualsLong(local, 3));
36}
37
38TEST_F(PythonrunExtensionApiTest,
39 CompileStringWithInvalidCodeRaisesSyntaxError) {
40 PyObjectPtr result(Py_CompileString(";", "<string>", Py_file_input));
41 EXPECT_EQ(result, nullptr);
42 ASSERT_NE(PyErr_Occurred(), nullptr);
43 EXPECT_EQ(PyErr_ExceptionMatches(PyExc_SyntaxError), 1);
44}
45
46TEST_F(PythonrunExtensionApiTest, CompileWithSourceIsUTF8RaisesValueError) {
47 int flags = PyCF_SOURCE_IS_UTF8;
48 EXPECT_EQ(0, moduleSet("__main__", "flags", PyLong_FromLong(flags)));
49 EXPECT_EQ(-1, PyRun_SimpleString(R"(
50try:
51 compile("1", "filename", "exec", flags=flags)
52 failed = False
53except ValueError:
54 failed = True
55 raise
56)"));
57 PyObjectPtr failed(mainModuleGet("failed"));
58 EXPECT_EQ(Py_True, failed);
59}
60
61TEST_F(PythonrunExtensionApiTest, PyRunAnyFileReturnsZero) {
62 CaptureStdStreams streams;
63 const char* buffer = R"(print(f"good morning by {__file__}"))";
64 FILE* fp = ::fmemopen(reinterpret_cast<void*>(const_cast<char*>(buffer)),
65 std::strlen(buffer), "r");
66 int returncode = PyRun_AnyFile(fp, "test string");
67 fclose(fp);
68 EXPECT_EQ(returncode, 0);
69 EXPECT_EQ(streams.out(), "good morning by test string\n");
70}
71
72TEST_F(PythonrunExtensionApiTest, PyRunAnyFileExReturnsZero) {
73 CaptureStdStreams streams;
74 const char* buffer = R"(print(f"I am {__file__}"))";
75 FILE* fp = ::fmemopen(reinterpret_cast<void*>(const_cast<char*>(buffer)),
76 std::strlen(buffer), "r");
77 int returncode = PyRun_AnyFileEx(fp, "test string", /*closeit=*/1);
78 EXPECT_EQ(returncode, 0);
79 EXPECT_EQ(streams.out(), "I am test string\n");
80}
81
82TEST_F(PythonrunExtensionApiTest, PyRunAnyFileExFlagsReturnsZero) {
83 CaptureStdStreams streams;
84 const char* buffer = R"(x = 2 <> 3; print(f"{x} by {__file__}"))";
85 FILE* fp = ::fmemopen(reinterpret_cast<void*>(const_cast<char*>(buffer)),
86 std::strlen(buffer), "r");
87 PyCompilerFlags flags = _PyCompilerFlags_INIT;
88 flags.cf_flags = CO_FUTURE_BARRY_AS_BDFL;
89 int returncode = PyRun_AnyFileExFlags(fp, nullptr, /*closeit=*/1, &flags);
90 EXPECT_EQ(returncode, 0);
91 EXPECT_EQ(streams.out(), "True by ???\n");
92}
93
94TEST_F(PythonrunExtensionApiTest, PyRunAnyFileFlagsReturnsZero) {
95 CaptureStdStreams streams;
96 const char* buffer = R"(x = 4 <> 4; print(f"{x} by {__file__}"))";
97 FILE* fp = ::fmemopen(reinterpret_cast<void*>(const_cast<char*>(buffer)),
98 std::strlen(buffer), "r");
99 PyCompilerFlags flags = _PyCompilerFlags_INIT;
100 flags.cf_flags = CO_FUTURE_BARRY_AS_BDFL;
101 int returncode = PyRun_AnyFileFlags(fp, "a test", &flags);
102 fclose(fp);
103 EXPECT_EQ(returncode, 0);
104 EXPECT_EQ(streams.out(), "False by a test\n");
105}
106
107TEST_F(PythonrunExtensionApiTest, PyRunFileReturnsStr) {
108 PyObjectPtr module(PyModule_New("testmodule"));
109 PyModule_AddStringConstant(module, "shout", "ya!");
110 PyObject* module_dict = PyModule_GetDict(module);
111 const char* buffer = R"("hey " + shout)";
112 FILE* fp = ::fmemopen(reinterpret_cast<void*>(const_cast<char*>(buffer)),
113 std::strlen(buffer), "r");
114 PyObjectPtr result(
115 PyRun_File(fp, "a test", Py_eval_input, module_dict, module_dict));
116 fclose(fp);
117 EXPECT_TRUE(isUnicodeEqualsCStr(result, "hey ya!"));
118}
119
120TEST_F(PythonrunExtensionApiTest, PyRunFileExReturnsStr) {
121 PyObjectPtr module(PyModule_New("testmodule"));
122 PyModule_AddStringConstant(module, "shout", "ya!");
123 PyObject* module_dict = PyModule_GetDict(module);
124 const char* buffer = R"("hey " + shout)";
125 FILE* fp = ::fmemopen(reinterpret_cast<void*>(const_cast<char*>(buffer)),
126 std::strlen(buffer), "r");
127 PyObjectPtr result(PyRun_FileEx(fp, "a test", Py_eval_input, module_dict,
128 module_dict, /*closeit=*/1));
129 EXPECT_TRUE(isUnicodeEqualsCStr(result, "hey ya!"));
130}
131
132TEST_F(PythonrunExtensionApiTest, PyRunFileExFlagsReturnsTrue) {
133 PyObjectPtr module(PyModule_New("testmodule"));
134 PyModule_AddIntConstant(module, "number", 42);
135 PyObjectPtr module_dict(PyModule_GetDict(module));
136 Py_INCREF(module_dict);
137 char buffer[] = R"(7 != number)";
138 FILE* fp =
139 ::fmemopen(reinterpret_cast<void*>(buffer), std::strlen(buffer), "r");
140 PyCompilerFlags flags = _PyCompilerFlags_INIT;
141 PyObjectPtr result(PyRun_FileExFlags(fp, "a test", Py_eval_input,
142 module_dict.get(), module_dict.get(),
143 /*closeit=*/1, &flags));
144 EXPECT_EQ(result, Py_True);
145}
146
147TEST_F(PythonrunExtensionApiTest, PyRunFileExFlagsWithUserLocalsReturnsTrue) {
148 PyObjectPtr module(PyModule_New("testmodule"));
149 PyModule_AddIntConstant(module, "number", 42);
150 PyObjectPtr module_dict(PyModule_GetDict(module));
151 Py_INCREF(module_dict);
152 char buffer[] = R"(7 != number)";
153 FILE* fp =
154 ::fmemopen(reinterpret_cast<void*>(buffer), std::strlen(buffer), "r");
155 PyCompilerFlags flags = _PyCompilerFlags_INIT;
156 PyObjectPtr locals(PyDict_New());
157 PyObjectPtr result(PyRun_FileExFlags(fp, "a test", Py_eval_input,
158 module_dict.get(), locals.get(),
159 /*closeit=*/1, &flags));
160 EXPECT_EQ(result, Py_True);
161}
162
163TEST_F(PythonrunExtensionApiTest, PyRunFileFlagsReturnsFalse) {
164 PyObjectPtr module(PyModule_New("testmodule"));
165 PyModule_AddIntConstant(module, "number", 9);
166 PyObject* module_dict = PyModule_GetDict(module);
167 const char* buffer = R"(number <> 9)";
168 FILE* fp = ::fmemopen(reinterpret_cast<void*>(const_cast<char*>(buffer)),
169 std::strlen(buffer), "r");
170 PyCompilerFlags flags = _PyCompilerFlags_INIT;
171 flags.cf_flags = CO_FUTURE_BARRY_AS_BDFL;
172 PyObjectPtr result(PyRun_FileFlags(fp, "a test", Py_eval_input, module_dict,
173 module_dict, &flags));
174 fclose(fp);
175 EXPECT_EQ(result, Py_False);
176}
177
178TEST_F(PythonrunExtensionApiTest, PyRunSimpleStringReturnsInt) {
179 EXPECT_EQ(PyRun_SimpleString("a = 42"), 0);
180 EXPECT_EQ(PyErr_Occurred(), nullptr);
181 PyObjectPtr value(mainModuleGet("a"));
182 EXPECT_TRUE(isLongEqualsLong(value, 42));
183}
184
185TEST_F(PythonrunExtensionApiTest, PyRunSimpleStringPrintsSyntaxError) {
186 CaptureStdStreams streams;
187 EXPECT_EQ(PyRun_SimpleString(",,,"), -1);
188 EXPECT_EQ(PyErr_Occurred(), nullptr);
189 ASSERT_NE(streams.err().find("SyntaxError: invalid syntax\n"),
190 std::string::npos);
191}
192
193TEST_F(PythonrunExtensionApiTest, PyRunSimpleStringPrintsUncaughtException) {
194 CaptureStdStreams streams;
195 ASSERT_EQ(PyRun_SimpleString("raise RuntimeError('boom')"), -1);
196 EXPECT_EQ(streams.out(), "");
197 EXPECT_EQ(streams.err(), R"(Traceback (most recent call last):
198 File "<string>", line 1, in <module>
199RuntimeError: boom
200)");
201}
202
203TEST_F(PythonrunExtensionApiTest, PyRunSimpleStringFlagsReturnsTrue) {
204 PyCompilerFlags flags = _PyCompilerFlags_INIT;
205 flags.cf_flags = CO_FUTURE_BARRY_AS_BDFL;
206 EXPECT_EQ(PyRun_SimpleStringFlags("foo = 13 <> 42", &flags), 0);
207 EXPECT_EQ(PyErr_Occurred(), nullptr);
208 PyObjectPtr value(mainModuleGet("foo"));
209 EXPECT_EQ(value, Py_True);
210}
211
212TEST_F(PythonrunExtensionApiTest, PyRunStringReturnsString) {
213 PyObjectPtr module(PyModule_New("testmodule"));
214 PyModule_AddStringConstant(module, "name", "tester");
215 PyObject* module_dict = PyModule_GetDict(module);
216 PyObjectPtr result(PyRun_String(R"(f"hello {name}")", Py_eval_input,
217 module_dict, module_dict));
218 EXPECT_TRUE(isUnicodeEqualsCStr(result, "hello tester"));
219}
220
221TEST_F(PythonrunExtensionApiTest, PyRunStringFlagsReturnsResult) {
222 PyObject* module = PyImport_AddModule("__main__");
223 ASSERT_NE(module, nullptr);
224 PyObject* module_proxy = PyModule_GetDict(module);
225 PyCompilerFlags flags = _PyCompilerFlags_INIT;
226 flags.cf_flags = CO_FUTURE_BARRY_AS_BDFL;
227 EXPECT_TRUE(
228 isLongEqualsLong(PyRun_StringFlags("(7 <> 7) + 3", Py_eval_input,
229 module_proxy, module_proxy, &flags),
230 3));
231}
232
233TEST_F(PythonrunExtensionApiTest,
234 PyRunStringFlagsWithSourceIsUtf8FlagReturnsResult) {
235 PyObject* module = PyImport_AddModule("__main__");
236 ASSERT_NE(module, nullptr);
237 PyObject* module_proxy = PyModule_GetDict(module);
238 PyCompilerFlags flags = _PyCompilerFlags_INIT;
239 flags.cf_flags = PyCF_SOURCE_IS_UTF8;
240 EXPECT_TRUE(
241 isLongEqualsLong(PyRun_StringFlags("1 + 2", Py_eval_input, module_proxy,
242 module_proxy, &flags),
243 3));
244}
245TEST_F(PythonrunExtensionApiTest, PyErrDisplayPrintsException) {
246 PyErr_SetString(PyExc_RuntimeError, "oopsie");
247 PyObject *exc, *value, *tb;
248 PyErr_Fetch(&exc, &value, &tb);
249 // PyErr_Display() expects a real exception in `value`.
250 PyErr_NormalizeException(&exc, &value, &tb);
251
252 CaptureStdStreams streams;
253 PyErr_Display(exc, value, tb);
254 ASSERT_EQ(streams.err(), "RuntimeError: oopsie\n");
255 EXPECT_EQ(streams.out(), "");
256
257 Py_DECREF(exc);
258 Py_DECREF(value);
259 Py_XDECREF(tb);
260}
261
262TEST_F(PythonrunExtensionApiTest, PyErrDisplayPrintsExceptionChain) {
263 // TODO(T77279778): Don't clear __traceback__ below.
264 ASSERT_EQ(PyRun_SimpleString(R"(
265try:
266 try:
267 raise RuntimeError("inner")
268 except Exception as e:
269 e.__traceback__ = None
270 e.__context__ = ValueError("non-raised inner")
271 raise RuntimeError("outer") from e
272except Exception as e:
273 e.__traceback__ = None
274 exc = e
275)"),
276 0);
277 PyObjectPtr exc(mainModuleGet("exc"));
278
279 CaptureStdStreams streams;
280 PyErr_Display(nullptr, exc, nullptr);
281 ASSERT_EQ(streams.err(),
282 "ValueError: non-raised inner\n\nDuring handling of the above "
283 "exception, another exception occurred:\n\nRuntimeError: "
284 "inner\n\nThe above exception was the direct cause "
285 "of the following exception:\n\nRuntimeError: outer\n");
286 EXPECT_EQ(streams.out(), "");
287}
288
289TEST_F(PythonrunExtensionApiTest, PyErrDisplayAvoidsCauseCycle) {
290 ASSERT_EQ(PyRun_SimpleString(R"(
291exc = RuntimeError("outer")
292exc.__cause__ = RuntimeError("inner")
293exc.__cause__.__cause__ = exc
294)"),
295 0);
296 PyObjectPtr exc(mainModuleGet("exc"));
297
298 CaptureStdStreams streams;
299 PyErr_Display(nullptr, exc, nullptr);
300 ASSERT_EQ(streams.err(),
301 "RuntimeError: inner\n\nThe above exception was the direct cause "
302 "of the following exception:\n\nRuntimeError: outer\n");
303 EXPECT_EQ(streams.out(), "");
304}
305
306TEST_F(PythonrunExtensionApiTest, PyErrDisplayAvoidsContextCycle) {
307 ASSERT_EQ(PyRun_SimpleString(R"(
308exc = RuntimeError("outer")
309exc.__context__ = RuntimeError("inner")
310exc.__context__.__context__ = exc
311)"),
312 0);
313 PyObjectPtr exc(mainModuleGet("exc"));
314
315 CaptureStdStreams streams;
316 PyErr_Display(nullptr, exc, nullptr);
317 ASSERT_EQ(streams.err(),
318 "RuntimeError: inner\n\nDuring handling of the above exception, "
319 "another exception occurred:\n\nRuntimeError: outer\n");
320 EXPECT_EQ(streams.out(), "");
321}
322
323TEST_F(PythonrunExtensionApiTest,
324 PyErrDisplayWithSuppressContextDoesntPrintContext) {
325 ASSERT_EQ(PyRun_SimpleString(R"(
326exc = RuntimeError("error")
327exc.__context__ = RuntimeError("inner error")
328exc.__suppress_context__ = True
329)"),
330 0);
331 PyObjectPtr exc(mainModuleGet("exc"));
332
333 CaptureStdStreams streams;
334 PyErr_Display(nullptr, exc, nullptr);
335 ASSERT_EQ(streams.err(), "RuntimeError: error\n");
336 EXPECT_EQ(streams.out(), "");
337}
338
339TEST_F(PythonrunExtensionApiTest, PyErrDisplayWithRaisingStr) {
340 ASSERT_EQ(PyRun_SimpleString(R"(
341class MyExc(Exception):
342 def __str__(self):
343 raise RuntimeError()
344exc = MyExc()
345)"),
346 0);
347 PyObjectPtr exc(mainModuleGet("exc"));
348
349 CaptureStdStreams streams;
350 PyErr_Display(nullptr, exc, nullptr);
351 ASSERT_EQ(streams.err(), "__main__.MyExc: <exception str() failed>\n");
352 EXPECT_EQ(streams.out(), "");
353}
354
355TEST_F(PythonrunExtensionApiTest, PyErrDisplayWithNoModule) {
356 ASSERT_EQ(PyRun_SimpleString(R"(
357class MyExc(Exception):
358 __module__ = None
359exc = MyExc("hi")
360)"),
361 0);
362 PyObjectPtr exc(mainModuleGet("exc"));
363
364 CaptureStdStreams streams;
365 PyErr_Display(nullptr, exc, nullptr);
366 ASSERT_EQ(streams.err(), "<unknown>MyExc: hi\n");
367 EXPECT_EQ(streams.out(), "");
368}
369
370TEST_F(PythonrunExtensionApiTest, PyErrDisplayWithNonException) {
371 PyObjectPtr value(PyFloat_FromDouble(123.0));
372
373 CaptureStdStreams streams;
374 PyErr_Display(nullptr, value, nullptr);
375 ASSERT_EQ(streams.err(),
376 "TypeError: print_exception(): Exception expected for value, float "
377 "found\n");
378 EXPECT_EQ(streams.out(), "");
379}
380
381TEST_F(PythonrunExtensionApiTest, PyErrDisplayWithSyntaxError) {
382 ASSERT_EQ(PyRun_SimpleString(R"(
383se = SyntaxError()
384se.print_file_and_line = None
385se.msg = "bad syntax"
386se.filename = "some_file.py"
387se.lineno = 0
388se.offset = 31
389se.text = "this is fake source code\nthat is multiple lines long"
390)"),
391 0);
392 PyObjectPtr se(mainModuleGet("se"));
393
394 CaptureStdStreams streams;
395 PyErr_Display(nullptr, se, nullptr);
396 ASSERT_EQ(streams.err(),
397 R"( File "some_file.py", line 0
398 that is multiple lines long
399 ^
400SyntaxError: bad syntax
401)");
402 EXPECT_EQ(streams.out(), "");
403}
404
405TEST_F(PythonrunExtensionApiTest, PyErrPrintExPrintsExceptionDoesntSetVars) {
406 PyErr_SetString(PyExc_RuntimeError, "abcd");
407
408 CaptureStdStreams streams;
409 PyErr_PrintEx(0);
410 ASSERT_EQ(streams.err(), "RuntimeError: abcd\n");
411 EXPECT_EQ(streams.out(), "");
412
413 ASSERT_EQ(moduleGet("sys", "last_type"), nullptr);
414 PyErr_Clear();
415 ASSERT_EQ(moduleGet("sys", "last_value"), nullptr);
416 PyErr_Clear();
417 ASSERT_EQ(moduleGet("sys", "last_traceback"), nullptr);
418 PyErr_Clear();
419}
420
421static void checkSysVars() {
422 PyObjectPtr type(moduleGet("sys", "last_type"));
423 ASSERT_EQ(PyErr_Occurred(), nullptr);
424 ASSERT_NE(type, nullptr);
425 EXPECT_EQ(type, PyExc_RuntimeError);
426
427 PyObjectPtr value(moduleGet("sys", "last_value"));
428 ASSERT_EQ(PyErr_Occurred(), nullptr);
429 ASSERT_NE(value, nullptr);
430 EXPECT_EQ(PyErr_GivenExceptionMatches(value, PyExc_RuntimeError), 1);
431
432 PyObjectPtr tb(moduleGet("sys", "last_traceback"));
433 ASSERT_EQ(PyErr_Occurred(), nullptr);
434 ASSERT_NE(tb, nullptr);
435 EXPECT_EQ(tb, Py_None);
436}
437
438TEST_F(PythonrunExtensionApiTest, PyErrPrintExWithArgSetsSysVars) {
439 PyErr_SetString(PyExc_RuntimeError, "critical error");
440
441 CaptureStdStreams streams;
442 PyErr_PrintEx(1);
443 ASSERT_EQ(streams.err(), "RuntimeError: critical error\n");
444 EXPECT_EQ(streams.out(), "");
445
446 EXPECT_NO_FATAL_FAILURE(checkSysVars());
447}
448
449TEST_F(PythonrunExtensionApiTest, PyErrPrintSetsSysVars) {
450 PyErr_SetString(PyExc_RuntimeError, "I don't hate you");
451
452 CaptureStdStreams streams;
453 PyErr_Print();
454 ASSERT_EQ(streams.err(), "RuntimeError: I don't hate you\n");
455 EXPECT_EQ(streams.out(), "");
456
457 EXPECT_NO_FATAL_FAILURE(checkSysVars());
458}
459
460TEST_F(PythonrunExtensionApiTest, PyErrPrintExCallsCustomExcepthook) {
461 ASSERT_EQ(PyRun_SimpleString(R"(
462import sys
463def my_hook(type, value, tb):
464 print("What exception?", file=sys.stderr)
465 print("Everything is fine. Nothing is ruined.")
466sys.excepthook = my_hook
467)"),
468 0);
469 PyErr_SetString(PyExc_RuntimeError, "boop");
470
471 CaptureStdStreams streams;
472 PyErr_PrintEx(0);
473 ASSERT_EQ(streams.err(), "What exception?\n");
474 EXPECT_EQ(streams.out(), "Everything is fine. Nothing is ruined.\n");
475}
476
477TEST_F(PythonrunExtensionApiTest, PyErrPrintExWithRaisingExcepthook) {
478 ASSERT_EQ(PyRun_SimpleString(R"(
479import sys
480def my_hook(type, value, tb):
481 raise RuntimeError("I'd rather not")
482sys.excepthook = my_hook
483)"),
484 0);
485 PyErr_SetString(PyExc_TypeError, "bad type");
486
487 CaptureStdStreams streams;
488 PyErr_PrintEx(0);
489 ASSERT_EQ(streams.err(), R"(Error in sys.excepthook:
490Traceback (most recent call last):
491 File "<string>", line 4, in my_hook
492RuntimeError: I'd rather not
493
494Original exception was:
495TypeError: bad type
496)");
497 EXPECT_EQ(streams.out(), "");
498}
499
500TEST_F(PythonrunExtensionApiTest, PyErrPrintExWithNoExceptHookPrintsException) {
501 ASSERT_EQ(PyRun_SimpleString("import sys; del sys.excepthook"), 0);
502 PyErr_SetString(PyExc_RuntimeError, "something broke");
503
504 CaptureStdStreams streams;
505 PyErr_PrintEx(0);
506 ASSERT_EQ(streams.err(),
507 "sys.excepthook is missing\nRuntimeError: something broke\n");
508 EXPECT_EQ(streams.out(), "");
509}
510
511TEST_F(PythonrunExtensionApiTest, PyErrPrintWithSystemExitExits) {
512 PyObject* zero = PyLong_FromLong(0);
513 PyErr_SetObject(PyExc_SystemExit, zero);
514 Py_DECREF(zero);
515 EXPECT_EXIT(PyErr_Print(), ::testing::ExitedWithCode(0), "^$");
516
517 PyErr_Clear();
518 PyObject* three = PyLong_FromLong(3);
519 PyErr_SetObject(PyExc_SystemExit, three);
520 Py_DECREF(three);
521 EXPECT_EXIT(PyErr_Print(), ::testing::ExitedWithCode(3), "^$");
522}
523
524TEST_F(PythonrunExtensionApiTest, PyErrPrintWithSystemExitFromExcepthookExits) {
525 ASSERT_EQ(PyRun_SimpleString(R"(
526import sys
527def my_hook(type, value, tb):
528 raise SystemExit(123)
529sys.excepthook = my_hook
530)"),
531 0);
532 PyErr_SetObject(PyExc_RuntimeError, Py_None);
533 EXPECT_EXIT(PyErr_Print(), ::testing::ExitedWithCode(123), "^$");
534}
535
536TEST_F(PythonrunExtensionApiTest, PyRunSimpleFileReturnsZero) {
537 CaptureStdStreams streams;
538 const char* buffer = R"(print(f"Greetings from {__file__}"))";
539 FILE* fp = ::fmemopen(reinterpret_cast<void*>(const_cast<char*>(buffer)),
540 std::strlen(buffer), "r");
541 int returncode = PyRun_SimpleFile(fp, "test string");
542 fclose(fp);
543 EXPECT_EQ(returncode, 0);
544 EXPECT_EQ(streams.out(), "Greetings from test string\n");
545}
546
547TEST_F(PythonrunExtensionApiTest, PyRunSimpleFileExReturnsZero) {
548 CaptureStdStreams streams;
549 const char* buffer = R"(print(f"This is {__file__}"))";
550 FILE* fp = ::fmemopen(reinterpret_cast<void*>(const_cast<char*>(buffer)),
551 std::strlen(buffer), "r");
552 int returncode = PyRun_SimpleFileEx(fp, "zombocom", /*closeit=*/1);
553 EXPECT_EQ(returncode, 0);
554 EXPECT_EQ(streams.out(), "This is zombocom\n");
555}
556
557TEST_F(PythonrunExtensionApiTest, PyRunSimpleFileExFlagsWithPyFileReturnsZero) {
558 CaptureStdStreams streams;
559 const char* buffer = R"(print("pyhello"))";
560 FILE* fp = ::fmemopen(reinterpret_cast<void*>(const_cast<char*>(buffer)),
561 std::strlen(buffer), "r");
562 PyCompilerFlags flags = _PyCompilerFlags_INIT;
563 int returncode = PyRun_SimpleFileExFlags(fp, "test.py", 1, &flags);
564 EXPECT_EQ(returncode, 0);
565 EXPECT_EQ(streams.out(), "pyhello\n");
566}
567
568TEST_F(PythonrunExtensionApiTest,
569 PyRunSimpleFileExFlagsSetsAndUnsetsDunderFile) {
570 CaptureStdStreams streams;
571 const char* buffer = R"(print(__file__))";
572 FILE* fp = ::fmemopen(reinterpret_cast<void*>(const_cast<char*>(buffer)),
573 std::strlen(buffer), "r");
574 PyCompilerFlags flags = _PyCompilerFlags_INIT;
575 int returncode = PyRun_SimpleFileExFlags(fp, "test.py", 1, &flags);
576 EXPECT_EQ(returncode, 0);
577 EXPECT_EQ(streams.out(), "test.py\n");
578 PyObject* mods = PyImport_GetModuleDict();
579 PyObject* dunder_main = PyUnicode_FromString("__main__");
580 PyObject* main_mod = PyDict_GetItem(mods, dunder_main);
581 PyObject* dunder_file = PyUnicode_FromString("__file__");
582 EXPECT_FALSE(PyObject_HasAttr(main_mod, dunder_file));
583}
584
585TEST_F(PythonrunExtensionApiTest, PyRunSimpleFileExFlagsPrintsSyntaxError) {
586 CaptureStdStreams streams;
587 const char* buffer = ",,,";
588 FILE* fp = ::fmemopen(reinterpret_cast<void*>(const_cast<char*>(buffer)),
589 std::strlen(buffer), "r");
590 PyCompilerFlags flags = _PyCompilerFlags_INIT;
591 int returncode = PyRun_SimpleFileExFlags(fp, "test.py", 1, &flags);
592 EXPECT_EQ(returncode, -1);
593 EXPECT_EQ(PyErr_Occurred(), nullptr);
594 EXPECT_NE(streams.err().find(R"( File "test.py", line 1
595 ,,,
596 ^
597SyntaxError: invalid syntax
598)"),
599 std::string::npos);
600}
601
602TEST_F(PythonrunExtensionApiTest,
603 PyRunSimpleFileExFlagsPrintsUncaughtException) {
604 CaptureStdStreams streams;
605 const char* buffer = "raise RuntimeError('boom')";
606 FILE* fp = ::fmemopen(reinterpret_cast<void*>(const_cast<char*>(buffer)),
607 std::strlen(buffer), "r");
608 PyCompilerFlags flags = _PyCompilerFlags_INIT;
609 int returncode = PyRun_SimpleFileExFlags(fp, "test.py", 1, &flags);
610 EXPECT_EQ(returncode, -1);
611 EXPECT_EQ(streams.out(), "");
612 EXPECT_EQ(streams.err(), R"(Traceback (most recent call last):
613 File "test.py", line 1, in <module>
614RuntimeError: boom
615)");
616}
617
618} // namespace testing
619} // namespace py