Source code for xdev._ipython_ext

import sys
from IPython.terminal.embed import InteractiveShellEmbed, load_default_config
# from IPython.terminal.ipapp import load_default_config


[docs] class InteractiveShellEmbedEnhanced(InteractiveShellEmbed): """ A workaround to issue https://github.com/ipython/ipython/issues/10695 In embedded ipython shell, new variables are created in locals() instead of globals() and functions do not make closures for the locals. Examples: If not embedded from a function, then nothing special needs to be done other than calling embed() defined below. These code will work: a=3; (lambda: a)() import time; (lambda: time.time())() If embedded from a function, in order to introduce new variables to global name space, we need to call ipy.to_global() first and then call ipy.to_local() to come back ipy.to_global() a=3 # here we cannot access the local variables ipy.to_local() (lambda: a)() It seems that a contextmanager to switch back automatically will not work: with ipy.to_global_manager(): a=3 print((lambda: a)()) It may be because of that IPython compiles the whole cell and run it using exec(code, globals, locals). But globals and locals are calculated at the time this context manager is defined. """ @property def user_global_ns(self): if getattr(self, 'embedded_outside_func', False): return self.user_ns else: return self.user_module.__dict__
[docs] def init_frame(self, frame): if frame.f_code.co_name == '<module>': # if embedded outside a function self.embedded_outside_func = True else: # if embedded from a function self.embedded_outside_func = False self._saved_user_ns = None self._ns = {} # make global variables for user access to the histories self._ns['_ih'] = self.history_manager.input_hist_parsed self._ns['_oh'] = self.history_manager.output_hist self._ns['_dh'] = self.history_manager.dir_hist # user aliases to input and output histories. These shouldn't show up # in %who, as they can have very large reprs. self._ns['In'] = self.history_manager.input_hist_parsed self._ns['Out'] = self.history_manager.output_hist # Store myself as the public api!!! # ns['get_ipython'] = self.get_ipython self._ns['exit'] = self.exiter self._ns['quit'] = self.exiter self.to_global = self._to_global self.to_local = self._to_local
[docs] def _to_global(self): self._saved_user_ns = self.user_ns assert not set(self._ns).intersection(self.user_module.__dict__) self.user_ns = self.user_module.__dict__ self.user_ns.update(self._ns)
[docs] def _to_local(self): if self._saved_user_ns is None: return for key in self._ns: del self.user_ns[key] self.user_ns = self._saved_user_ns
[docs] def share_locals(self): """ share the locals to global manually """ if (id(self.user_ns) != id(self.user_module.__dict__)): self.user_module.__dict__.update(self.user_ns)
[docs] def embed2(local_ns=None, **kwargs): """ based on IPython.terminal.embed.embed() References: https://github.com/ipython/ipython/issues/10695 """ import glob import os from IPython.core.interactiveshell import InteractiveShell config = kwargs.get('config') header = kwargs.pop('header', u'') compile_flags = kwargs.pop('compile_flags', None) if config is None: config = load_default_config() config.InteractiveShellEmbedEnhanced = config.TerminalInteractiveShell kwargs['config'] = config #save ps1/ps2 if defined ps1 = None ps2 = None try: ps1 = sys.ps1 ps2 = sys.ps2 except AttributeError: pass #save previous instance 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 is the ipython instance returned from get_ipython() # frame refers to the caller of this function shell = InteractiveShellEmbedEnhanced.instance(_init_location_id='%s:%s' % ( frame.f_code.co_filename, frame.f_lineno), **kwargs) shell.init_frame(frame) ####################### # load the startup files and update the local_ns ###################### # if local_ns is None: # local_ns = frame.f_locals global_ns = frame.f_globals global_ns['ipy'] = shell global_ns['share_locals'] = shell.share_locals startup_files = glob.glob(os.path.join(shell.profile_dir.startup_dir, '*.py')) startup_files += glob.glob(os.path.join(shell.profile_dir.startup_dir, '*.ipy')) if '__file__' in global_ns: hasfile = True cfile = global_ns['__file__'] else: hasfile = False for filename in sorted(startup_files): if filename.endswith('.ipy'): shell.safe_execfile_ipy(filename) else: global_ns['__file__'] = filename shell.safe_execfile(filename, global_ns, raise_exceptions=True) # this updates the global_ns if hasfile: global_ns['__file__'] = cfile else: del global_ns['__file__'] ######################## # launch the shell ####################### shell(local_ns=local_ns, header=header, stack_depth=2, compile_flags=compile_flags, _call_location_id='%s:%s' % (frame.f_code.co_filename, frame.f_lineno)) InteractiveShellEmbedEnhanced.clear_instance() #restore previous 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