Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit b882a14

Browse files
committed
Fix deactivation of embedded instance.
While deactivation of full instances make some sens, the most common behavior user expect when creating an instance and using ``kill_embedded`` is deactivation of the current call location. Implement the expected on by default, add options to get previous behavior, and add flag for direct exit and no-confirm Fix #9761
1 parent 78ec96d commit b882a14

2 files changed

Lines changed: 112 additions & 26 deletions

File tree

IPython/terminal/embed.py

Lines changed: 95 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
import warnings
1111

1212
from IPython.core import ultratb, compilerop
13+
from IPython.core import magic_arguments
1314
from IPython.core.magic import Magics, magics_class, line_magic
1415
from IPython.core.interactiveshell import DummyMod, InteractiveShell
1516
from IPython.terminal.interactiveshell import TerminalInteractiveShell
@@ -25,22 +26,68 @@ class KillEmbeded(Exception):pass
2526
class EmbeddedMagics(Magics):
2627

2728
@line_magic
29+
@magic_arguments.magic_arguments()
30+
@magic_arguments.argument('-i', '--instance', action='store_true',
31+
help='Kill instance instead of call location')
32+
@magic_arguments.argument('-x', '--exit', action='store_true',
33+
help='Also exit the current session')
34+
@magic_arguments.argument('-y', '--yes', action='store_true',
35+
help='Do not ask confirmation')
2836
def kill_embedded(self, parameter_s=''):
29-
"""%kill_embedded : deactivate for good the current embedded IPython.
37+
"""%kill_embedded : deactivate for good the current embedded IPython
3038
3139
This function (after asking for confirmation) sets an internal flag so
32-
that an embedded IPython will never activate again. This is useful to
33-
permanently disable a shell that is being called inside a loop: once
34-
you've figured out what you needed from it, you may then kill it and
35-
the program will then continue to run without the interactive shell
36-
interfering again.
40+
that an embedded IPython will never activate again for the given call
41+
location. This is useful to permanently disable a shell that is being
42+
called inside a loop: once you've figured out what you needed from it,
43+
you may then kill it and the program will then continue to run without
44+
the interactive shell interfering again.
45+
46+
47+
Kill Instance Option
48+
--------------------
49+
50+
If for some reasons you need to kill the location where the instance is
51+
created and not called, for example if you create a single instance in
52+
one place and debug in many locations, you can use the ``--instance``
53+
option to kill this specific instance. Like for the ``call location``
54+
killing an "instance" should work even if it is recreated within a
55+
loop.
56+
57+
.. note::
58+
59+
This was the default behavior before IPython 5.2
60+
3761
"""
3862

39-
kill = ask_yes_no("Are you sure you want to kill this embedded instance? [y/N] ",'n')
40-
if kill:
41-
self.shell.embedded_active = False
42-
print ("This embedded IPython will not reactivate anymore "
43-
"once you exit.")
63+
args = magic_arguments.parse_argstring(self.kill_embedded, parameter_s)
64+
print(args)
65+
if args.instance:
66+
# let no ask
67+
if not args.yes:
68+
kill = ask_yes_no(
69+
"Are you sure you want to kill this embedded instance? [y/N] ", 'n')
70+
else:
71+
kill = True
72+
if kill:
73+
self.shell._disable_init_location()
74+
print("This embedded IPython instance will not reactivate anymore "
75+
"once you exit.")
76+
else:
77+
if not args.yes:
78+
kill = ask_yes_no(
79+
"Are you sure you want to kill this embedded call_location? [y/N] ", 'n')
80+
else:
81+
kill = True
82+
if kill:
83+
self.shell.embedded_active = False
84+
print("This embedded IPython call location will not reactivate anymore "
85+
"once you exit.")
86+
87+
if args.exit:
88+
# Ask-exit does not really ask, it just set internals flags to exit
89+
# on next loop.
90+
self.shell.ask_exit()
4491

4592

4693
@line_magic
@@ -77,44 +124,55 @@ class InteractiveShellEmbed(TerminalInteractiveShell):
77124

78125
@property
79126
def embedded_active(self):
80-
return self._call_location_id not in InteractiveShellEmbed._inactive_locations
127+
return (self._call_location_id not in InteractiveShellEmbed._inactive_locations)\
128+
and (self._init_location_id not in InteractiveShellEmbed._inactive_locations)
129+
130+
def _disable_init_location(self):
131+
"""Disable the current Instance creation location"""
132+
InteractiveShellEmbed._inactive_locations.add(self._init_location_id)
81133

82134
@embedded_active.setter
83135
def embedded_active(self, value):
84-
if value :
85-
if self._call_location_id in InteractiveShellEmbed._inactive_locations:
86-
InteractiveShellEmbed._inactive_locations.remove(self._call_location_id)
136+
if value:
137+
InteractiveShellEmbed._inactive_locations.discard(
138+
self._call_location_id)
139+
InteractiveShellEmbed._inactive_locations.discard(
140+
self._init_location_id)
87141
else:
88-
InteractiveShellEmbed._inactive_locations.add(self._call_location_id)
142+
InteractiveShellEmbed._inactive_locations.add(
143+
self._call_location_id)
89144

90145
def __init__(self, **kw):
91-
92-
93146
if kw.get('user_global_ns', None) is not None:
94-
raise DeprecationWarning("Key word argument `user_global_ns` has been replaced by `user_module` since IPython 4.0.")
147+
raise DeprecationWarning(
148+
"Key word argument `user_global_ns` has been replaced by `user_module` since IPython 4.0.")
95149

96-
self._call_location_id = kw.pop('_call_location_id', None)
150+
clid = kw.pop('_init_location_id', None)
151+
if not clid:
152+
frame = sys._getframe(1)
153+
clid = '%s:%s' % (frame.f_code.co_filename, frame.f_lineno)
154+
self._init_location_id = clid
97155

98156
super(InteractiveShellEmbed,self).__init__(**kw)
99157

100-
if not self._call_location_id:
101-
frame = sys._getframe(1)
102-
self._call_location_id = '%s:%s' % (frame.f_code.co_filename, frame.f_lineno)
103158
# don't use the ipython crash handler so that user exceptions aren't
104159
# trapped
105160
sys.excepthook = ultratb.FormattedTB(color_scheme=self.colors,
106161
mode=self.xmode,
107162
call_pdb=self.pdb)
108163

109164
def init_sys_modules(self):
165+
"""
166+
Explicitly overwrite :any:`IPython.core.interactiveshell` to do nothing.
167+
"""
110168
pass
111169

112170
def init_magics(self):
113171
super(InteractiveShellEmbed, self).init_magics()
114172
self.register_magics(EmbeddedMagics)
115173

116174
def __call__(self, header='', local_ns=None, module=None, dummy=None,
117-
stack_depth=1, global_ns=None, compile_flags=None):
175+
stack_depth=1, global_ns=None, compile_flags=None, **kw):
118176
"""Activate the interactive interpreter.
119177
120178
__call__(self,header='',local_ns=None,module=None,dummy=None) -> Start
@@ -131,7 +189,16 @@ def __call__(self, header='', local_ns=None, module=None, dummy=None,
131189
can still have a specific call work by making it as IPShell(dummy=False).
132190
"""
133191

192+
# we are called, set the underlying interactiveshell not to exit.
193+
self.keep_running = True
194+
134195
# If the user has turned it off, go away
196+
clid = kw.pop('_call_location_id', None)
197+
if not clid:
198+
frame = sys._getframe(1)
199+
clid = '%s:%s' % (frame.f_code.co_filename, frame.f_lineno)
200+
self._call_location_id = clid
201+
135202
if not self.embedded_active:
136203
return
137204

@@ -310,8 +377,10 @@ def embed(**kwargs):
310377
cls = type(saved_shell_instance)
311378
cls.clear_instance()
312379
frame = sys._getframe(1)
313-
shell = InteractiveShellEmbed.instance(_call_location_id='%s:%s' % (frame.f_code.co_filename, frame.f_lineno), **kwargs)
314-
shell(header=header, stack_depth=2, compile_flags=compile_flags)
380+
shell = InteractiveShellEmbed.instance(_init_location_id='%s:%s' % (
381+
frame.f_code.co_filename, frame.f_lineno), **kwargs)
382+
shell(header=header, stack_depth=2, compile_flags=compile_flags,
383+
_call_location_id='%s:%s' % (frame.f_code.co_filename, frame.f_lineno))
315384
InteractiveShellEmbed.clear_instance()
316385
#restore previous instance
317386
if saved_shell_instance is not None:

docs/source/whatsnew/version5.rst

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,23 @@ Remarkable changes and fixes:
3131
``limit_to_all`` option of the completer. :ghpull:`10198`
3232

3333

34+
Changes of behavior to :any:`InteractiveShellEmbed`.
35+
36+
:any:`InteractiveShellEmbed` interactive behavior have changed a bit in between
37+
5.1 and 5.2. By default ``%kill_embedded`` magic will prevent further invocation
38+
of the current ``call location`` instead of preventing further invocation of
39+
the current instance creation location. For most use case this will not change
40+
much for you, though previous behavior was confusing and less consistent with
41+
previous IPython versions.
42+
43+
You can now deactivate instances by using ``%kill_embedded --instance`` flag,
44+
(or ``-i`` in short). The ``%kill_embedded`` magic also gained a
45+
``--yes``/``-y`` option which skip confirmation step, and ``-x``/``--exit``
46+
which also exit the current embedded call without asking for confirmation.
47+
48+
See :ghpull:`10207`.
49+
50+
3451

3552
IPython 5.1
3653
===========

0 commit comments

Comments
 (0)