Close Python IDLE shell without prompt

2020-02-13 04:51发布

I am working on a script (script A), that needs to open a new Python IDLE shell, automatically run another script (script B) in it and then close it. The following code is what I use for this purpose:

import sys
sys.argv=['','-n','-t','My New Shell','-c','execfile("VarLoader.py")']
import idlelib.PyShell
idlelib.PyShell.main()

However I can't get the new shell close automatically. I have tried adding the following to script B but either it doesn't close the new shell or a windows pops up asking whether I want to kill it.

exit()

.

import sys
sys.exit()

1条回答
▲ chillily
2楼-- · 2020-02-13 05:03

Instead of monkeypatching or modifying the IDLE source code to make your program skip the prompt to exit I'd recommend you create a subclass of PyShell that overrides the close method how you want it to work:

import idlelib.PyShell
class PyShell_NoExitPrompt(idlelib.PyShell.PyShell):
    def close(self):
        "Extend EditorWindow.close(), does not prompt to exit"
##        if self.executing:
##            response = tkMessageBox.askokcancel(
##                "Kill?",
##                "Your program is still running!\n Do you want to kill it?",
##                default="ok",
##                parent=self.text)
##            if response is False:
##                return "cancel"
        self.stop_readline()
        self.canceled = True
        self.closing = True
        return idlelib.PyShell.EditorWindow.close(self)

The original issue with this was that then using idlelib.PyShell.main would not use your subclass, you can in fact create a copy of the function - without modifying the original - by using the FunctionType constructor that will use your modified class:

import functools
from types import FunctionType

def copy_function(f, namespace_override):
    """creates a copy of a function (code, signature, defaults) with a modified global scope"""
    namespace = dict(f.__globals__)
    namespace.update(namespace_override)
    new_f = FunctionType(f.__code__, namespace, f.__name__, f.__defaults__, f.__closure__)
    return functools.update_wrapper(f, new_f)

Then you can run your extra IDLE shell like this:

import sys
#there is also a way to prevent the need to override sys.argv but that isn't as concerning to me.
sys.argv = ['','-n','-t','My New Shell','-c','execfile("VarLoader.py")']
hacked_main = copy_function(idlelib.PyShell.main,
                            {"PyShell":PyShell_NoExitPrompt})

hacked_main()

Now you can leave IDLE the way it is and have your program work the way you want it too. (it is also compatible with other versions of python!)

查看更多
登录 后发表回答