Closed patrick-kidger closed 5 months ago
Okay, I've figured this one out.
The issue is that builtins.{exit,quit}
actually explicitly calls sys.stdin.close()
:
import dis
dis.dis(exit.__call__)
19 0 RESUME 0
22 2 NOP
23 4 LOAD_GLOBAL 0 (sys)
16 LOAD_ATTR 1 (stdin)
26 LOAD_METHOD 2 (close)
48 PRECALL 0
52 CALL 0
62 POP_TOP
64 JUMP_FORWARD 7 (to 80)
>> 66 PUSH_EXC_INFO
24 68 POP_TOP
25 70 POP_EXCEPT
72 JUMP_FORWARD 3 (to 80)
>> 74 COPY 3
76 POP_EXCEPT
78 RERAISE 1
26 >> 80 LOAD_GLOBAL 7 (NULL + SystemExit)
92 LOAD_FAST 1 (code)
94 PRECALL 1
98 CALL 1
108 RAISE_VARARGS 1
which seems a bit strange! (See also this related cpython discussion.)
This means it's actually possible to reproduce this issue in default python
!
try:
exit()
except SystemExit:
pass
input()
The solution is to call embed
with monkey-patched exit
and quit
methods:
globals = dict(globals())
globals["exit"] = sys.exit
globals["quit"] = sys.exit
ptpython.repl.embed(globals, ...)
(Note that sys.exit
is different from builtins.exit
.)
Consider the following MWE:
Here I am (a) using
ptpython
from the command line (as I really like it!) and (b) happen to calling.embed
as part of my program; it has atry
/except
to recognise when the inner interpreter has terminated, to pass control back to the original invocation.However, this produces an infinite loop of:
Another way of framing this might be that
ptpython
does not evaluate programs in the same way as normalpython
: the above is a program with different behaviour.