Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/IPython/terminal/embed.py: 26%
178 statements
« prev ^ index » next coverage.py v7.4.4, created at 2024-04-20 06:09 +0000
« prev ^ index » next coverage.py v7.4.4, created at 2024-04-20 06:09 +0000
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.
9import sys
10import warnings
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 DummyMod, InteractiveShell
16from IPython.terminal.interactiveshell import TerminalInteractiveShell
17from IPython.terminal.ipapp import load_default_config
19from traitlets import Bool, CBool, Unicode
20from IPython.utils.io import ask_yes_no
22from typing import Set
24class KillEmbedded(Exception):pass
26# kept for backward compatibility as IPython 6 was released with
27# the typo. See https://github.com/ipython/ipython/pull/10706
28KillEmbeded = KillEmbedded
30# This is an additional magic that is exposed in embedded shells.
31@magics_class
32class EmbeddedMagics(Magics):
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
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.
52 Kill Instance Option:
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.
61 .. note::
63 This was the default behavior before IPython 5.2
65 """
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.")
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()
97 @line_magic
98 def exit_raise(self, parameter_s=''):
99 """%exit_raise Make the current embedded kernel exit and raise and exception.
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 """
106 self.shell.should_raise = True
107 self.shell.ask_exit()
110class _Sentinel:
111 def __init__(self, repr):
112 assert isinstance(repr, str)
113 self.repr = repr
115 def __repr__(self):
116 return repr
119class InteractiveShellEmbed(TerminalInteractiveShell):
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()
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)
135 _inactive_locations: Set[str] = set()
137 def _disable_init_location(self):
138 """Disable the current Instance creation location"""
139 InteractiveShellEmbed._inactive_locations.add(self._init_location_id)
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)
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)
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
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
174 super(InteractiveShellEmbed,self).__init__(**kw)
176 # don't use the ipython crash handler so that user exceptions aren't
177 # trapped
178 sys.excepthook = ultratb.FormattedTB(color_scheme=self.colors,
179 mode=self.xmode,
180 call_pdb=self.pdb)
182 def init_sys_modules(self):
183 """
184 Explicitly overwrite :mod:`IPython.core.interactiveshell` to do nothing.
185 """
186 pass
188 def init_magics(self):
189 super(InteractiveShellEmbed, self).init_magics()
190 self.register_magics(EmbeddedMagics)
192 def __call__(
193 self,
194 header="",
195 local_ns=None,
196 module=None,
197 dummy=None,
198 stack_depth=1,
199 compile_flags=None,
200 **kw
201 ):
202 """Activate the interactive interpreter.
204 __call__(self,header='',local_ns=None,module=None,dummy=None) -> Start
205 the interpreter shell with the given local and global namespaces, and
206 optionally print a header string at startup.
208 The shell can be globally activated/deactivated using the
209 dummy_mode attribute. This allows you to turn off a shell used
210 for debugging globally.
212 However, *each* time you call the shell you can override the current
213 state of dummy_mode with the optional keyword parameter 'dummy'. For
214 example, if you set dummy mode on with IPShell.dummy_mode = True, you
215 can still have a specific call work by making it as IPShell(dummy=False).
216 """
218 # we are called, set the underlying interactiveshell not to exit.
219 self.keep_running = True
221 # If the user has turned it off, go away
222 clid = kw.pop('_call_location_id', None)
223 if not clid:
224 frame = sys._getframe(1)
225 clid = '%s:%s' % (frame.f_code.co_filename, frame.f_lineno)
226 self._call_location_id = clid
228 if not self.embedded_active:
229 return
231 # Normal exits from interactive mode set this flag, so the shell can't
232 # re-enter (it checks this variable at the start of interactive mode).
233 self.exit_now = False
235 # Allow the dummy parameter to override the global __dummy_mode
236 if dummy or (dummy != 0 and self.dummy_mode):
237 return
239 # self.banner is auto computed
240 if header:
241 self.old_banner2 = self.banner2
242 self.banner2 = self.banner2 + '\n' + header + '\n'
243 else:
244 self.old_banner2 = ''
246 if self.display_banner:
247 self.show_banner()
249 # Call the embedding code with a stack depth of 1 so it can skip over
250 # our call and get the original caller's namespaces.
251 self.mainloop(
252 local_ns, module, stack_depth=stack_depth, compile_flags=compile_flags
253 )
255 self.banner2 = self.old_banner2
257 if self.exit_msg is not None:
258 print(self.exit_msg)
260 if self.should_raise:
261 raise KillEmbedded('Embedded IPython raising error, as user requested.')
263 def mainloop(
264 self,
265 local_ns=None,
266 module=None,
267 stack_depth=0,
268 compile_flags=None,
269 ):
270 """Embeds IPython into a running python program.
272 Parameters
273 ----------
274 local_ns, module
275 Working local namespace (a dict) and module (a module or similar
276 object). If given as None, they are automatically taken from the scope
277 where the shell was called, so that program variables become visible.
278 stack_depth : int
279 How many levels in the stack to go to looking for namespaces (when
280 local_ns or module is None). This allows an intermediate caller to
281 make sure that this function gets the namespace from the intended
282 level in the stack. By default (0) it will get its locals and globals
283 from the immediate caller.
284 compile_flags
285 A bit field identifying the __future__ features
286 that are enabled, as passed to the builtin :func:`compile` function.
287 If given as None, they are automatically taken from the scope where
288 the shell was called.
290 """
292 # Get locals and globals from caller
293 if ((local_ns is None or module is None or compile_flags is None)
294 and self.default_user_namespaces):
295 call_frame = sys._getframe(stack_depth).f_back
297 if local_ns is None:
298 local_ns = call_frame.f_locals
299 if module is None:
300 global_ns = call_frame.f_globals
301 try:
302 module = sys.modules[global_ns['__name__']]
303 except KeyError:
304 warnings.warn("Failed to get module %s" % \
305 global_ns.get('__name__', 'unknown module')
306 )
307 module = DummyMod()
308 module.__dict__ = global_ns
309 if compile_flags is None:
310 compile_flags = (call_frame.f_code.co_flags &
311 compilerop.PyCF_MASK)
313 # Save original namespace and module so we can restore them after
314 # embedding; otherwise the shell doesn't shut down correctly.
315 orig_user_module = self.user_module
316 orig_user_ns = self.user_ns
317 orig_compile_flags = self.compile.flags
319 # Update namespaces and fire up interpreter
321 # The global one is easy, we can just throw it in
322 if module is not None:
323 self.user_module = module
325 # But the user/local one is tricky: ipython needs it to store internal
326 # data, but we also need the locals. We'll throw our hidden variables
327 # like _ih and get_ipython() into the local namespace, but delete them
328 # later.
329 if local_ns is not None:
330 reentrant_local_ns = {k: v for (k, v) in local_ns.items() if k not in self.user_ns_hidden.keys()}
331 self.user_ns = reentrant_local_ns
332 self.init_user_ns()
334 # Compiler flags
335 if compile_flags is not None:
336 self.compile.flags = compile_flags
338 # make sure the tab-completer has the correct frame information, so it
339 # actually completes using the frame's locals/globals
340 self.set_completer_frame()
342 with self.builtin_trap, self.display_trap:
343 self.interact()
345 # now, purge out the local namespace of IPython's hidden variables.
346 if local_ns is not None:
347 local_ns.update({k: v for (k, v) in self.user_ns.items() if k not in self.user_ns_hidden.keys()})
350 # Restore original namespace so shell can shut down when we exit.
351 self.user_module = orig_user_module
352 self.user_ns = orig_user_ns
353 self.compile.flags = orig_compile_flags
356def embed(*, header="", compile_flags=None, **kwargs):
357 """Call this to embed IPython at the current point in your program.
359 The first invocation of this will create a :class:`terminal.embed.InteractiveShellEmbed`
360 instance and then call it. Consecutive calls just call the already
361 created instance.
363 If you don't want the kernel to initialize the namespace
364 from the scope of the surrounding function,
365 and/or you want to load full IPython configuration,
366 you probably want `IPython.start_ipython()` instead.
368 Here is a simple example::
370 from IPython import embed
371 a = 10
372 b = 20
373 embed(header='First time')
374 c = 30
375 d = 40
376 embed()
378 Parameters
379 ----------
381 header : str
382 Optional header string to print at startup.
383 compile_flags
384 Passed to the `compile_flags` parameter of :py:meth:`terminal.embed.InteractiveShellEmbed.mainloop()`,
385 which is called when the :class:`terminal.embed.InteractiveShellEmbed` instance is called.
386 **kwargs : various, optional
387 Any other kwargs will be passed to the :class:`terminal.embed.InteractiveShellEmbed` constructor.
388 Full customization can be done by passing a traitlets :class:`Config` in as the
389 `config` argument (see :ref:`configure_start_ipython` and :ref:`terminal_options`).
390 """
391 config = kwargs.get('config')
392 if config is None:
393 config = load_default_config()
394 config.InteractiveShellEmbed = config.TerminalInteractiveShell
395 kwargs['config'] = config
396 using = kwargs.get('using', 'sync')
397 if using :
398 kwargs['config'].update({'TerminalInteractiveShell':{'loop_runner':using, 'colors':'NoColor', 'autoawait': using!='sync'}})
399 #save ps1/ps2 if defined
400 ps1 = None
401 ps2 = None
402 try:
403 ps1 = sys.ps1
404 ps2 = sys.ps2
405 except AttributeError:
406 pass
407 #save previous instance
408 saved_shell_instance = InteractiveShell._instance
409 if saved_shell_instance is not None:
410 cls = type(saved_shell_instance)
411 cls.clear_instance()
412 frame = sys._getframe(1)
413 shell = InteractiveShellEmbed.instance(_init_location_id='%s:%s' % (
414 frame.f_code.co_filename, frame.f_lineno), **kwargs)
415 shell(header=header, stack_depth=2, compile_flags=compile_flags,
416 _call_location_id='%s:%s' % (frame.f_code.co_filename, frame.f_lineno))
417 InteractiveShellEmbed.clear_instance()
418 #restore previous instance
419 if saved_shell_instance is not None:
420 cls = type(saved_shell_instance)
421 cls.clear_instance()
422 for subclass in cls._walk_mro():
423 subclass._instance = saved_shell_instance
424 if ps1 is not None:
425 sys.ps1 = ps1
426 sys.ps2 = ps2