1# encoding: utf-8
2"""
3An embedded IPython shell.
4"""
5# Copyright (c) IPython Development Team.
6# Distributed under the terms of the Modified BSD License.
7
8
9import sys
10import warnings
11
12from IPython.core import ultratb, compilerop
13from IPython.core import magic_arguments
14from IPython.core.magic import Magics, magics_class, line_magic
15from IPython.core.interactiveshell import InteractiveShell, make_main_module_type
16from IPython.terminal.interactiveshell import TerminalInteractiveShell
17from IPython.terminal.ipapp import load_default_config
18
19from traitlets import Bool, CBool, Unicode
20from IPython.utils.io import ask_yes_no
21
22from typing import Set
23
24class KillEmbedded(Exception):pass
25
26# kept for backward compatibility as IPython 6 was released with
27# the typo. See https://github.com/ipython/ipython/pull/10706
28KillEmbeded = KillEmbedded
29
30# This is an additional magic that is exposed in embedded shells.
31@magics_class
32class EmbeddedMagics(Magics):
33
34 @line_magic
35 @magic_arguments.magic_arguments()
36 @magic_arguments.argument('-i', '--instance', action='store_true',
37 help='Kill instance instead of call location')
38 @magic_arguments.argument('-x', '--exit', action='store_true',
39 help='Also exit the current session')
40 @magic_arguments.argument('-y', '--yes', action='store_true',
41 help='Do not ask confirmation')
42 def kill_embedded(self, parameter_s=''):
43 """%kill_embedded : deactivate for good the current embedded IPython
44
45 This function (after asking for confirmation) sets an internal flag so
46 that an embedded IPython will never activate again for the given call
47 location. This is useful to permanently disable a shell that is being
48 called inside a loop: once you've figured out what you needed from it,
49 you may then kill it and the program will then continue to run without
50 the interactive shell interfering again.
51
52 Kill Instance Option:
53
54 If for some reasons you need to kill the location where the instance
55 is created and not called, for example if you create a single
56 instance in one place and debug in many locations, you can use the
57 ``--instance`` option to kill this specific instance. Like for the
58 ``call location`` killing an "instance" should work even if it is
59 recreated within a loop.
60
61 .. note::
62
63 This was the default behavior before IPython 5.2
64
65 """
66
67 args = magic_arguments.parse_argstring(self.kill_embedded, parameter_s)
68 print(args)
69 if args.instance:
70 # let no ask
71 if not args.yes:
72 kill = ask_yes_no(
73 "Are you sure you want to kill this embedded instance? [y/N] ", 'n')
74 else:
75 kill = True
76 if kill:
77 self.shell._disable_init_location()
78 print("This embedded IPython instance will not reactivate anymore "
79 "once you exit.")
80 else:
81 if not args.yes:
82 kill = ask_yes_no(
83 "Are you sure you want to kill this embedded call_location? [y/N] ", 'n')
84 else:
85 kill = True
86 if kill:
87 self.shell.embedded_active = False
88 print("This embedded IPython call location will not reactivate anymore "
89 "once you exit.")
90
91 if args.exit:
92 # Ask-exit does not really ask, it just set internals flags to exit
93 # on next loop.
94 self.shell.ask_exit()
95
96
97 @line_magic
98 def exit_raise(self, parameter_s=''):
99 """%exit_raise Make the current embedded kernel exit and raise and exception.
100
101 This function sets an internal flag so that an embedded IPython will
102 raise a `IPython.terminal.embed.KillEmbedded` Exception on exit, and then exit the current I. This is
103 useful to permanently exit a loop that create IPython embed instance.
104 """
105
106 self.shell.should_raise = True
107 self.shell.ask_exit()
108
109
110class _Sentinel:
111 def __init__(self, repr):
112 assert isinstance(repr, str)
113 self.repr = repr
114
115 def __repr__(self):
116 return repr
117
118
119class InteractiveShellEmbed(TerminalInteractiveShell):
120
121 dummy_mode = Bool(False)
122 exit_msg = Unicode('')
123 embedded = CBool(True)
124 should_raise = CBool(False)
125 # Like the base class display_banner is not configurable, but here it
126 # is True by default.
127 display_banner = CBool(True)
128 exit_msg = Unicode()
129
130 # When embedding, by default we don't change the terminal title
131 term_title = Bool(False,
132 help="Automatically set the terminal title"
133 ).tag(config=True)
134
135 _inactive_locations: Set[str] = set()
136
137 def _disable_init_location(self):
138 """Disable the current Instance creation location"""
139 InteractiveShellEmbed._inactive_locations.add(self._init_location_id)
140
141 @property
142 def embedded_active(self):
143 return (self._call_location_id not in InteractiveShellEmbed._inactive_locations)\
144 and (self._init_location_id not in InteractiveShellEmbed._inactive_locations)
145
146 @embedded_active.setter
147 def embedded_active(self, value):
148 if value:
149 InteractiveShellEmbed._inactive_locations.discard(
150 self._call_location_id)
151 InteractiveShellEmbed._inactive_locations.discard(
152 self._init_location_id)
153 else:
154 InteractiveShellEmbed._inactive_locations.add(
155 self._call_location_id)
156
157 def __init__(self, **kw):
158 assert (
159 "user_global_ns" not in kw
160 ), "Key word argument `user_global_ns` has been replaced by `user_module` since IPython 4.0."
161 # temporary fix for https://github.com/ipython/ipython/issues/14164
162 cls = type(self)
163 if cls._instance is None:
164 for subclass in cls._walk_mro():
165 subclass._instance = self
166 cls._instance = self
167
168 clid = kw.pop('_init_location_id', None)
169 if not clid:
170 frame = sys._getframe(1)
171 clid = '%s:%s' % (frame.f_code.co_filename, frame.f_lineno)
172 self._init_location_id = clid
173
174 super(InteractiveShellEmbed,self).__init__(**kw)
175
176 # don't use the ipython crash handler so that user exceptions aren't
177 # trapped
178 sys.excepthook = ultratb.FormattedTB(
179 theme_name=self.colors,
180 mode=self.xmode,
181 call_pdb=self.pdb,
182 )
183
184 def init_sys_modules(self):
185 """
186 Explicitly overwrite :mod:`IPython.core.interactiveshell` to do nothing.
187 """
188 pass
189
190 def init_magics(self):
191 super(InteractiveShellEmbed, self).init_magics()
192 self.register_magics(EmbeddedMagics)
193
194 def __call__(
195 self,
196 header="",
197 local_ns=None,
198 module=None,
199 dummy=None,
200 stack_depth=1,
201 compile_flags=None,
202 **kw,
203 ):
204 """Activate the interactive interpreter.
205
206 __call__(self,header='',local_ns=None,module=None,dummy=None) -> Start
207 the interpreter shell with the given local and global namespaces, and
208 optionally print a header string at startup.
209
210 The shell can be globally activated/deactivated using the
211 dummy_mode attribute. This allows you to turn off a shell used
212 for debugging globally.
213
214 However, *each* time you call the shell you can override the current
215 state of dummy_mode with the optional keyword parameter 'dummy'. For
216 example, if you set dummy mode on with IPShell.dummy_mode = True, you
217 can still have a specific call work by making it as IPShell(dummy=False).
218 """
219
220 # we are called, set the underlying interactiveshell not to exit.
221 self.keep_running = True
222
223 # If the user has turned it off, go away
224 clid = kw.pop('_call_location_id', None)
225 if not clid:
226 frame = sys._getframe(1)
227 clid = '%s:%s' % (frame.f_code.co_filename, frame.f_lineno)
228 self._call_location_id = clid
229
230 if not self.embedded_active:
231 return
232
233 # Normal exits from interactive mode set this flag, so the shell can't
234 # re-enter (it checks this variable at the start of interactive mode).
235 self.exit_now = False
236
237 # Allow the dummy parameter to override the global __dummy_mode
238 if dummy or (dummy != 0 and self.dummy_mode):
239 return
240
241 # self.banner is auto computed
242 if header:
243 self.old_banner2 = self.banner2
244 self.banner2 = self.banner2 + '\n' + header + '\n'
245 else:
246 self.old_banner2 = ''
247
248 if self.display_banner:
249 self.show_banner()
250
251 # Call the embedding code with a stack depth of 1 so it can skip over
252 # our call and get the original caller's namespaces.
253 self.mainloop(
254 local_ns, module, stack_depth=stack_depth, compile_flags=compile_flags
255 )
256
257 self.banner2 = self.old_banner2
258
259 if self.exit_msg is not None:
260 print(self.exit_msg)
261
262 if self.should_raise:
263 raise KillEmbedded('Embedded IPython raising error, as user requested.')
264
265 def mainloop(
266 self,
267 local_ns=None,
268 module=None,
269 stack_depth=0,
270 compile_flags=None,
271 ):
272 """Embeds IPython into a running python program.
273
274 Parameters
275 ----------
276 local_ns, module
277 Working local namespace (a dict) and module (a module or similar
278 object). If given as None, they are automatically taken from the scope
279 where the shell was called, so that program variables become visible.
280 stack_depth : int
281 How many levels in the stack to go to looking for namespaces (when
282 local_ns or module is None). This allows an intermediate caller to
283 make sure that this function gets the namespace from the intended
284 level in the stack. By default (0) it will get its locals and globals
285 from the immediate caller.
286 compile_flags
287 A bit field identifying the __future__ features
288 that are enabled, as passed to the builtin :func:`compile` function.
289 If given as None, they are automatically taken from the scope where
290 the shell was called.
291
292 """
293
294 # Get locals and globals from caller
295 if ((local_ns is None or module is None or compile_flags is None)
296 and self.default_user_namespaces):
297 call_frame = sys._getframe(stack_depth).f_back
298
299 if local_ns is None:
300 local_ns = call_frame.f_locals
301 if module is None:
302 global_ns = call_frame.f_globals
303 try:
304 module = sys.modules[global_ns['__name__']]
305 except KeyError:
306 warnings.warn("Failed to get module %s" % \
307 global_ns.get('__name__', 'unknown module')
308 )
309 module = make_main_module_type(global_ns)()
310 if compile_flags is None:
311 compile_flags = (call_frame.f_code.co_flags &
312 compilerop.PyCF_MASK)
313
314 # Save original namespace and module so we can restore them after
315 # embedding; otherwise the shell doesn't shut down correctly.
316 orig_user_module = self.user_module
317 orig_user_ns = self.user_ns
318 orig_compile_flags = self.compile.flags
319
320 # Update namespaces and fire up interpreter
321
322 # The global one is easy, we can just throw it in
323 if module is not None:
324 self.user_module = module
325
326 # But the user/local one is tricky: ipython needs it to store internal
327 # data, but we also need the locals. We'll throw our hidden variables
328 # like _ih and get_ipython() into the local namespace, but delete them
329 # later.
330 if local_ns is not None:
331 reentrant_local_ns = {k: v for (k, v) in local_ns.items() if k not in self.user_ns_hidden.keys()}
332 self.user_ns = reentrant_local_ns
333 self.init_user_ns()
334
335 # Compiler flags
336 if compile_flags is not None:
337 self.compile.flags = compile_flags
338
339 # make sure the tab-completer has the correct frame information, so it
340 # actually completes using the frame's locals/globals
341 self.set_completer_frame()
342
343 with self.builtin_trap, self.display_trap:
344 self.interact()
345
346 # now, purge out the local namespace of IPython's hidden variables.
347 if local_ns is not None:
348 local_ns.update({k: v for (k, v) in self.user_ns.items() if k not in self.user_ns_hidden.keys()})
349
350
351 # Restore original namespace so shell can shut down when we exit.
352 self.user_module = orig_user_module
353 self.user_ns = orig_user_ns
354 self.compile.flags = orig_compile_flags
355
356
357def embed(*, header="", compile_flags=None, **kwargs):
358 """Call this to embed IPython at the current point in your program.
359
360 The first invocation of this will create a :class:`terminal.embed.InteractiveShellEmbed`
361 instance and then call it. Consecutive calls just call the already
362 created instance.
363
364 If you don't want the kernel to initialize the namespace
365 from the scope of the surrounding function,
366 and/or you want to load full IPython configuration,
367 you probably want `IPython.start_ipython()` instead.
368
369 Here is a simple example::
370
371 from IPython import embed
372 a = 10
373 b = 20
374 embed(header='First time')
375 c = 30
376 d = 40
377 embed()
378
379 Parameters
380 ----------
381
382 header : str
383 Optional header string to print at startup.
384 compile_flags
385 Passed to the `compile_flags` parameter of :py:meth:`terminal.embed.InteractiveShellEmbed.mainloop()`,
386 which is called when the :class:`terminal.embed.InteractiveShellEmbed` instance is called.
387 **kwargs : various, optional
388 Any other kwargs will be passed to the :class:`terminal.embed.InteractiveShellEmbed` constructor.
389 Full customization can be done by passing a traitlets :class:`Config` in as the
390 `config` argument (see :ref:`configure_start_ipython` and :ref:`terminal_options`).
391 """
392 config = kwargs.get('config')
393 if config is None:
394 config = load_default_config()
395 config.InteractiveShellEmbed = config.TerminalInteractiveShell
396 kwargs["config"] = config
397 using = kwargs.get("using", "sync")
398 colors = kwargs.pop("colors", "nocolor")
399 if using:
400 kwargs["config"].update(
401 {
402 "TerminalInteractiveShell": {
403 "loop_runner": using,
404 "colors": colors,
405 "autoawait": using != "sync",
406 }
407 }
408 )
409 # save ps1/ps2 if defined
410 ps1 = None
411 ps2 = None
412 try:
413 ps1 = sys.ps1
414 ps2 = sys.ps2
415 except AttributeError:
416 pass
417 #save previous instance
418 saved_shell_instance = InteractiveShell._instance
419 if saved_shell_instance is not None:
420 cls = type(saved_shell_instance)
421 cls.clear_instance()
422 frame = sys._getframe(1)
423 shell = InteractiveShellEmbed.instance(_init_location_id='%s:%s' % (
424 frame.f_code.co_filename, frame.f_lineno), **kwargs)
425 shell(header=header, stack_depth=2, compile_flags=compile_flags,
426 _call_location_id='%s:%s' % (frame.f_code.co_filename, frame.f_lineno))
427 InteractiveShellEmbed.clear_instance()
428 #restore previous instance
429 if saved_shell_instance is not None:
430 cls = type(saved_shell_instance)
431 cls.clear_instance()
432 for subclass in cls._walk_mro():
433 subclass._instance = saved_shell_instance
434 if ps1 is not None:
435 sys.ps1 = ps1
436 sys.ps2 = ps2