| | |
| | """ |
| | An embedded IPython shell. |
| | """ |
| | |
| | |
| |
|
| |
|
| | import sys |
| | import warnings |
| |
|
| | from IPython.core import ultratb, compilerop |
| | from IPython.core import magic_arguments |
| | from IPython.core.magic import Magics, magics_class, line_magic |
| | from IPython.core.interactiveshell import DummyMod, InteractiveShell |
| | from IPython.terminal.interactiveshell import TerminalInteractiveShell |
| | from IPython.terminal.ipapp import load_default_config |
| |
|
| | from traitlets import Bool, CBool, Unicode |
| | from IPython.utils.io import ask_yes_no |
| |
|
| | from typing import Set |
| |
|
| | class KillEmbedded(Exception):pass |
| |
|
| | |
| | |
| | KillEmbeded = KillEmbedded |
| |
|
| | |
| | @magics_class |
| | class EmbeddedMagics(Magics): |
| |
|
| | @line_magic |
| | @magic_arguments.magic_arguments() |
| | @magic_arguments.argument('-i', '--instance', action='store_true', |
| | help='Kill instance instead of call location') |
| | @magic_arguments.argument('-x', '--exit', action='store_true', |
| | help='Also exit the current session') |
| | @magic_arguments.argument('-y', '--yes', action='store_true', |
| | help='Do not ask confirmation') |
| | def kill_embedded(self, parameter_s=''): |
| | """%kill_embedded : deactivate for good the current embedded IPython |
| | |
| | This function (after asking for confirmation) sets an internal flag so |
| | that an embedded IPython will never activate again for the given call |
| | location. This is useful to permanently disable a shell that is being |
| | called inside a loop: once you've figured out what you needed from it, |
| | you may then kill it and the program will then continue to run without |
| | the interactive shell interfering again. |
| | |
| | Kill Instance Option: |
| | |
| | If for some reasons you need to kill the location where the instance |
| | is created and not called, for example if you create a single |
| | instance in one place and debug in many locations, you can use the |
| | ``--instance`` option to kill this specific instance. Like for the |
| | ``call location`` killing an "instance" should work even if it is |
| | recreated within a loop. |
| | |
| | .. note:: |
| | |
| | This was the default behavior before IPython 5.2 |
| | |
| | """ |
| |
|
| | args = magic_arguments.parse_argstring(self.kill_embedded, parameter_s) |
| | print(args) |
| | if args.instance: |
| | |
| | if not args.yes: |
| | kill = ask_yes_no( |
| | "Are you sure you want to kill this embedded instance? [y/N] ", 'n') |
| | else: |
| | kill = True |
| | if kill: |
| | self.shell._disable_init_location() |
| | print("This embedded IPython instance will not reactivate anymore " |
| | "once you exit.") |
| | else: |
| | if not args.yes: |
| | kill = ask_yes_no( |
| | "Are you sure you want to kill this embedded call_location? [y/N] ", 'n') |
| | else: |
| | kill = True |
| | if kill: |
| | self.shell.embedded_active = False |
| | print("This embedded IPython call location will not reactivate anymore " |
| | "once you exit.") |
| |
|
| | if args.exit: |
| | |
| | |
| | self.shell.ask_exit() |
| |
|
| |
|
| | @line_magic |
| | def exit_raise(self, parameter_s=''): |
| | """%exit_raise Make the current embedded kernel exit and raise and exception. |
| | |
| | This function sets an internal flag so that an embedded IPython will |
| | raise a `IPython.terminal.embed.KillEmbedded` Exception on exit, and then exit the current I. This is |
| | useful to permanently exit a loop that create IPython embed instance. |
| | """ |
| |
|
| | self.shell.should_raise = True |
| | self.shell.ask_exit() |
| |
|
| |
|
| | class _Sentinel: |
| | def __init__(self, repr): |
| | assert isinstance(repr, str) |
| | self.repr = repr |
| |
|
| | def __repr__(self): |
| | return repr |
| |
|
| |
|
| | class InteractiveShellEmbed(TerminalInteractiveShell): |
| |
|
| | dummy_mode = Bool(False) |
| | exit_msg = Unicode('') |
| | embedded = CBool(True) |
| | should_raise = CBool(False) |
| | |
| | |
| | display_banner = CBool(True) |
| | exit_msg = Unicode() |
| |
|
| | |
| | term_title = Bool(False, |
| | help="Automatically set the terminal title" |
| | ).tag(config=True) |
| |
|
| | _inactive_locations: Set[str] = set() |
| |
|
| | def _disable_init_location(self): |
| | """Disable the current Instance creation location""" |
| | InteractiveShellEmbed._inactive_locations.add(self._init_location_id) |
| |
|
| | @property |
| | def embedded_active(self): |
| | return (self._call_location_id not in InteractiveShellEmbed._inactive_locations)\ |
| | and (self._init_location_id not in InteractiveShellEmbed._inactive_locations) |
| |
|
| | @embedded_active.setter |
| | def embedded_active(self, value): |
| | if value: |
| | InteractiveShellEmbed._inactive_locations.discard( |
| | self._call_location_id) |
| | InteractiveShellEmbed._inactive_locations.discard( |
| | self._init_location_id) |
| | else: |
| | InteractiveShellEmbed._inactive_locations.add( |
| | self._call_location_id) |
| |
|
| | def __init__(self, **kw): |
| | assert ( |
| | "user_global_ns" not in kw |
| | ), "Key word argument `user_global_ns` has been replaced by `user_module` since IPython 4.0." |
| |
|
| | clid = kw.pop('_init_location_id', None) |
| | if not clid: |
| | frame = sys._getframe(1) |
| | clid = '%s:%s' % (frame.f_code.co_filename, frame.f_lineno) |
| | self._init_location_id = clid |
| |
|
| | super(InteractiveShellEmbed,self).__init__(**kw) |
| |
|
| | |
| | |
| | sys.excepthook = ultratb.FormattedTB(color_scheme=self.colors, |
| | mode=self.xmode, |
| | call_pdb=self.pdb) |
| |
|
| | def init_sys_modules(self): |
| | """ |
| | Explicitly overwrite :mod:`IPython.core.interactiveshell` to do nothing. |
| | """ |
| | pass |
| |
|
| | def init_magics(self): |
| | super(InteractiveShellEmbed, self).init_magics() |
| | self.register_magics(EmbeddedMagics) |
| |
|
| | def __call__( |
| | self, |
| | header="", |
| | local_ns=None, |
| | module=None, |
| | dummy=None, |
| | stack_depth=1, |
| | compile_flags=None, |
| | **kw |
| | ): |
| | """Activate the interactive interpreter. |
| | |
| | __call__(self,header='',local_ns=None,module=None,dummy=None) -> Start |
| | the interpreter shell with the given local and global namespaces, and |
| | optionally print a header string at startup. |
| | |
| | The shell can be globally activated/deactivated using the |
| | dummy_mode attribute. This allows you to turn off a shell used |
| | for debugging globally. |
| | |
| | However, *each* time you call the shell you can override the current |
| | state of dummy_mode with the optional keyword parameter 'dummy'. For |
| | example, if you set dummy mode on with IPShell.dummy_mode = True, you |
| | can still have a specific call work by making it as IPShell(dummy=False). |
| | """ |
| |
|
| | |
| | self.keep_running = True |
| |
|
| | |
| | clid = kw.pop('_call_location_id', None) |
| | if not clid: |
| | frame = sys._getframe(1) |
| | clid = '%s:%s' % (frame.f_code.co_filename, frame.f_lineno) |
| | self._call_location_id = clid |
| |
|
| | if not self.embedded_active: |
| | return |
| |
|
| | |
| | |
| | self.exit_now = False |
| |
|
| | |
| | if dummy or (dummy != 0 and self.dummy_mode): |
| | return |
| |
|
| | |
| | if header: |
| | self.old_banner2 = self.banner2 |
| | self.banner2 = self.banner2 + '\n' + header + '\n' |
| | else: |
| | self.old_banner2 = '' |
| |
|
| | if self.display_banner: |
| | self.show_banner() |
| |
|
| | |
| | |
| | self.mainloop( |
| | local_ns, module, stack_depth=stack_depth, compile_flags=compile_flags |
| | ) |
| |
|
| | self.banner2 = self.old_banner2 |
| |
|
| | if self.exit_msg is not None: |
| | print(self.exit_msg) |
| |
|
| | if self.should_raise: |
| | raise KillEmbedded('Embedded IPython raising error, as user requested.') |
| |
|
| | def mainloop( |
| | self, |
| | local_ns=None, |
| | module=None, |
| | stack_depth=0, |
| | compile_flags=None, |
| | ): |
| | """Embeds IPython into a running python program. |
| | |
| | Parameters |
| | ---------- |
| | local_ns, module |
| | Working local namespace (a dict) and module (a module or similar |
| | object). If given as None, they are automatically taken from the scope |
| | where the shell was called, so that program variables become visible. |
| | stack_depth : int |
| | How many levels in the stack to go to looking for namespaces (when |
| | local_ns or module is None). This allows an intermediate caller to |
| | make sure that this function gets the namespace from the intended |
| | level in the stack. By default (0) it will get its locals and globals |
| | from the immediate caller. |
| | compile_flags |
| | A bit field identifying the __future__ features |
| | that are enabled, as passed to the builtin :func:`compile` function. |
| | If given as None, they are automatically taken from the scope where |
| | the shell was called. |
| | |
| | """ |
| | |
| | |
| | if ((local_ns is None or module is None or compile_flags is None) |
| | and self.default_user_namespaces): |
| | call_frame = sys._getframe(stack_depth).f_back |
| |
|
| | if local_ns is None: |
| | local_ns = call_frame.f_locals |
| | if module is None: |
| | global_ns = call_frame.f_globals |
| | try: |
| | module = sys.modules[global_ns['__name__']] |
| | except KeyError: |
| | warnings.warn("Failed to get module %s" % \ |
| | global_ns.get('__name__', 'unknown module') |
| | ) |
| | module = DummyMod() |
| | module.__dict__ = global_ns |
| | if compile_flags is None: |
| | compile_flags = (call_frame.f_code.co_flags & |
| | compilerop.PyCF_MASK) |
| | |
| | |
| | |
| | orig_user_module = self.user_module |
| | orig_user_ns = self.user_ns |
| | orig_compile_flags = self.compile.flags |
| | |
| | |
| | |
| | |
| | if module is not None: |
| | self.user_module = module |
| |
|
| | |
| | |
| | |
| | |
| | if local_ns is not None: |
| | reentrant_local_ns = {k: v for (k, v) in local_ns.items() if k not in self.user_ns_hidden.keys()} |
| | self.user_ns = reentrant_local_ns |
| | self.init_user_ns() |
| |
|
| | |
| | if compile_flags is not None: |
| | self.compile.flags = compile_flags |
| |
|
| | |
| | |
| | self.set_completer_frame() |
| |
|
| | with self.builtin_trap, self.display_trap: |
| | self.interact() |
| | |
| | |
| | if local_ns is not None: |
| | local_ns.update({k: v for (k, v) in self.user_ns.items() if k not in self.user_ns_hidden.keys()}) |
| |
|
| | |
| | |
| | self.user_module = orig_user_module |
| | self.user_ns = orig_user_ns |
| | self.compile.flags = orig_compile_flags |
| |
|
| |
|
| | def embed(*, header="", compile_flags=None, **kwargs): |
| | """Call this to embed IPython at the current point in your program. |
| | |
| | The first invocation of this will create a :class:`terminal.embed.InteractiveShellEmbed` |
| | instance and then call it. Consecutive calls just call the already |
| | created instance. |
| | |
| | If you don't want the kernel to initialize the namespace |
| | from the scope of the surrounding function, |
| | and/or you want to load full IPython configuration, |
| | you probably want `IPython.start_ipython()` instead. |
| | |
| | Here is a simple example:: |
| | |
| | from IPython import embed |
| | a = 10 |
| | b = 20 |
| | embed(header='First time') |
| | c = 30 |
| | d = 40 |
| | embed() |
| | |
| | Parameters |
| | ---------- |
| | |
| | header : str |
| | Optional header string to print at startup. |
| | compile_flags |
| | Passed to the `compile_flags` parameter of :py:meth:`terminal.embed.InteractiveShellEmbed.mainloop()`, |
| | which is called when the :class:`terminal.embed.InteractiveShellEmbed` instance is called. |
| | **kwargs : various, optional |
| | Any other kwargs will be passed to the :class:`terminal.embed.InteractiveShellEmbed` constructor. |
| | Full customization can be done by passing a traitlets :class:`Config` in as the |
| | `config` argument (see :ref:`configure_start_ipython` and :ref:`terminal_options`). |
| | """ |
| | config = kwargs.get('config') |
| | if config is None: |
| | config = load_default_config() |
| | config.InteractiveShellEmbed = config.TerminalInteractiveShell |
| | kwargs['config'] = config |
| | using = kwargs.get('using', 'sync') |
| | if using : |
| | kwargs['config'].update({'TerminalInteractiveShell':{'loop_runner':using, 'colors':'NoColor', 'autoawait': using!='sync'}}) |
| | |
| | ps1 = None |
| | ps2 = None |
| | try: |
| | ps1 = sys.ps1 |
| | ps2 = sys.ps2 |
| | except AttributeError: |
| | pass |
| | |
| | saved_shell_instance = InteractiveShell._instance |
| | if saved_shell_instance is not None: |
| | cls = type(saved_shell_instance) |
| | cls.clear_instance() |
| | frame = sys._getframe(1) |
| | shell = InteractiveShellEmbed.instance(_init_location_id='%s:%s' % ( |
| | frame.f_code.co_filename, frame.f_lineno), **kwargs) |
| | shell(header=header, stack_depth=2, compile_flags=compile_flags, |
| | _call_location_id='%s:%s' % (frame.f_code.co_filename, frame.f_lineno)) |
| | InteractiveShellEmbed.clear_instance() |
| | |
| | if saved_shell_instance is not None: |
| | cls = type(saved_shell_instance) |
| | cls.clear_instance() |
| | for subclass in cls._walk_mro(): |
| | subclass._instance = saved_shell_instance |
| | if ps1 is not None: |
| | sys.ps1 = ps1 |
| | sys.ps2 = ps2 |
| |
|