-
-
Notifications
You must be signed in to change notification settings - Fork 31.9k
gh-102895 Add an option local_exit in code.interact to block exit() from terminating the whole process #102896
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
gaogaotiantian
commented
Mar 22, 2023
•
edited by bedevere-bot
Loading
edited by bedevere-bot
- Issue: code.interact() will exit the whole process when given exit() or quit() #102895
@gvanrossum, @brettcannon (as the ones who committed to |
Looks like a fine idea. I won’t be able to review. Make sure to add tests. |
Test is already added to make sure |
Me, really?!? I can't even remember when I last touched the code, so I don't feel up for doing a code review. |
Hi, I know this file has not been actively maintained for a while, but the code change is pretty straightforward and it's backward compatible. It will be at least beneficial for |
@brandtbucher if you could take a look at this when you have some time :) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This isn't a full review, sorry.
Doc/library/code.rst
Outdated
the :meth:`InteractiveConsole.raw_input` method, if provided. If *local* is | ||
provided, it is passed to the :class:`InteractiveConsole` constructor for | ||
use as the default namespace for the interpreter loop. The :meth:`interact` | ||
method of the instance is then run with *banner* and *exitmsg* passed as the | ||
banner and exit message to use, if provided. The console object is discarded | ||
after use. | ||
the :meth:`InteractiveConsole.raw_input` method, if provided. If *local* | ||
or *block_exit* is provided, it is passed to the :class:`InteractiveConsole` | ||
constructor for use as the default namespace for the interpreter loop. | ||
The :meth:`interact` method of the instance is then run with *banner* and | ||
*exitmsg* passed as the banner and exit message to use, if provided. | ||
The console object is discarded after use. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We don't normally reflow the paragraph, it distracts the review. Just let the lines be uneven in length. :-)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah, I just thought the paragraph would be kind of ugly. Do you want me to convert this back to the uneven format? So only one line is modified?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes please! :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This still needs to be done.
Doc/library/code.rst
Outdated
@@ -23,27 +23,33 @@ build applications which provide an interactive interpreter prompt. | |||
``'__doc__'`` set to ``None``. | |||
|
|||
|
|||
.. class:: InteractiveConsole(locals=None, filename="<console>") | |||
.. class:: InteractiveConsole(locals=None, filename="<console>", block_exit=False) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you come up with a better name? To me, allow_exit=True
or even just exit=True
feels better.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So the difference between the option on and off is whether exit()
would terminate the whole process, or just the interactive console. By block_exit
I was trying to describe the option "blocks" the exit()
function from exiting the whole process. exit()
will always be allowed in the interactive console and it will always have effect. The only difference is to what level.
I would be happy to rename it to something better, but allow_exit
probably does not describe the behavior well.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, this name is tricky. Maybe one of the other reviewers has an idea? If not, we can go with block_exit.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah. Maybe ignore_exit=False
?
When I see block
here, it makes me think of a syntactic block (noun), or blocking (verb, as in "a blocking call"). It doesn't suggest "catch SystemExit" to me.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Well it's not "ignoring" exit()
, like I said above, exit()
has effect, just different when the option is toggled. How about something like local_exit
? Meaning we will only "exit" the interpreter.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah. Maybe
ignore_exit=False
?When I see
block
here, it makes me think of a syntactic block (noun), or blocking (verb, as in "a blocking call"). It doesn't suggest "catch SystemExit" to me.
Circling back to this as I still think this PR has some value. Let's figure out a name for it. Do you like use_local_exit
or local_exit
? Or maybe a vague description like safe_exit
? Or we can label its actual mechanism - exit_without_closing_stdin
- that's the key thing we need - the existing behavior also raises SystemExit
but stdin
is closed so we are basically done.
# process. exit and quit in builtins closes sys.stdin which makes | ||
# it super difficult to restore |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's interesting! I didn't know about this additional wrinkle... thanks for the comment.
Lib/code.py
Outdated
self.eof = 'Ctrl-Z plus Return' if sys.platform == "win32" else \ | ||
'Ctrl-D (i.e. EOF)' |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: I personally prefer an if-else to a ternary with a backslash. Also, site.py
uses os.pathsep
for this purpose... so let's maybe just be consistent with that?
self.eof = 'Ctrl-Z plus Return' if sys.platform == "win32" else \ | |
'Ctrl-D (i.e. EOF)' | |
if os.sep == '\\': | |
self.eof = "Ctrl-Z plus Return" | |
else: | |
self.eof = "Ctrl-D (i.e. EOF)" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm okay with the if/else block instead of an inline statement, but I kind of changed the OS check on purpose.
The fundamental thing we want to know here is what's the EOF on a specific system, and the best effort we can make is to get the OS type - if it's Windows then it's Ctrl+Z + Return. os.sep
is a design decision(finger print?) for Windows, the logic would be: os.sep == '\\'
-> "this is Windows!". The seperator itself has nothing to do with what the eof directly, it just happens to be linked together because of Windows.
For similar checks, there are a lot of usage of sys.platform == "win32"
in the standard library, and almost only site.py
uses os.sep
to figure out the OS platform. So I think sys.platform
is actually preferred in this case.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could the site.py code be a holdover from DOS, which isn’t Windows but still uses \ ? Presumably other antique OS’es using \ also used ^Z ? Not that it matters today. :-)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Too bad I'm not young enough to ask the question "what is DOS". That's a possibility, but the connection between \\
and Ctrl+Z is still indirect. I don't know if we can build modern Python on ancient OS anymore, always assume that Windows is the only outlier.
@@ -276,8 +315,20 @@ def raw_input(self, prompt=""): | |||
return input(prompt) | |||
|
|||
|
|||
class Quitter: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe just add a short docstring explaining that this is based on _sitebuiltins.Quitter
, and how it is different.
Perhaps it's even worth just subclassing Quitter
and overriding __call__
? Not sure. These are both pretty simple, but also pretty tightly coupled. Your call.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Subclassing Quitter
won't give a lot of benefit, because the OS checking is outside the original Quitter
class. We could calculate eof
outside of the __init__
function, it just seems like unnecessary. The class itself is already very compact and easy to understand.
Doc/library/code.rst
Outdated
@@ -23,27 +23,33 @@ build applications which provide an interactive interpreter prompt. | |||
``'__doc__'`` set to ``None``. | |||
|
|||
|
|||
.. class:: InteractiveConsole(locals=None, filename="<console>") | |||
.. class:: InteractiveConsole(locals=None, filename="<console>", block_exit=False) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah. Maybe ignore_exit=False
?
When I see block
here, it makes me think of a syntactic block (noun), or blocking (verb, as in "a blocking call"). It doesn't suggest "catch SystemExit" to me.
Doc/library/code.rst
Outdated
the :meth:`InteractiveConsole.raw_input` method, if provided. If *local* is | ||
provided, it is passed to the :class:`InteractiveConsole` constructor for | ||
use as the default namespace for the interpreter loop. The :meth:`interact` | ||
method of the instance is then run with *banner* and *exitmsg* passed as the | ||
banner and exit message to use, if provided. The console object is discarded | ||
after use. | ||
the :meth:`InteractiveConsole.raw_input` method, if provided. If *local* | ||
or *block_exit* is provided, it is passed to the :class:`InteractiveConsole` | ||
constructor for use as the default namespace for the interpreter loop. | ||
The :meth:`interact` method of the instance is then run with *banner* and | ||
*exitmsg* passed as the banner and exit message to use, if provided. | ||
The console object is discarded after use. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes please! :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks like several review comments didn't see action yet?
Regarding the name for the flag, was it already decided that we can't just always overwrite those two builtins? (Why are they closing stdin anyways? I don't recall; maybe you can find a clue in the code? It may be too long ago for the commit history to be useful.)
If we do need this feature, since the name has proved tricky, let's go with local_exit
. The name probably doesn't matter too much since the feature feels pretty obscure.
Doc/library/code.rst
Outdated
the :meth:`InteractiveConsole.raw_input` method, if provided. If *local* is | ||
provided, it is passed to the :class:`InteractiveConsole` constructor for | ||
use as the default namespace for the interpreter loop. The :meth:`interact` | ||
method of the instance is then run with *banner* and *exitmsg* passed as the | ||
banner and exit message to use, if provided. The console object is discarded | ||
after use. | ||
the :meth:`InteractiveConsole.raw_input` method, if provided. If *local* | ||
or *block_exit* is provided, it is passed to the :class:`InteractiveConsole` | ||
constructor for use as the default namespace for the interpreter loop. | ||
The :meth:`interact` method of the instance is then run with *banner* and | ||
*exitmsg* passed as the banner and exit message to use, if provided. | ||
The console object is discarded after use. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This still needs to be done.
Yes, I waited for the name confirmation as almost all files require that name. I updated all the files to use the name By overwriting those two builtins, what do you mean? Change the builtin classes? They close stdin because
|
@brandtbucher: Could you take over review here? I don't want this to languish for another 5 months (but neither is it urgent). |
Yep, I can. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The changes LGTM but I see Merging is blocked, hence suggest this is moved up to near the top.
Taking a note here that this would fix #85268 |
Alright, I think this has marinated for long enough. :) |
…it() from terminating the whole process (pythonGH-102896)
…it() from terminating the whole process (pythonGH-102896)