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