From 7e14db60d893d9a9420be4062998b28289238808 Mon Sep 17 00:00:00 2001 From: Fernando Perez Date: Tue, 8 May 2012 16:22:18 -0700 Subject: [PATCH 001/103] First full decoupling of magics into a standalone object. --- IPython/core/completer.py | 4 ++-- IPython/core/interactiveshell.py | 31 ++++++++++++++++++------------- IPython/core/magic.py | 8 ++++---- IPython/core/splitinput.py | 2 +- 4 files changed, 25 insertions(+), 20 deletions(-) diff --git a/IPython/core/completer.py b/IPython/core/completer.py index dd236614af6..02f646feca0 100644 --- a/IPython/core/completer.py +++ b/IPython/core/completer.py @@ -472,7 +472,7 @@ def __init__(self, shell=None, namespace=None, global_namespace=None, # List where completion matches will be stored self.matches = [] - self.shell = shell.shell + self.shell = shell if alias_table is None: alias_table = {} self.alias_table = alias_table @@ -602,7 +602,7 @@ def magic_matches(self, text): #print 'Completer->magic_matches:',text,'lb',self.text_until_cursor # dbg # Get all shell magics now rather than statically, so magics loaded at # runtime show up too - magics = self.shell.lsmagic() + magics = self.shell._magic.lsmagic() pre = self.magic_escape baretext = text.lstrip(pre) return [ pre+m for m in magics if m.startswith(baretext)] diff --git a/IPython/core/interactiveshell.py b/IPython/core/interactiveshell.py index a2ccbe81323..9c59da87bb4 100644 --- a/IPython/core/interactiveshell.py +++ b/IPython/core/interactiveshell.py @@ -187,7 +187,7 @@ def get_readline_tail(self, n=10): # Main IPython class #----------------------------------------------------------------------------- -class InteractiveShell(SingletonConfigurable, Magic): +class InteractiveShell(SingletonConfigurable): """An enhanced, interactive shell for Python.""" _instance = None @@ -430,7 +430,7 @@ def __init__(self, config=None, ipython_dir=None, profile_dir=None, self.init_encoding() self.init_prefilter() - Magic.__init__(self, self) + self._magic = Magic(self) self.init_syntax_highlighting() self.init_hooks() @@ -588,11 +588,11 @@ def init_logstart(self): """Initialize logging in case it was requested at the command line. """ if self.logappend: - self.magic_logstart(self.logappend + ' append') + self.magic('logstart %s append' % self.logappend) elif self.logfile: - self.magic_logstart(self.logfile) + self.magic('logstart %' % self.logfile) elif self.logstart: - self.magic_logstart() + self.magic('logstart') def init_builtins(self): # A single, static flag that we set to True. Its presence indicates @@ -1397,7 +1397,7 @@ def _ofind(self, oname, namespaces=None): if not found: if oname.startswith(ESC_MAGIC): oname = oname[1:] - obj = getattr(self,'magic_'+oname,None) + obj = self.find_magic(oname) if obj is not None: found = True ospace = 'IPython internal' @@ -1996,7 +1996,7 @@ def init_magics(self): # FIXME: Move the color initialization to the DisplayHook, which # should be split into a prompt manager and displayhook. We probably # even need a centralize colors management object. - self.magic_colors(self.colors) + self.magic('colors %s' % self.colors) # History was moved to a separate module from IPython.core import history history.init_ipython(self) @@ -2026,11 +2026,11 @@ def magic(self, arg_s, next_input=None): magic_name, _, magic_args = arg_s.partition(' ') magic_name = magic_name.lstrip(prefilter.ESC_MAGIC) - fn = getattr(self,'magic_'+magic_name,None) + fn = self.find_magic(magic_name) if fn is None: error("Magic function `%s` not found." % magic_name) else: - magic_args = self.var_expand(magic_args,1) + magic_args = self.var_expand(magic_args, 1) # Grab local namespace if we need it: if getattr(fn, "needs_local_scope", False): self._magic_locals = sys._getframe(1).f_locals @@ -2040,7 +2040,7 @@ def magic(self, arg_s, next_input=None): self._magic_locals = {} return result - def define_magic(self, magicname, func): + def define_magic(self, magic_name, func): """Expose own function as magic function for ipython Example:: @@ -2054,10 +2054,15 @@ def foo_impl(self,parameter_s=''): ip.define_magic('foo',foo_impl) """ im = types.MethodType(func,self) - old = getattr(self, "magic_" + magicname, None) - setattr(self, "magic_" + magicname, im) + old = self.find_magic(magic_name) + setattr(self._magic, 'magic_' + magic_name, im) return old + def find_magic(self, magic_name): + """Find and return a magic function by name. + """ + return getattr(self._magic, 'magic_' + magic_name, None) + #------------------------------------------------------------------------- # Things related to macros #------------------------------------------------------------------------- @@ -2685,7 +2690,7 @@ def enable_pylab(self, gui=None, import_all=True): # Now we must activate the gui pylab wants to use, and fix %run to take # plot updates into account self.enable_gui(gui) - self.magic_run = self._pylab_magic_run + self._magic.magic_run = self._pylab_magic_run #------------------------------------------------------------------------- # Utilities diff --git a/IPython/core/magic.py b/IPython/core/magic.py index 6980f0da9f4..d15047ec98b 100644 --- a/IPython/core/magic.py +++ b/IPython/core/magic.py @@ -107,7 +107,7 @@ class MacroToEdit(ValueError): pass # Configurable. This messes up the MRO in some way. The fix is that we need to # make Magic a configurable that InteractiveShell does not subclass. -class Magic: +class Magic(object): """Magic functions for InteractiveShell. Shell functions which can be reached as %function_name. All magic @@ -127,7 +127,7 @@ class Magic: #...................................................................... # some utility functions - def __init__(self,shell): + def __init__(self, shell): self.options_table = {} if profile is None: @@ -3012,7 +3012,7 @@ def magic_cd(self, parameter_s=''): # jump to bookmark if needed else: if not os.path.isdir(ps) or opts.has_key('b'): - bkms = self.db.get('bookmarks', {}) + bkms = self.shell.db.get('bookmarks', {}) if bkms.has_key(ps): target = bkms[ps] @@ -3049,7 +3049,7 @@ def magic_cd(self, parameter_s=''): if oldcwd != cwd: dhist.append(cwd) - self.db['dhist'] = compress_dhist(dhist)[-100:] + self.shell.db['dhist'] = compress_dhist(dhist)[-100:] if not 'q' in opts and self.shell.user_ns['_dh']: print self.shell.user_ns['_dh'][-1] diff --git a/IPython/core/splitinput.py b/IPython/core/splitinput.py index ab9063b8cd4..9cf060cde3c 100644 --- a/IPython/core/splitinput.py +++ b/IPython/core/splitinput.py @@ -133,7 +133,7 @@ def ofind(self, ip): """ if not self._oinfo: # ip.shell._ofind is actually on the Magic class! - self._oinfo = ip.shell._ofind(self.ifun) + self._oinfo = ip._ofind(self.ifun) return self._oinfo def __str__(self): From f26fdb6b68c1b27aaffd3523d075bba50129f88b Mon Sep 17 00:00:00 2001 From: Fernando Perez Date: Tue, 8 May 2012 16:51:17 -0700 Subject: [PATCH 002/103] Simplify logic of passing local scope to magics that need it. Just use an argument rather than setting more global state. --- IPython/core/interactiveshell.py | 12 ++++++------ IPython/core/magic.py | 16 +++++++--------- 2 files changed, 13 insertions(+), 15 deletions(-) diff --git a/IPython/core/interactiveshell.py b/IPython/core/interactiveshell.py index 9c59da87bb4..de4c9946785 100644 --- a/IPython/core/interactiveshell.py +++ b/IPython/core/interactiveshell.py @@ -2023,21 +2023,21 @@ def magic(self, arg_s, next_input=None): if next_input: self.set_next_input(next_input) - magic_name, _, magic_args = arg_s.partition(' ') + magic_name, _, magic_arg_s = arg_s.partition(' ') magic_name = magic_name.lstrip(prefilter.ESC_MAGIC) fn = self.find_magic(magic_name) if fn is None: error("Magic function `%s` not found." % magic_name) else: - magic_args = self.var_expand(magic_args, 1) + magic_arg_s = self.var_expand(magic_arg_s, 1) + # Put magic args in a list so we can call with f(*a) syntax + args = [magic_arg_s] # Grab local namespace if we need it: if getattr(fn, "needs_local_scope", False): - self._magic_locals = sys._getframe(1).f_locals + args.append(sys._getframe(1).f_locals) with self.builtin_trap: - result = fn(magic_args) - # Ensure we're not keeping object references around: - self._magic_locals = {} + result = fn(*args) return result def define_magic(self, magic_name, func): diff --git a/IPython/core/magic.py b/IPython/core/magic.py index d15047ec98b..5269e7a729b 100644 --- a/IPython/core/magic.py +++ b/IPython/core/magic.py @@ -2017,7 +2017,7 @@ def magic_timeit(self, parameter_s =''): @skip_doctest @needs_local_scope - def magic_time(self,parameter_s = ''): + def magic_time(self,parameter_s, user_locals): """Time execution of a Python statement or expression. The CPU and wall clock times are printed, and the value of the @@ -2084,19 +2084,17 @@ def magic_time(self,parameter_s = ''): tc = clock()-t0 # skew measurement as little as possible glob = self.shell.user_ns - locs = self._magic_locals - clk = clock2 wtime = time.time # time execution wall_st = wtime() if mode=='eval': - st = clk() - out = eval(code, glob, locs) - end = clk() + st = clock2() + out = eval(code, glob, user_locals) + end = clock2() else: - st = clk() - exec code in glob, locs - end = clk() + st = clock2() + exec code in glob, user_locals + end = clock2() out = None wall_end = wtime() # Compute actual times and report From 09ab6976eb73fbadf0ac12d988a813af487c711a Mon Sep 17 00:00:00 2001 From: Fernando Perez Date: Tue, 8 May 2012 17:00:22 -0700 Subject: [PATCH 003/103] Fix automagic detection. --- IPython/core/prefilter.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/IPython/core/prefilter.py b/IPython/core/prefilter.py index 7c5c29611f2..83f08ce4555 100644 --- a/IPython/core/prefilter.py +++ b/IPython/core/prefilter.py @@ -612,7 +612,7 @@ def check(self, line_info): check_esc_chars. This just checks for automagic. Also, before triggering the magic handler, make sure that there is nothing in the user namespace which could shadow it.""" - if not self.shell.automagic or not hasattr(self.shell,'magic_'+line_info.ifun): + if not self.shell.automagic or not self.shell.find_magic(line_info.ifun): return None # We have a likely magic method. Make sure we should actually call it. @@ -891,7 +891,7 @@ def handle(self, line_info): line = line[:-1] if line: #print 'line:<%r>' % line # dbg - self.shell.magic_pinfo(line_info.ifun) + self.shell.magic('pinfo %s' % line_info.ifun) else: self.shell.show_usage() return '' # Empty string is needed here! From c0117b5cede8cf082027f8e7afd2f552de6ef7ca Mon Sep 17 00:00:00 2001 From: Fernando Perez Date: Tue, 8 May 2012 17:02:20 -0700 Subject: [PATCH 004/103] Fix access from magics to shell storage database. --- IPython/core/magic.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/IPython/core/magic.py b/IPython/core/magic.py index 5269e7a729b..15ed9216879 100644 --- a/IPython/core/magic.py +++ b/IPython/core/magic.py @@ -1752,7 +1752,7 @@ def magic_run(self, parameter_s ='', runner=None, try: stats = None - with self.readline_no_record: + with self.shell.readline_no_record: if 'p' in opts: stats = self.magic_prun('', 0, opts, arg_lst, prog_ns) else: @@ -2796,7 +2796,7 @@ def magic_alias(self, parameter_s = ''): par = parameter_s.strip() if not par: - stored = self.db.get('stored_aliases', {} ) + stored = self.shell.db.get('stored_aliases', {} ) aliases = sorted(self.shell.alias_manager.aliases) # for k, v in stored: # atab.append(k, v[0]) @@ -2819,11 +2819,11 @@ def magic_unalias(self, parameter_s = ''): aname = parameter_s.strip() self.shell.alias_manager.undefine_alias(aname) - stored = self.db.get('stored_aliases', {} ) + stored = self.shell.db.get('stored_aliases', {} ) if aname in stored: print "Removing %stored alias",aname del stored[aname] - self.db['stored_aliases'] = stored + self.shell.db['stored_aliases'] = stored def magic_rehashx(self, parameter_s = ''): """Update the alias table with all executable files in $PATH. @@ -3036,7 +3036,7 @@ def magic_cd(self, parameter_s=''): dhist = self.shell.user_ns['_dh'] if oldcwd != cwd: dhist.append(cwd) - self.db['dhist'] = compress_dhist(dhist)[-100:] + self.shell.db['dhist'] = compress_dhist(dhist)[-100:] else: os.chdir(self.shell.home_dir) @@ -3305,7 +3305,7 @@ def magic_bookmark(self, parameter_s=''): if len(args) > 2: raise UsageError("%bookmark: too many arguments") - bkms = self.db.get('bookmarks',{}) + bkms = self.shell.db.get('bookmarks',{}) if opts.has_key('d'): try: @@ -3340,7 +3340,7 @@ def magic_bookmark(self, parameter_s=''): bkms[args[0]] = os.getcwdu() elif len(args)==2: bkms[args[0]] = args[1] - self.db['bookmarks'] = bkms + self.shell.db['bookmarks'] = bkms def magic_pycat(self, parameter_s=''): From 8c87551ae15e2488b0284120433b048059de8b02 Mon Sep 17 00:00:00 2001 From: Fernando Perez Date: Tue, 8 May 2012 18:08:54 -0700 Subject: [PATCH 005/103] Fix pylab support and simplify approach to reduce global state in main object. --- IPython/core/history.py | 5 +++-- IPython/core/interactiveshell.py | 4 ++-- IPython/core/magic.py | 15 ++++----------- 3 files changed, 9 insertions(+), 15 deletions(-) diff --git a/IPython/core/history.py b/IPython/core/history.py index 6c51f73299b..5a9078e3304 100644 --- a/IPython/core/history.py +++ b/IPython/core/history.py @@ -924,6 +924,7 @@ def magic_rep(self, arg): self.set_next_input(cmd.rstrip()) print("Couldn't evaluate or find in history:", arg) + def magic_rerun(self, parameter_s=''): """Re-run previous input @@ -969,8 +970,8 @@ def init_ipython(ip): ip.define_magic("rep", magic_rep) ip.define_magic("recall", magic_rep) ip.define_magic("rerun", magic_rerun) - ip.define_magic("hist",magic_history) # Alternative name - ip.define_magic("history",magic_history) + ip.define_magic("hist", magic_history) # Alternative name + ip.define_magic("history", magic_history) # XXX - ipy_completers are in quarantine, need to be updated to new apis #import ipy_completers diff --git a/IPython/core/interactiveshell.py b/IPython/core/interactiveshell.py index de4c9946785..8c249d5ccc9 100644 --- a/IPython/core/interactiveshell.py +++ b/IPython/core/interactiveshell.py @@ -2674,7 +2674,7 @@ def enable_pylab(self, gui=None, import_all=True): make sense in all contexts, for example a terminal ipython can't display figures inline. """ - + from IPython.core.pylabtools import mpl_runner # We want to prevent the loading of pylab to pollute the user's # namespace as shown by the %who* magics, so we execute the activation # code in an empty namespace, and we update *both* user_ns and @@ -2690,7 +2690,7 @@ def enable_pylab(self, gui=None, import_all=True): # Now we must activate the gui pylab wants to use, and fix %run to take # plot updates into account self.enable_gui(gui) - self._magic.magic_run = self._pylab_magic_run + self._magic.default_runner = mpl_runner(self.safe_execfile) #------------------------------------------------------------------------- # Utilities diff --git a/IPython/core/magic.py b/IPython/core/magic.py index 15ed9216879..b9b658edf3b 100644 --- a/IPython/core/magic.py +++ b/IPython/core/magic.py @@ -49,7 +49,6 @@ from IPython.core.macro import Macro from IPython.core import magic_arguments, page from IPython.core.prefilter import ESC_MAGIC -from IPython.core.pylabtools import mpl_runner from IPython.testing.skipdoctest import skip_doctest from IPython.utils import py3compat from IPython.utils.encoding import DEFAULT_ENCODING @@ -124,6 +123,8 @@ class Magic(object): configurables = None + + default_runner = None #...................................................................... # some utility functions @@ -1795,6 +1796,8 @@ def magic_run(self, parameter_s ='', runner=None, # user (run by exec in pdb itself). self.shell.InteractiveTB(etype, value, tb, tb_offset=3) else: + if runner is None: + runner = self.default_runner if runner is None: runner = self.shell.safe_execfile if 't' in opts: @@ -3549,16 +3552,6 @@ def magic_install_default_config(self, s): "Add `--reset` to overwrite already existing config files with defaults." ]) - # Pylab support: simple wrappers that activate pylab, load gui input - # handling and modify slightly %run - - @skip_doctest - def _pylab_magic_run(self, parameter_s=''): - Magic.magic_run(self, parameter_s, - runner=mpl_runner(self.shell.safe_execfile)) - - _pylab_magic_run.__doc__ = magic_run.__doc__ - @skip_doctest def magic_pylab(self, s): """Load numpy and matplotlib to work interactively. From 1244bdc92453789d633939e7a3a54059b1562388 Mon Sep 17 00:00:00 2001 From: Fernando Perez Date: Tue, 8 May 2012 18:12:23 -0700 Subject: [PATCH 006/103] Fix history (was accessing directly shell methods) --- IPython/core/history.py | 16 ++++++++++------ IPython/core/interactiveshell.py | 2 +- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/IPython/core/history.py b/IPython/core/history.py index 5a9078e3304..e3761e5384b 100644 --- a/IPython/core/history.py +++ b/IPython/core/history.py @@ -53,7 +53,8 @@ def __enter__(self, *args, **kwargs): def __exit__(self, *args, **kwargs): pass - + + @decorator def needs_sqlite(f,*a,**kw): """return an empty list in the absence of sqlite""" @@ -62,6 +63,7 @@ def needs_sqlite(f,*a,**kw): else: return f(*a,**kw) + class HistoryAccessor(Configurable): """Access the history database without adding to it. @@ -84,7 +86,6 @@ class HistoryAccessor(Configurable): """) - # The SQLite database if sqlite3: db = Instance(sqlite3.Connection) @@ -676,6 +677,7 @@ def stop(self): (?P\d+))? $""", re.VERBOSE) + def extract_hist_ranges(ranges_str): """Turn a string of history ranges into 3-tuples of (session, start, stop). @@ -708,12 +710,14 @@ def extract_hist_ranges(ranges_str): yield (sess, 1, None) yield (endsess, 1, end) + def _format_lineno(session, line): """Helper function to format line numbers properly.""" if session == 0: return str(line) return "%s#%s" % (session, line) + @skip_doctest def magic_history(self, parameter_s = ''): """Print input history (_i variables), with most recent last. @@ -902,13 +906,13 @@ def magic_rep(self, arg): placed at the next input prompt. """ if not arg: # Last output - self.set_next_input(str(self.shell.user_ns["_"])) + self.shell.set_next_input(str(self.shell.user_ns["_"])) return # Get history range histlines = self.history_manager.get_range_by_str(arg) cmd = "\n".join(x[2] for x in histlines) if cmd: - self.set_next_input(cmd.rstrip()) + self.shell.set_next_input(cmd.rstrip()) return try: # Variable in user namespace @@ -918,10 +922,10 @@ def magic_rep(self, arg): for h in reversed([x[2] for x in histlines]): if 'rep' in h: continue - self.set_next_input(h.rstrip()) + self.shell.set_next_input(h.rstrip()) return else: - self.set_next_input(cmd.rstrip()) + self.shell.set_next_input(cmd.rstrip()) print("Couldn't evaluate or find in history:", arg) diff --git a/IPython/core/interactiveshell.py b/IPython/core/interactiveshell.py index 8c249d5ccc9..2643dafb259 100644 --- a/IPython/core/interactiveshell.py +++ b/IPython/core/interactiveshell.py @@ -2053,7 +2053,7 @@ def foo_impl(self,parameter_s=''): ip.define_magic('foo',foo_impl) """ - im = types.MethodType(func,self) + im = types.MethodType(func, self._magic) old = self.find_magic(magic_name) setattr(self._magic, 'magic_' + magic_name, im) return old From 7233d8bd47ee241dc83f60177eea1c991af9905e Mon Sep 17 00:00:00 2001 From: Fernando Perez Date: Tue, 8 May 2012 19:58:02 -0700 Subject: [PATCH 007/103] Fix more failures due to direct shell access in magics. --- IPython/core/history.py | 14 +++++++------- IPython/core/magic.py | 19 ++++++++++--------- IPython/core/prefilter.py | 2 +- IPython/core/splitinput.py | 4 +++- 4 files changed, 21 insertions(+), 18 deletions(-) diff --git a/IPython/core/history.py b/IPython/core/history.py index e3761e5384b..8434872aef7 100644 --- a/IPython/core/history.py +++ b/IPython/core/history.py @@ -909,7 +909,7 @@ def magic_rep(self, arg): self.shell.set_next_input(str(self.shell.user_ns["_"])) return # Get history range - histlines = self.history_manager.get_range_by_str(arg) + histlines = self.shell.history_manager.get_range_by_str(arg) cmd = "\n".join(x[2] for x in histlines) if cmd: self.shell.set_next_input(cmd.rstrip()) @@ -918,7 +918,7 @@ def magic_rep(self, arg): try: # Variable in user namespace cmd = str(eval(arg, self.shell.user_ns)) except Exception: # Search for term in history - histlines = self.history_manager.search("*"+arg+"*") + histlines = self.shell.history_manager.search("*"+arg+"*") for h in reversed([x[2] for x in histlines]): if 'rep' in h: continue @@ -945,10 +945,10 @@ def magic_rerun(self, parameter_s=''): opts, args = self.parse_options(parameter_s, 'l:g:', mode='string') if "l" in opts: # Last n lines n = int(opts['l']) - hist = self.history_manager.get_tail(n) + hist = self.shell.history_manager.get_tail(n) elif "g" in opts: # Search p = "*"+opts['g']+"*" - hist = list(self.history_manager.search(p)) + hist = list(self.shell.history_manager.search(p)) for l in reversed(hist): if "rerun" not in l[2]: hist = [l] # The last match which isn't a %rerun @@ -956,9 +956,9 @@ def magic_rerun(self, parameter_s=''): else: hist = [] # No matches except %rerun elif args: # Specify history ranges - hist = self.history_manager.get_range_by_str(args) + hist = self.shell.history_manager.get_range_by_str(args) else: # Last line - hist = self.history_manager.get_tail(1) + hist = self.shell.history_manager.get_tail(1) hist = [x[2] for x in hist] if not hist: print("No lines in history match specification") @@ -967,7 +967,7 @@ def magic_rerun(self, parameter_s=''): print("=== Executing: ===") print(histlines) print("=== Output: ===") - self.run_cell("\n".join(hist), store_history=False) + self.shell.run_cell("\n".join(hist), store_history=False) def init_ipython(ip): diff --git a/IPython/core/magic.py b/IPython/core/magic.py index b9b658edf3b..d54ab01ecfa 100644 --- a/IPython/core/magic.py +++ b/IPython/core/magic.py @@ -171,8 +171,8 @@ def lsmagic(self): inst_bound_magic = lambda fn: fn.startswith('magic_') and \ callable(self.__class__.__dict__[fn]) magics = filter(class_magic,Magic.__dict__.keys()) + \ - filter(inst_magic,self.__dict__.keys()) + \ - filter(inst_bound_magic,self.__class__.__dict__.keys()) + filter(inst_magic, self.__dict__.keys()) + \ + filter(inst_bound_magic, self.__class__.__dict__.keys()) out = [] for fn in set(magics): out.append(fn.replace('magic_','',1)) @@ -337,7 +337,7 @@ def magic_magic(self, parameter_s = ''): magic_docs = [] for fname in self.lsmagic(): mname = 'magic_' + fname - for space in (Magic,self,self.__class__): + for space in (Magic, self, self.__class__): try: fn = space.__dict__[mname] except KeyError: @@ -1035,17 +1035,17 @@ def magic_reset(self, parameter_s=''): # reset in/out/dhist/array: previously extensinions/clearcmd.py ip = self.shell - user_ns = self.user_ns # local lookup, heavily used + user_ns = self.shell.user_ns # local lookup, heavily used for target in args: target = target.lower() # make matches case insensitive if target == 'out': print "Flushing output cache (%d entries)" % len(user_ns['_oh']) - self.displayhook.flush() + self.shell.displayhook.flush() elif target == 'in': print "Flushing input history" - pc = self.displayhook.prompt_count + 1 + pc = self.shell.displayhook.prompt_count + 1 for n in range(1, pc): key = '_i'+repr(n) user_ns.pop(key,None) @@ -2630,7 +2630,7 @@ def magic_edit(self,parameter_s='',last_call=['','']): self.shell.run_cell(file_read(filename), store_history=False) else: - self.shell.safe_execfile(filename,self.shell.user_ns, + self.shell.safe_execfile(filename, self.shell.user_ns, self.shell.user_ns) if is_temp: @@ -3804,7 +3804,8 @@ def magic_config(self, s): # some IPython objects are Configurable, but do not yet have # any configurable traits. Exclude them from the effects of # this magic, as their presence is just noise: - configurables = [ c for c in self.configurables if c.__class__.class_traits(config=True) ] + configurables = [ c for c in self.shell.configurables + if c.__class__.class_traits(config=True) ] classnames = [ c.__class__.__name__ for c in configurables ] line = s.strip() @@ -3832,7 +3833,7 @@ def magic_config(self, s): # leave quotes on args when splitting, because we want # unquoted args to eval in user_ns cfg = Config() - exec "cfg."+line in locals(), self.user_ns + exec "cfg."+line in locals(), self.shell.user_ns for configurable in configurables: try: diff --git a/IPython/core/prefilter.py b/IPython/core/prefilter.py index 83f08ce4555..22bf1e15ceb 100644 --- a/IPython/core/prefilter.py +++ b/IPython/core/prefilter.py @@ -807,7 +807,7 @@ def handle(self, line_info): pre = line_info.pre esc = line_info.esc continue_prompt = line_info.continue_prompt - obj = line_info.ofind(self)['obj'] + obj = line_info.ofind(self.shell)['obj'] #print 'pre <%s> ifun <%s> rest <%s>' % (pre,ifun,the_rest) # dbg # This should only be active for single-line input! diff --git a/IPython/core/splitinput.py b/IPython/core/splitinput.py index 9cf060cde3c..692251e7677 100644 --- a/IPython/core/splitinput.py +++ b/IPython/core/splitinput.py @@ -49,6 +49,7 @@ (.*?$|$) # rest of line """, re.VERBOSE) + def split_user_input(line, pattern=None): """Split user input into initial whitespace, escape character, function part and the rest. @@ -76,6 +77,7 @@ def split_user_input(line, pattern=None): #print 'pre <%s> ifun <%s> rest <%s>' % (pre,ifun.strip(),the_rest) # dbg return pre, esc or '', ifun.strip(), the_rest.lstrip() + class LineInfo(object): """A single line of input and associated info. @@ -122,7 +124,7 @@ def ofind(self, ip): """Do a full, attribute-walking lookup of the ifun in the various namespaces for the given IPython InteractiveShell instance. - Return a dict with keys: found,obj,ospace,ismagic + Return a dict with keys: {found, obj, ospace, ismagic} Note: can cause state changes because of calling getattr, but should only be run if autocall is on and if the line hasn't matched any From 2b8793f7c0167ebc68b89151b4798df21e9efbba Mon Sep 17 00:00:00 2001 From: Fernando Perez Date: Tue, 8 May 2012 20:01:01 -0700 Subject: [PATCH 008/103] Simplify line info code. --- IPython/core/splitinput.py | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/IPython/core/splitinput.py b/IPython/core/splitinput.py index 692251e7677..a8c680eb9aa 100644 --- a/IPython/core/splitinput.py +++ b/IPython/core/splitinput.py @@ -118,8 +118,6 @@ def __init__(self, line, continue_prompt=False): else: self.pre_whitespace = self.pre - self._oinfo = None - def ofind(self, ip): """Do a full, attribute-walking lookup of the ifun in the various namespaces for the given IPython InteractiveShell instance. @@ -133,10 +131,7 @@ def ofind(self, ip): Does cache the results of the call, so can be called multiple times without worrying about *further* damaging state. """ - if not self._oinfo: - # ip.shell._ofind is actually on the Magic class! - self._oinfo = ip._ofind(self.ifun) - return self._oinfo + return ip._ofind(self.ifun) def __str__(self): return "LineInfo [%s|%s|%s|%s]" %(self.pre, self.esc, self.ifun, self.the_rest) From e223878f8ed69da8c9ac26c5d82ac86b97b20bec Mon Sep 17 00:00:00 2001 From: Fernando Perez Date: Tue, 8 May 2012 20:03:27 -0700 Subject: [PATCH 009/103] Move extract_input_lines to main shell object, where it belongs. --- IPython/core/interactiveshell.py | 23 +++++++++++++++++++++++ IPython/core/magic.py | 26 +------------------------- 2 files changed, 24 insertions(+), 25 deletions(-) diff --git a/IPython/core/interactiveshell.py b/IPython/core/interactiveshell.py index 2643dafb259..120512c9ebb 100644 --- a/IPython/core/interactiveshell.py +++ b/IPython/core/interactiveshell.py @@ -2754,6 +2754,29 @@ def show_usage(self): """Show a usage message""" page.page(IPython.core.usage.interactive_usage) + def extract_input_lines(self, range_str, raw=False): + """Return as a string a set of input history slices. + + Parameters + ---------- + range_str : string + The set of slices is given as a string, like "~5/6-~4/2 4:8 9", + since this function is for use by magic functions which get their + arguments as strings. The number before the / is the session + number: ~n goes n back from the current session. + + Optional Parameters: + - raw(False): by default, the processed input is used. If this is + true, the raw input history is used instead. + + Note that slices can be called with two notations: + + N:M -> standard python form, means including items N...(M-1). + + N-M -> include items N..M (closed endpoint).""" + lines = self.history_manager.get_range_by_str(range_str, raw=raw) + return "\n".join(x for _, _, x in lines) + def find_user_code(self, target, raw=True, py_only=False): """Get a code string from history, file, url, or a string or macro. diff --git a/IPython/core/magic.py b/IPython/core/magic.py index d54ab01ecfa..54bf6deaf01 100644 --- a/IPython/core/magic.py +++ b/IPython/core/magic.py @@ -179,30 +179,6 @@ def lsmagic(self): out.sort() return out - def extract_input_lines(self, range_str, raw=False): - """Return as a string a set of input history slices. - - Parameters - ---------- - range_str : string - The set of slices is given as a string, like "~5/6-~4/2 4:8 9", - since this function is for use by magic functions which get their - arguments as strings. The number before the / is the session - number: ~n goes n back from the current session. - - Optional Parameters: - - raw(False): by default, the processed input is used. If this is - true, the raw input history is used instead. - - Note that slices can be called with two notations: - - N:M -> standard python form, means including items N...(M-1). - - N-M -> include items N..M (closed endpoint).""" - lines = self.shell.history_manager.\ - get_range_by_str(range_str, raw=raw) - return "\n".join(x for _, _, x in lines) - def arg_err(self,func): """Print docstring if incorrect arguments were passed""" print 'Error in arguments:' @@ -2370,7 +2346,7 @@ class DataIsObject(Exception): pass use_temp = False elif args: # Mode where user specifies ranges of lines, like in %macro. - data = self.extract_input_lines(args, opts_raw) + data = self.shell.extract_input_lines(args, opts_raw) if not data: try: # Load the parameter given as a variable. If not a string, From c92f639c4e55004a532fa6937984f1de343fe4ce Mon Sep 17 00:00:00 2001 From: Fernando Perez Date: Tue, 8 May 2012 20:04:30 -0700 Subject: [PATCH 010/103] Fix failing test that did direct magic access. --- IPython/core/tests/test_history.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/IPython/core/tests/test_history.py b/IPython/core/tests/test_history.py index b55a8aab588..9e5e758936f 100644 --- a/IPython/core/tests/test_history.py +++ b/IPython/core/tests/test_history.py @@ -92,7 +92,7 @@ def test_history(): # Cross testing: check that magic %save can get previous session. testfilename = os.path.realpath(os.path.join(tmpdir, "test.py")) - ip.magic_save(testfilename + " ~1/1-3") + ip.magic("save " + testfilename + " ~1/1-3") with py3compat.open(testfilename) as testfile: nt.assert_equal(testfile.read(), u"# coding: utf-8\n" + u"\n".join(hist)) From 09897796b837b71c259543caec69cea5ff429ec5 Mon Sep 17 00:00:00 2001 From: Fernando Perez Date: Tue, 8 May 2012 22:45:45 -0700 Subject: [PATCH 011/103] Fix remaining test failures. This completes the decoupling of the Magic class from the main InteractiveShell one. Finally! (it's been only 10 1/2 years...) At this point, all tests pass (modulo two failures in zmq that were there before and are not related to the magics work). We can now start the rest of the work for cell level magics, including a cleaner magic management architecture and breaking them up into several classes. --- IPython/core/magic.py | 22 +- IPython/core/tests/test_interactiveshell.py | 4 +- IPython/core/tests/test_magic.py | 12 +- IPython/core/tests/test_prefilter.py | 2 +- IPython/frontend/terminal/interactiveshell.py | 266 +++++++++--------- IPython/parallel/client/view.py | 2 +- IPython/parallel/tests/test_view.py | 28 +- 7 files changed, 176 insertions(+), 160 deletions(-) diff --git a/IPython/core/magic.py b/IPython/core/magic.py index 54bf6deaf01..d8fa23e2e3c 100644 --- a/IPython/core/magic.py +++ b/IPython/core/magic.py @@ -3045,11 +3045,11 @@ def magic_pushd(self, parameter_s=''): dir_s = self.shell.dir_stack tgt = os.path.expanduser(unquote_filename(parameter_s)) - cwd = os.getcwdu().replace(self.home_dir,'~') + cwd = os.getcwdu().replace(self.shell.home_dir,'~') if tgt: self.magic_cd(parameter_s) dir_s.insert(0,cwd) - return self.magic_dirs() + return self.shell.magic('dirs') def magic_popd(self, parameter_s=''): """Change to directory popped off the top of the stack. @@ -3417,7 +3417,7 @@ def magic_doctest_mode(self,parameter_s=''): ptformatter.pprint = False disp_formatter.plain_text_only = True - shell.magic_xmode('Plain') + shell.magic('xmode Plain') else: # turn off pm.in_template, pm.in2_template, pm.out_template = dstore.prompt_templates @@ -3432,7 +3432,7 @@ def magic_doctest_mode(self,parameter_s=''): ptformatter.pprint = dstore.rc_pprint disp_formatter.plain_text_only = dstore.rc_plain_text_only - shell.magic_xmode(dstore.xmode) + shell.magic('xmode ' + dstore.xmode) # Store new mode and inform dstore.mode = bool(1-int(mode)) @@ -3487,7 +3487,8 @@ def magic_install_ext(self, parameter_s): """ opts, args = self.parse_options(parameter_s, 'n:') try: - filename = self.extension_manager.install_extension(args, opts.get('n')) + filename = self.shell.extension_manager.install_extension(args, + opts.get('n')) except ValueError as e: print e return @@ -3499,15 +3500,15 @@ def magic_install_ext(self, parameter_s): def magic_load_ext(self, module_str): """Load an IPython extension by its module name.""" - return self.extension_manager.load_extension(module_str) + return self.shell.extension_manager.load_extension(module_str) def magic_unload_ext(self, module_str): """Unload an IPython extension by its module name.""" - self.extension_manager.unload_extension(module_str) + self.shell.extension_manager.unload_extension(module_str) def magic_reload_ext(self, module_str): """Reload an IPython extension by its module name.""" - self.extension_manager.reload_extension(module_str) + self.shell.extension_manager.reload_extension(module_str) def magic_install_profiles(self, s): """%install_profiles has been deprecated.""" @@ -3681,9 +3682,10 @@ def magic_notebook(self, s): if args.export: fname, name, format = current.parse_filename(args.filename) cells = [] - hist = list(self.history_manager.get_range()) + hist = list(self.shell.history_manager.get_range()) for session, prompt_number, input in hist[:-1]: - cells.append(current.new_code_cell(prompt_number=prompt_number, input=input)) + cells.append(current.new_code_cell(prompt_number=prompt_number, + input=input)) worksheet = current.new_worksheet(cells=cells) nb = current.new_notebook(name=name,worksheets=[worksheet]) with io.open(fname, 'w', encoding='utf-8') as f: diff --git a/IPython/core/tests/test_interactiveshell.py b/IPython/core/tests/test_interactiveshell.py index 21008e77533..2ae07cfe7da 100644 --- a/IPython/core/tests/test_interactiveshell.py +++ b/IPython/core/tests/test_interactiveshell.py @@ -343,7 +343,7 @@ def tearDown(self): def test_1(self): """Test safe_execfile with non-ascii path """ - _ip.shell.safe_execfile(self.fname, {}, raise_exceptions=True) + _ip.safe_execfile(self.fname, {}, raise_exceptions=True) class TestSystemRaw(unittest.TestCase): @@ -351,7 +351,7 @@ def test_1(self): """Test system_raw with non-ascii cmd """ cmd = ur'''python -c "'åäö'" ''' - _ip.shell.system_raw(cmd) + _ip.system_raw(cmd) def test__IPYTHON__(): diff --git a/IPython/core/tests/test_magic.py b/IPython/core/tests/test_magic.py index 530085a70ff..04664745a44 100644 --- a/IPython/core/tests/test_magic.py +++ b/IPython/core/tests/test_magic.py @@ -51,7 +51,7 @@ def test_magic_parse_options(): """Test that we don't mangle paths when parsing magic options.""" ip = get_ipython() path = 'c:\\x' - opts = ip.parse_options('-f %s' % path,'f:')[0] + opts = ip._magic.parse_options('-f %s' % path,'f:')[0] # argv splitting is os-dependent if os.name == 'posix': expected = 'c:x' @@ -284,8 +284,8 @@ def test_parse_options(): """Tests for basic options parsing in magics.""" # These are only the most minimal of tests, more should be added later. At # the very least we check that basic text/unicode calls work OK. - nt.assert_equal(_ip.parse_options('foo', '')[1], 'foo') - nt.assert_equal(_ip.parse_options(u'foo', '')[1], u'foo') + nt.assert_equal(_ip._magic.parse_options('foo', '')[1], 'foo') + nt.assert_equal(_ip._magic.parse_options(u'foo', '')[1], u'foo') def test_dirops(): @@ -326,7 +326,7 @@ def __repr__(self): _ip.run_cell("a") nt.assert_equal(monitor, []) - _ip.magic_reset("-f") + _ip.magic("reset -f") nt.assert_equal(monitor, [1]) class TestXdel(tt.TempFileMixin): @@ -388,7 +388,7 @@ def __repr__(self): def doctest_precision(): """doctest for %precision - In [1]: f = get_ipython().shell.display_formatter.formatters['text/plain'] + In [1]: f = get_ipython().display_formatter.formatters['text/plain'] In [2]: %precision 5 Out[2]: {u}'%.5f' @@ -422,7 +422,7 @@ def test_timeit_arguments(): "Test valid timeit arguments, should not cause SyntaxError (GH #1269)" _ip.magic("timeit ('#')") -@dec.skipif(_ip.magic_prun == _ip.profile_missing_notice) +@dec.skipif(_ip._magic.magic_prun == _ip._magic.profile_missing_notice) def test_prun_quotes(): "Test that prun does not clobber string escapes (GH #1302)" _ip.magic("prun -q x = '\t'") diff --git a/IPython/core/tests/test_prefilter.py b/IPython/core/tests/test_prefilter.py index 4da06b89840..d14aba1ff61 100644 --- a/IPython/core/tests/test_prefilter.py +++ b/IPython/core/tests/test_prefilter.py @@ -79,7 +79,7 @@ def test_issue_114(): msp = ip.prefilter_manager.multi_line_specials ip.prefilter_manager.multi_line_specials = False try: - for mgk in ip.lsmagic(): + for mgk in ip._magic.lsmagic(): raw = template % mgk yield nt.assert_equals(ip.prefilter(raw), raw) finally: diff --git a/IPython/frontend/terminal/interactiveshell.py b/IPython/frontend/terminal/interactiveshell.py index 49c88dd36e9..58732fb82c5 100644 --- a/IPython/frontend/terminal/interactiveshell.py +++ b/IPython/frontend/terminal/interactiveshell.py @@ -120,6 +120,133 @@ def rerun_pasted(shell, name='pasted_block'): shell.run_cell(b) +#------------------------------------------------------------------------ +# Terminal-specific magics +#------------------------------------------------------------------------ + +def magic_autoindent(self, parameter_s = ''): + """Toggle autoindent on/off (if available).""" + + self.shell.set_autoindent() + print "Automatic indentation is:",['OFF','ON'][self.shell.autoindent] + +@skip_doctest +def magic_cpaste(self, parameter_s=''): + """Paste & execute a pre-formatted code block from clipboard. + + You must terminate the block with '--' (two minus-signs) or Ctrl-D + alone on the line. You can also provide your own sentinel with '%paste + -s %%' ('%%' is the new sentinel for this operation) + + The block is dedented prior to execution to enable execution of method + definitions. '>' and '+' characters at the beginning of a line are + ignored, to allow pasting directly from e-mails, diff files and + doctests (the '...' continuation prompt is also stripped). The + executed block is also assigned to variable named 'pasted_block' for + later editing with '%edit pasted_block'. + + You can also pass a variable name as an argument, e.g. '%cpaste foo'. + This assigns the pasted block to variable 'foo' as string, without + dedenting or executing it (preceding >>> and + is still stripped) + + '%cpaste -r' re-executes the block previously entered by cpaste. + + Do not be alarmed by garbled output on Windows (it's a readline bug). + Just press enter and type -- (and press enter again) and the block + will be what was just pasted. + + IPython statements (magics, shell escapes) are not supported (yet). + + See also + -------- + paste: automatically pull code from clipboard. + + Examples + -------- + :: + + In [8]: %cpaste + Pasting code; enter '--' alone on the line to stop. + :>>> a = ["world!", "Hello"] + :>>> print " ".join(sorted(a)) + :-- + Hello world! + """ + + opts, name = self.parse_options(parameter_s, 'rs:', mode='string') + if 'r' in opts: + rerun_pasted(self.shell) + return + + sentinel = opts.get('s', '--') + block = strip_email_quotes(get_pasted_lines(sentinel)) + store_or_execute(self.shell, block, name) + + +def magic_paste(self, parameter_s=''): + """Paste & execute a pre-formatted code block from clipboard. + + The text is pulled directly from the clipboard without user + intervention and printed back on the screen before execution (unless + the -q flag is given to force quiet mode). + + The block is dedented prior to execution to enable execution of method + definitions. '>' and '+' characters at the beginning of a line are + ignored, to allow pasting directly from e-mails, diff files and + doctests (the '...' continuation prompt is also stripped). The + executed block is also assigned to variable named 'pasted_block' for + later editing with '%edit pasted_block'. + + You can also pass a variable name as an argument, e.g. '%paste foo'. + This assigns the pasted block to variable 'foo' as string, without + dedenting or executing it (preceding >>> and + is still stripped) + + Options + ------- + + -r: re-executes the block previously entered by cpaste. + + -q: quiet mode: do not echo the pasted text back to the terminal. + + IPython statements (magics, shell escapes) are not supported (yet). + + See also + -------- + cpaste: manually paste code into terminal until you mark its end. + """ + opts, name = self.parse_options(parameter_s, 'rq', mode='string') + if 'r' in opts: + rerun_pasted(self.shell) + return + try: + text = self.shell.hooks.clipboard_get() + block = strip_email_quotes(text.splitlines()) + except TryNext as clipboard_exc: + message = getattr(clipboard_exc, 'args') + if message: + error(message[0]) + else: + error('Could not get text from the clipboard.') + return + + # By default, echo back to terminal unless quiet mode is requested + if 'q' not in opts: + write = self.shell.write + write(self.shell.pycolorize(block)) + if not block.endswith('\n'): + write('\n') + write("## -- End pasted text --\n") + + store_or_execute(self.shell, block, name) + + +# Class-level: add a '%cls' magic only on Windows +if sys.platform == 'win32': + def magic_cls(self, s): + """Clear screen. + """ + os.system("cls") + #----------------------------------------------------------------------------- # Main class #----------------------------------------------------------------------------- @@ -531,134 +658,21 @@ def exit(self): else: self.ask_exit() - #------------------------------------------------------------------------ - # Magic overrides - #------------------------------------------------------------------------ - # Once the base class stops inheriting from magic, this code needs to be - # moved into a separate machinery as well. For now, at least isolate here - # the magics which this class needs to implement differently from the base - # class, or that are unique to it. - - def magic_autoindent(self, parameter_s = ''): - """Toggle autoindent on/off (if available).""" - - self.shell.set_autoindent() - print "Automatic indentation is:",['OFF','ON'][self.shell.autoindent] - - @skip_doctest - def magic_cpaste(self, parameter_s=''): - """Paste & execute a pre-formatted code block from clipboard. - - You must terminate the block with '--' (two minus-signs) or Ctrl-D - alone on the line. You can also provide your own sentinel with '%paste - -s %%' ('%%' is the new sentinel for this operation) - - The block is dedented prior to execution to enable execution of method - definitions. '>' and '+' characters at the beginning of a line are - ignored, to allow pasting directly from e-mails, diff files and - doctests (the '...' continuation prompt is also stripped). The - executed block is also assigned to variable named 'pasted_block' for - later editing with '%edit pasted_block'. - - You can also pass a variable name as an argument, e.g. '%cpaste foo'. - This assigns the pasted block to variable 'foo' as string, without - dedenting or executing it (preceding >>> and + is still stripped) - - '%cpaste -r' re-executes the block previously entered by cpaste. - - Do not be alarmed by garbled output on Windows (it's a readline bug). - Just press enter and type -- (and press enter again) and the block - will be what was just pasted. - - IPython statements (magics, shell escapes) are not supported (yet). - - See also - -------- - paste: automatically pull code from clipboard. - - Examples - -------- - :: - - In [8]: %cpaste - Pasting code; enter '--' alone on the line to stop. - :>>> a = ["world!", "Hello"] - :>>> print " ".join(sorted(a)) - :-- - Hello world! - """ - - opts, name = self.parse_options(parameter_s, 'rs:', mode='string') - if 'r' in opts: - rerun_pasted(self.shell) - return - - sentinel = opts.get('s', '--') - block = strip_email_quotes(get_pasted_lines(sentinel)) - store_or_execute(self.shell, block, name) - - def magic_paste(self, parameter_s=''): - """Paste & execute a pre-formatted code block from clipboard. - - The text is pulled directly from the clipboard without user - intervention and printed back on the screen before execution (unless - the -q flag is given to force quiet mode). - - The block is dedented prior to execution to enable execution of method - definitions. '>' and '+' characters at the beginning of a line are - ignored, to allow pasting directly from e-mails, diff files and - doctests (the '...' continuation prompt is also stripped). The - executed block is also assigned to variable named 'pasted_block' for - later editing with '%edit pasted_block'. - - You can also pass a variable name as an argument, e.g. '%paste foo'. - This assigns the pasted block to variable 'foo' as string, without - dedenting or executing it (preceding >>> and + is still stripped) - - Options - ------- - - -r: re-executes the block previously entered by cpaste. - - -q: quiet mode: do not echo the pasted text back to the terminal. - - IPython statements (magics, shell escapes) are not supported (yet). + #------------------------------------------------------------------------- + # Things related to magics + #------------------------------------------------------------------------- - See also - -------- - cpaste: manually paste code into terminal until you mark its end. - """ - opts, name = self.parse_options(parameter_s, 'rq', mode='string') - if 'r' in opts: - rerun_pasted(self.shell) - return + def init_magics(self): + super(TerminalInteractiveShell, self).init_magics() + self.define_magic('autoindent', magic_autoindent) + self.define_magic('cpaste', magic_cpaste) + self.define_magic('paste', magic_paste) try: - text = self.shell.hooks.clipboard_get() - block = strip_email_quotes(text.splitlines()) - except TryNext as clipboard_exc: - message = getattr(clipboard_exc, 'args') - if message: - error(message[0]) - else: - error('Could not get text from the clipboard.') - return - - # By default, echo back to terminal unless quiet mode is requested - if 'q' not in opts: - write = self.shell.write - write(self.shell.pycolorize(block)) - if not block.endswith('\n'): - write('\n') - write("## -- End pasted text --\n") - - store_or_execute(self.shell, block, name) - - # Class-level: add a '%cls' magic only on Windows - if sys.platform == 'win32': - def magic_cls(self, s): - """Clear screen. - """ - os.system("cls") + magic_cls + except NameError: + pass + else: + self.define_magic('cls', magic_cls) def showindentationerror(self): super(TerminalInteractiveShell, self).showindentationerror() diff --git a/IPython/parallel/client/view.py b/IPython/parallel/client/view.py index ca29e841906..90c73e44c53 100644 --- a/IPython/parallel/client/view.py +++ b/IPython/parallel/client/view.py @@ -815,7 +815,7 @@ def activate(self): else: pmagic = ip.plugin_manager.get_plugin('parallelmagic') if pmagic is None: - ip.magic_load_ext('parallelmagic') + ip.magic('load_ext parallelmagic') pmagic = ip.plugin_manager.get_plugin('parallelmagic') pmagic.active_view = self diff --git a/IPython/parallel/tests/test_view.py b/IPython/parallel/tests/test_view.py index 6c1a7a0e816..d8cc803eda6 100644 --- a/IPython/parallel/tests/test_view.py +++ b/IPython/parallel/tests/test_view.py @@ -374,21 +374,21 @@ def test_magic_px_blocking(self): v.activate() v.block=True - ip.magic_px('a=5') + ip.magic('px a=5') self.assertEquals(v['a'], 5) - ip.magic_px('a=10') + ip.magic('px a=10') self.assertEquals(v['a'], 10) sio = StringIO() savestdout = sys.stdout sys.stdout = sio # just 'print a' worst ~99% of the time, but this ensures that # the stdout message has arrived when the result is finished: - ip.magic_px('import sys,time;print (a); sys.stdout.flush();time.sleep(0.2)') + ip.magic('px import sys,time;print (a); sys.stdout.flush();time.sleep(0.2)') sys.stdout = savestdout buf = sio.getvalue() self.assertTrue('[stdout:' in buf, buf) self.assertTrue(buf.rstrip().endswith('10')) - self.assertRaisesRemote(ZeroDivisionError, ip.magic_px, '1/0') + self.assertRaisesRemote(ZeroDivisionError, ip.magic, 'px 1/0') def test_magic_px_nonblocking(self): ip = get_ipython() @@ -396,18 +396,18 @@ def test_magic_px_nonblocking(self): v.activate() v.block=False - ip.magic_px('a=5') + ip.magic('px a=5') self.assertEquals(v['a'], 5) - ip.magic_px('a=10') + ip.magic('px a=10') self.assertEquals(v['a'], 10) sio = StringIO() savestdout = sys.stdout sys.stdout = sio - ip.magic_px('print a') + ip.magic('px print a') sys.stdout = savestdout buf = sio.getvalue() self.assertFalse('[stdout:%i]'%v.targets in buf) - ip.magic_px('1/0') + ip.magic('px 1/0') ar = v.get_result(-1) self.assertRaisesRemote(ZeroDivisionError, ar.get) @@ -420,12 +420,12 @@ def test_magic_autopx_blocking(self): sio = StringIO() savestdout = sys.stdout sys.stdout = sio - ip.magic_autopx() + ip.magic('autopx') ip.run_cell('\n'.join(('a=5','b=10','c=0'))) ip.run_cell('b*=2') ip.run_cell('print (b)') ip.run_cell("b/c") - ip.magic_autopx() + ip.magic('autopx') sys.stdout = savestdout output = sio.getvalue().strip() self.assertTrue(output.startswith('%autopx enabled')) @@ -445,13 +445,13 @@ def test_magic_autopx_nonblocking(self): sio = StringIO() savestdout = sys.stdout sys.stdout = sio - ip.magic_autopx() + ip.magic('autopx') ip.run_cell('\n'.join(('a=5','b=10','c=0'))) ip.run_cell('print (b)') ip.run_cell('import time; time.sleep(0.1)') ip.run_cell("b/c") ip.run_cell('b*=2') - ip.magic_autopx() + ip.magic('autopx') sys.stdout = savestdout output = sio.getvalue().strip() self.assertTrue(output.startswith('%autopx enabled')) @@ -472,10 +472,10 @@ def test_magic_result(self): v['a'] = 111 ra = v['a'] - ar = ip.magic_result() + ar = ip.magic('result') self.assertEquals(ar.msg_ids, [v.history[-1]]) self.assertEquals(ar.get(), 111) - ar = ip.magic_result('-2') + ar = ip.magic('result -2') self.assertEquals(ar.msg_ids, [v.history[-2]]) def test_unicode_execute(self): From 6c6e057a152f0973645cd00de573159305d802ab Mon Sep 17 00:00:00 2001 From: Fernando Perez Date: Sun, 13 May 2012 13:39:33 -0700 Subject: [PATCH 012/103] Major restructuring of magics, breaking them up into separate classes. This is the first step to get the new magic architecture in place, with a new base class for magic functions. At this point IPython does *not* run, but the changes are extensive enough to warrant intermediate non-working commits. --- IPython/core/interactiveshell.py | 1 + IPython/core/magic.py | 4582 +++++++++++++++--------------- 2 files changed, 2321 insertions(+), 2262 deletions(-) diff --git a/IPython/core/interactiveshell.py b/IPython/core/interactiveshell.py index 120512c9ebb..a53a4c7d29d 100644 --- a/IPython/core/interactiveshell.py +++ b/IPython/core/interactiveshell.py @@ -380,6 +380,7 @@ def _prompt_trait_changed(self, name, old, new): plugin_manager = Instance('IPython.core.plugin.PluginManager') payload_manager = Instance('IPython.core.payload.PayloadManager') history_manager = Instance('IPython.core.history.HistoryManager') + magics_manager = Instance('IPython.core.magic.MagicsManager') profile_dir = Instance('IPython.core.application.ProfileDir') @property diff --git a/IPython/core/magic.py b/IPython/core/magic.py index d8fa23e2e3c..4bb0f5bd733 100644 --- a/IPython/core/magic.py +++ b/IPython/core/magic.py @@ -4,8 +4,8 @@ #----------------------------------------------------------------------------- # Copyright (C) 2001 Janko Hauser and -# Copyright (C) 2001-2007 Fernando Perez -# Copyright (C) 2008-2011 The IPython Development Team +# Copyright (C) 2001 Fernando Perez +# Copyright (C) 2008 The IPython Development Team # Distributed under the terms of the BSD License. The full license is in # the file COPYING, distributed as part of this software. @@ -18,14 +18,16 @@ import __builtin__ as builtin_mod import __future__ import bdb +import gc +import imp import inspect import io import json import os -import sys import re +import shutil +import sys import time -import gc from StringIO import StringIO from getopt import getopt,GetoptError from pprint import pformat @@ -42,36 +44,48 @@ except ImportError: profile = pstats = None +import IPython +from IPython.config.application import Application +from IPython.config.configurable import Configurable from IPython.core import debugger, oinspect +from IPython.core import magic_arguments, page +from IPython.core.error import StdinNotImplementedError from IPython.core.error import TryNext from IPython.core.error import UsageError -from IPython.core.error import StdinNotImplementedError +from IPython.core.fakemodule import FakeModule from IPython.core.macro import Macro -from IPython.core import magic_arguments, page from IPython.core.prefilter import ESC_MAGIC +from IPython.core.profiledir import ProfileDir from IPython.testing.skipdoctest import skip_doctest +from IPython.utils import openpy from IPython.utils import py3compat from IPython.utils.encoding import DEFAULT_ENCODING from IPython.utils.io import file_read, nlprint +from IPython.utils.ipstruct import Struct from IPython.utils.module_paths import find_mod from IPython.utils.path import get_py_filename, unquote_filename from IPython.utils.process import arg_split, abbrev_cwd from IPython.utils.terminal import set_term_title from IPython.utils.text import format_screen from IPython.utils.timing import clock, clock2 +from IPython.utils.traitlets import Bool, Dict, Instance, Integer, List, Unicode from IPython.utils.warn import warn, error -from IPython.utils.ipstruct import Struct -from IPython.config.application import Application #----------------------------------------------------------------------------- -# Utility functions +# Utility classes and functions #----------------------------------------------------------------------------- +class Bunch: pass + + +# Used for exception handling in magic_edit +class MacroToEdit(ValueError): pass + + def on_off(tag): """Return an ON/OFF string for a 1/0 input. Simple utility function.""" return ['OFF','ON'][tag] -class Bunch: pass def compress_dhist(dh): head, tail = dh[:-10], dh[-10:] @@ -86,72 +100,28 @@ def compress_dhist(dh): return newhead + tail + def needs_local_scope(func): """Decorator to mark magic functions which need to local scope to run.""" func.needs_local_scope = True return func - -# Used for exception handling in magic_edit -class MacroToEdit(ValueError): pass - #*************************************************************************** -# Main class implementing Magic functionality - -# XXX - for some odd reason, if Magic is made a new-style class, we get errors -# on construction of the main InteractiveShell object. Something odd is going -# on with super() calls, Configurable and the MRO... For now leave it as-is, but -# eventually this needs to be clarified. -# BG: This is because InteractiveShell inherits from this, but is itself a -# Configurable. This messes up the MRO in some way. The fix is that we need to -# make Magic a configurable that InteractiveShell does not subclass. - -class Magic(object): - """Magic functions for InteractiveShell. - - Shell functions which can be reached as %function_name. All magic - functions should accept a string, which they can parse for their own - needs. This can make some functions easier to type, eg `%cd ../` - vs. `%cd("../")` - - ALL definitions MUST begin with the prefix magic_. The user won't need it - at the command line, but it is is needed in the definition. """ - # class globals - auto_status = ['Automagic is OFF, % prefix IS needed for magic functions.', - 'Automagic is ON, % prefix NOT needed for magic functions.'] +class MagicManager(Configurable): + """Object that handles all magic-related functionality for IPython. + """ + # An instance of the IPython shell we are attached to + shell = Instance('IPython.core.interactiveshell.InteractiveShellABC') + auto_status = Enum([ + 'Automagic is OFF, % prefix IS needed for magic functions.', + 'Automagic is ON, % prefix NOT needed for magic functions.']) - configurables = None + def __init__(self, shell=None, config=None, **traits): - default_runner = None - #...................................................................... - # some utility functions + super(MagicManager, self).__init__(shell=shell, config=config, **traits) - def __init__(self, shell): - - self.options_table = {} - if profile is None: - self.magic_prun = self.profile_missing_notice - self.shell = shell - if self.configurables is None: - self.configurables = [] - - # namespace for holding state we may need - self._magic_state = Bunch() - - def profile_missing_notice(self, *args, **kwargs): - error("""\ -The profile module could not be found. It has been removed from the standard -python packages because of its non-free license. To use profiling, install the -python-profiler package from non-free.""") - - def default_option(self,fn,optstr): - """Make an entry in the options_table for fn, with value optstr""" - - if fn not in self.lsmagic(): - error("%s is not a magic function" % fn) - self.options_table[fn] = optstr def lsmagic(self): """Return a list of currently available magic functions. @@ -170,15 +140,39 @@ def lsmagic(self): # and bound magics by user (so they can access self): inst_bound_magic = lambda fn: fn.startswith('magic_') and \ callable(self.__class__.__dict__[fn]) - magics = filter(class_magic,Magic.__dict__.keys()) + \ + magics = filter(class_magic, Magic.__dict__.keys()) + \ filter(inst_magic, self.__dict__.keys()) + \ filter(inst_bound_magic, self.__class__.__dict__.keys()) out = [] for fn in set(magics): - out.append(fn.replace('magic_','',1)) + out.append(fn.replace('magic_', '', 1)) out.sort() return out + +class MagicFunctions(object): + """Base class for implementing magic functions. + + Shell functions which can be reached as %function_name. All magic + functions should accept a string, which they can parse for their own + needs. This can make some functions easier to type, eg `%cd ../` + vs. `%cd("../")` + """ + + options_table = Dict(config=True, + help = """Dict holding all command-line options for each magic. + """) + + class __metaclass__(type): + def __new__(cls, name, bases, dct): + cls.registered = False + return type.__new__(cls, name, bases, dct) + + def __init__(self, shell): + if not(self.__class__.registered): + raise ValueError('unregistered Magics') + self.shell = shell + def arg_err(self,func): """Print docstring if incorrect arguments were passed""" print 'Error in arguments:' @@ -211,7 +205,7 @@ def format_latex(self,strng): strng = newline_re.sub(r'\\textbackslash{}n',strng) return strng - def parse_options(self,arg_str,opt_str,*long_opts,**kw): + def parse_options(self, arg_str, opt_str, *long_opts, **kw): """Parse options passed to an argument string. The interface is similar to that of getopt(), but it returns back a @@ -280,10 +274,20 @@ def parse_options(self,arg_str,opt_str,*long_opts,**kw): return opts,args - #...................................................................... - # And now the actual magic functions + def default_option(self,fn,optstr): + """Make an entry in the options_table for fn, with value optstr""" + + if fn not in self.lsmagic(): + error("%s is not a magic function" % fn) + self.options_table[fn] = optstr + + +class BasicMagics(MagicFunctions): + """Magics that provide central IPython functionality. + + These are various magics that don't fit into specific categories but that + are all part of the base 'IPython experience'.""" - # Functions for IPython shell work (vars,funcs, config, etc) def magic_lsmagic(self, parameter_s = ''): """List currently available magic functions.""" mesc = ESC_MAGIC @@ -383,99 +387,6 @@ def magic_magic(self, parameter_s = ''): Magic.auto_status[self.shell.automagic] ) ) page.page(outmsg) - def magic_automagic(self, parameter_s = ''): - """Make magic functions callable without having to type the initial %. - - Without argumentsl toggles on/off (when off, you must call it as - %automagic, of course). With arguments it sets the value, and you can - use any of (case insensitive): - - - on,1,True: to activate - - - off,0,False: to deactivate. - - Note that magic functions have lowest priority, so if there's a - variable whose name collides with that of a magic fn, automagic won't - work for that function (you get the variable instead). However, if you - delete the variable (del var), the previously shadowed magic function - becomes visible to automagic again.""" - - arg = parameter_s.lower() - if parameter_s in ('on','1','true'): - self.shell.automagic = True - elif parameter_s in ('off','0','false'): - self.shell.automagic = False - else: - self.shell.automagic = not self.shell.automagic - print '\n' + Magic.auto_status[self.shell.automagic] - - @skip_doctest - def magic_autocall(self, parameter_s = ''): - """Make functions callable without having to type parentheses. - - Usage: - - %autocall [mode] - - The mode can be one of: 0->Off, 1->Smart, 2->Full. If not given, the - value is toggled on and off (remembering the previous state). - - In more detail, these values mean: - - 0 -> fully disabled - - 1 -> active, but do not apply if there are no arguments on the line. - - In this mode, you get:: - - In [1]: callable - Out[1]: - - In [2]: callable 'hello' - ------> callable('hello') - Out[2]: False - - 2 -> Active always. Even if no arguments are present, the callable - object is called:: - - In [2]: float - ------> float() - Out[2]: 0.0 - - Note that even with autocall off, you can still use '/' at the start of - a line to treat the first argument on the command line as a function - and add parentheses to it:: - - In [8]: /str 43 - ------> str(43) - Out[8]: '43' - - # all-random (note for auto-testing) - """ - - if parameter_s: - arg = int(parameter_s) - else: - arg = 'toggle' - - if not arg in (0,1,2,'toggle'): - error('Valid modes: (0->Off, 1->Smart, 2->Full') - return - - if arg in (0,1,2): - self.shell.autocall = arg - else: # toggle - if self.shell.autocall: - self._magic_state.autocall_save = self.shell.autocall - self.shell.autocall = 0 - else: - try: - self.shell.autocall = self._magic_state.autocall_save - except AttributeError: - self.shell.autocall = self._magic_state.autocall_save = 1 - - print "Automatic calling is:",['OFF','Smart','Full'][self.shell.autocall] - def magic_page(self, parameter_s=''): """Pretty print the object and display it through a pager. @@ -510,2214 +421,2572 @@ def magic_profile(self, parameter_s=''): else: error("profile is an application-level value, but you don't appear to be in an IPython application") - def magic_pinfo(self, parameter_s='', namespaces=None): - """Provide detailed information about an object. - - '%pinfo object' is just a synonym for object? or ?object.""" - - #print 'pinfo par: <%s>' % parameter_s # dbg - - - # detail_level: 0 -> obj? , 1 -> obj?? - detail_level = 0 - # We need to detect if we got called as 'pinfo pinfo foo', which can - # happen if the user types 'pinfo foo?' at the cmd line. - pinfo,qmark1,oname,qmark2 = \ - re.match('(pinfo )?(\?*)(.*?)(\??$)',parameter_s).groups() - if pinfo or qmark1 or qmark2: - detail_level = 1 - if "*" in oname: - self.magic_psearch(oname) - else: - self.shell._inspect('pinfo', oname, detail_level=detail_level, - namespaces=namespaces) - - def magic_pinfo2(self, parameter_s='', namespaces=None): - """Provide extra detailed information about an object. + def magic_pprint(self, parameter_s=''): + """Toggle pretty printing on/off.""" + ptformatter = self.shell.display_formatter.formatters['text/plain'] + ptformatter.pprint = bool(1 - ptformatter.pprint) + print 'Pretty printing has been turned', \ + ['OFF','ON'][ptformatter.pprint] - '%pinfo2 object' is just a synonym for object?? or ??object.""" - self.shell._inspect('pinfo', parameter_s, detail_level=1, - namespaces=namespaces) + def magic_colors(self,parameter_s = ''): + """Switch color scheme for prompts, info system and exception handlers. - @skip_doctest - def magic_pdef(self, parameter_s='', namespaces=None): - """Print the definition header for any callable object. + Currently implemented schemes: NoColor, Linux, LightBG. - If the object is a class, print the constructor information. + Color scheme names are not case-sensitive. Examples -------- - :: + To get a plain black and white terminal:: - In [3]: %pdef urllib.urlopen - urllib.urlopen(url, data=None, proxies=None) + %colors nocolor """ - self._inspect('pdef',parameter_s, namespaces) - def magic_pdoc(self, parameter_s='', namespaces=None): - """Print the docstring for an object. + def color_switch_err(name): + warn('Error changing %s color schemes.\n%s' % + (name,sys.exc_info()[1])) - If the given object is a class, it will print both the class and the - constructor docstrings.""" - self._inspect('pdoc',parameter_s, namespaces) - def magic_psource(self, parameter_s='', namespaces=None): - """Print (or run through pager) the source code for an object.""" - self._inspect('psource',parameter_s, namespaces) + new_scheme = parameter_s.strip() + if not new_scheme: + raise UsageError( + "%colors: you must specify a color scheme. See '%colors?'") + return + # local shortcut + shell = self.shell - def magic_pfile(self, parameter_s=''): - """Print (or run through pager) the file where an object is defined. + import IPython.utils.rlineimpl as readline - The file opens at the line where the object definition begins. IPython - will honor the environment variable PAGER if set, and otherwise will - do its best to print the file in a convenient form. + if not shell.colors_force and \ + not readline.have_readline and sys.platform == "win32": + msg = """\ +Proper color support under MS Windows requires the pyreadline library. +You can find it at: +http://ipython.org/pyreadline.html +Gary's readline needs the ctypes module, from: +http://starship.python.net/crew/theller/ctypes +(Note that ctypes is already part of Python versions 2.5 and newer). - If the given argument is not an object currently defined, IPython will - try to interpret it as a filename (automatically adding a .py extension - if needed). You can thus use %pfile as a syntax highlighting code - viewer.""" +Defaulting color scheme to 'NoColor'""" + new_scheme = 'NoColor' + warn(msg) - # first interpret argument as an object name - out = self._inspect('pfile',parameter_s) - # if not, try the input as a filename - if out == 'not found': + # readline option is 0 + if not shell.colors_force and not shell.has_readline: + new_scheme = 'NoColor' + + # Set prompt colors + try: + shell.prompt_manager.color_scheme = new_scheme + except: + color_switch_err('prompt') + else: + shell.colors = \ + shell.prompt_manager.color_scheme_table.active_scheme_name + # Set exception colors + try: + shell.InteractiveTB.set_colors(scheme = new_scheme) + shell.SyntaxTB.set_colors(scheme = new_scheme) + except: + color_switch_err('exception') + + # Set info (for 'object?') colors + if shell.color_info: try: - filename = get_py_filename(parameter_s) - except IOError,msg: - print msg - return - page.page(self.shell.inspector.format(open(filename).read())) + shell.inspector.set_active_scheme(new_scheme) + except: + color_switch_err('object inspector') + else: + shell.inspector.set_active_scheme('NoColor') - def magic_psearch(self, parameter_s=''): - """Search for object in namespaces by wildcard. + def magic_xmode(self,parameter_s = ''): + """Switch modes for the exception handlers. - %psearch [options] PATTERN [OBJECT TYPE] + Valid modes: Plain, Context and Verbose. - Note: ? can be used as a synonym for %psearch, at the beginning or at - the end: both a*? and ?a* are equivalent to '%psearch a*'. Still, the - rest of the command line must be unchanged (options come first), so - for example the following forms are equivalent + If called without arguments, acts as a toggle.""" - %psearch -i a* function - -i a* function? - ?-i a* function + def xmode_switch_err(name): + warn('Error changing %s exception modes.\n%s' % + (name,sys.exc_info()[1])) - Arguments: + shell = self.shell + new_mode = parameter_s.strip().capitalize() + try: + shell.InteractiveTB.set_mode(mode=new_mode) + print 'Exception reporting mode:',shell.InteractiveTB.mode + except: + xmode_switch_err('user') - PATTERN + def magic_quickref(self,arg): + """ Show a quick reference sheet """ + import IPython.core.usage + qr = IPython.core.usage.quick_reference + self.magic_magic('-brief') + page.page(qr) - where PATTERN is a string containing * as a wildcard similar to its - use in a shell. The pattern is matched in all namespaces on the - search path. By default objects starting with a single _ are not - matched, many IPython generated objects have a single - underscore. The default is case insensitive matching. Matching is - also done on the attributes of objects and not only on the objects - in a module. + def magic_doctest_mode(self,parameter_s=''): + """Toggle doctest mode on and off. - [OBJECT TYPE] + This mode is intended to make IPython behave as much as possible like a + plain Python shell, from the perspective of how its prompts, exceptions + and output look. This makes it easy to copy and paste parts of a + session into doctests. It does so by: - Is the name of a python type from the types module. The name is - given in lowercase without the ending type, ex. StringType is - written string. By adding a type here only objects matching the - given type are matched. Using all here makes the pattern match all - types (this is the default). + - Changing the prompts to the classic ``>>>`` ones. + - Changing the exception reporting mode to 'Plain'. + - Disabling pretty-printing of output. - Options: + Note that IPython also supports the pasting of code snippets that have + leading '>>>' and '...' prompts in them. This means that you can paste + doctests from files or docstrings (even if they have leading + whitespace), and the code will execute correctly. You can then use + '%history -t' to see the translated history; this will give you the + input after removal of all the leading prompts and whitespace, which + can be pasted back into an editor. - -a: makes the pattern match even objects whose names start with a - single underscore. These names are normally omitted from the - search. + With these features, you can switch into this mode easily whenever you + need to do testing and changes to doctests, without having to leave + your existing IPython session. + """ - -i/-c: make the pattern case insensitive/sensitive. If neither of - these options are given, the default is read from your configuration - file, with the option ``InteractiveShell.wildcards_case_sensitive``. - If this option is not specified in your configuration file, IPython's - internal default is to do a case sensitive search. + from IPython.utils.ipstruct import Struct - -e/-s NAMESPACE: exclude/search a given namespace. The pattern you - specify can be searched in any of the following namespaces: - 'builtin', 'user', 'user_global','internal', 'alias', where - 'builtin' and 'user' are the search defaults. Note that you should - not use quotes when specifying namespaces. + # Shorthands + shell = self.shell + pm = shell.prompt_manager + meta = shell.meta + disp_formatter = self.shell.display_formatter + ptformatter = disp_formatter.formatters['text/plain'] + # dstore is a data store kept in the instance metadata bag to track any + # changes we make, so we can undo them later. + dstore = meta.setdefault('doctest_mode',Struct()) + save_dstore = dstore.setdefault - 'Builtin' contains the python module builtin, 'user' contains all - user data, 'alias' only contain the shell aliases and no python - objects, 'internal' contains objects used by IPython. The - 'user_global' namespace is only used by embedded IPython instances, - and it contains module-level globals. You can add namespaces to the - search with -s or exclude them with -e (these options can be given - more than once). + # save a few values we'll need to recover later + mode = save_dstore('mode',False) + save_dstore('rc_pprint',ptformatter.pprint) + save_dstore('xmode',shell.InteractiveTB.mode) + save_dstore('rc_separate_out',shell.separate_out) + save_dstore('rc_separate_out2',shell.separate_out2) + save_dstore('rc_prompts_pad_left',pm.justify) + save_dstore('rc_separate_in',shell.separate_in) + save_dstore('rc_plain_text_only',disp_formatter.plain_text_only) + save_dstore('prompt_templates',(pm.in_template, pm.in2_template, pm.out_template)) - Examples - -------- - :: + if mode == False: + # turn on + pm.in_template = '>>> ' + pm.in2_template = '... ' + pm.out_template = '' - %psearch a* -> objects beginning with an a - %psearch -e builtin a* -> objects NOT in the builtin space starting in a - %psearch a* function -> all functions beginning with an a - %psearch re.e* -> objects beginning with an e in module re - %psearch r*.e* -> objects that start with e in modules starting in r - %psearch r*.* string -> all strings in modules beginning with r + # Prompt separators like plain python + shell.separate_in = '' + shell.separate_out = '' + shell.separate_out2 = '' - Case sensitive search:: + pm.justify = False - %psearch -c a* list all object beginning with lower case a + ptformatter.pprint = False + disp_formatter.plain_text_only = True - Show objects beginning with a single _:: + shell.magic('xmode Plain') + else: + # turn off + pm.in_template, pm.in2_template, pm.out_template = dstore.prompt_templates - %psearch -a _* list objects beginning with a single underscore""" - try: - parameter_s.encode('ascii') - except UnicodeEncodeError: - print 'Python identifiers can only contain ascii characters.' - return + shell.separate_in = dstore.rc_separate_in - # default namespaces to be searched - def_search = ['user_local', 'user_global', 'builtin'] + shell.separate_out = dstore.rc_separate_out + shell.separate_out2 = dstore.rc_separate_out2 - # Process options/args - opts,args = self.parse_options(parameter_s,'cias:e:',list_all=True) - opt = opts.get - shell = self.shell - psearch = shell.inspector.psearch + pm.justify = dstore.rc_prompts_pad_left - # select case options - if opts.has_key('i'): - ignore_case = True - elif opts.has_key('c'): - ignore_case = False - else: - ignore_case = not shell.wildcards_case_sensitive + ptformatter.pprint = dstore.rc_pprint + disp_formatter.plain_text_only = dstore.rc_plain_text_only - # Build list of namespaces to search from user options - def_search.extend(opt('s',[])) - ns_exclude = ns_exclude=opt('e',[]) - ns_search = [nm for nm in def_search if nm not in ns_exclude] + shell.magic('xmode ' + dstore.xmode) - # Call the actual search + # Store new mode and inform + dstore.mode = bool(1-int(mode)) + mode_label = ['OFF','ON'][dstore.mode] + print 'Doctest mode is:', mode_label + + def magic_gui(self, parameter_s=''): + """Enable or disable IPython GUI event loop integration. + + %gui [GUINAME] + + This magic replaces IPython's threaded shells that were activated + using the (pylab/wthread/etc.) command line flags. GUI toolkits + can now be enabled at runtime and keyboard + interrupts should work without any problems. The following toolkits + are supported: wxPython, PyQt4, PyGTK, Tk and Cocoa (OSX):: + + %gui wx # enable wxPython event loop integration + %gui qt4|qt # enable PyQt4 event loop integration + %gui gtk # enable PyGTK event loop integration + %gui gtk3 # enable Gtk3 event loop integration + %gui tk # enable Tk event loop integration + %gui OSX # enable Cocoa event loop integration + # (requires %matplotlib 1.1) + %gui # disable all event loop integration + + WARNING: after any of these has been called you can simply create + an application object, but DO NOT start the event loop yourself, as + we have already handled that. + """ + opts, arg = self.parse_options(parameter_s, '') + if arg=='': arg = None try: - psearch(args,shell.ns_table,ns_search, - show_all=opt('a'),ignore_case=ignore_case) - except: - shell.showtraceback() + return self.enable_gui(arg) + except Exception as e: + # print simple error message, rather than traceback if we can't + # hook up the GUI + error(str(e)) @skip_doctest - def magic_who_ls(self, parameter_s=''): - """Return a sorted list of all interactive variables. + def magic_precision(self, s=''): + """Set floating point precision for pretty printing. - If arguments are given, only variables of types matching these - arguments are returned. + Can set either integer precision or a format string. + + If numpy has been imported and precision is an int, + numpy display precision will also be set, via ``numpy.set_printoptions``. + + If no argument is given, defaults will be restored. Examples -------- + :: - Define two variables and list them with who_ls:: + In [1]: from math import pi - In [1]: alpha = 123 + In [2]: %precision 3 + Out[2]: u'%.3f' - In [2]: beta = 'test' + In [3]: pi + Out[3]: 3.142 - In [3]: %who_ls - Out[3]: ['alpha', 'beta'] + In [4]: %precision %i + Out[4]: u'%i' - In [4]: %who_ls int - Out[4]: ['alpha'] + In [5]: pi + Out[5]: 3 - In [5]: %who_ls str - Out[5]: ['beta'] - """ + In [6]: %precision %e + Out[6]: u'%e' - user_ns = self.shell.user_ns - user_ns_hidden = self.shell.user_ns_hidden - out = [ i for i in user_ns - if not i.startswith('_') \ - and not i in user_ns_hidden ] + In [7]: pi**10 + Out[7]: 9.364805e+04 - typelist = parameter_s.split() - if typelist: - typeset = set(typelist) - out = [i for i in out if type(user_ns[i]).__name__ in typeset] + In [8]: %precision + Out[8]: u'%r' - out.sort() - return out + In [9]: pi**10 + Out[9]: 93648.047476082982 + """ + ptformatter = self.shell.display_formatter.formatters['text/plain'] + ptformatter.float_precision = s + return ptformatter.float_format - @skip_doctest - def magic_who(self, parameter_s=''): - """Print all interactive variables, with some minimal formatting. + @magic_arguments.magic_arguments() + @magic_arguments.argument( + '-e', '--export', action='store_true', default=False, + help='Export IPython history as a notebook. The filename argument ' + 'is used to specify the notebook name and format. For example ' + 'a filename of notebook.ipynb will result in a notebook name ' + 'of "notebook" and a format of "xml". Likewise using a ".json" ' + 'or ".py" file extension will write the notebook in the json ' + 'or py formats.' + ) + @magic_arguments.argument( + '-f', '--format', + help='Convert an existing IPython notebook to a new format. This option ' + 'specifies the new format and can have the values: xml, json, py. ' + 'The target filename is chosen automatically based on the new ' + 'format. The filename argument gives the name of the source file.' + ) + @magic_arguments.argument( + 'filename', type=unicode, + help='Notebook name or filename' + ) + def magic_notebook(self, s): + """Export and convert IPython notebooks. - If any arguments are given, only variables whose type matches one of - these are printed. For example:: + This function can export the current IPython history to a notebook file + or can convert an existing notebook file into a different format. For + example, to export the history to "foo.ipynb" do "%notebook -e foo.ipynb". + To export the history to "foo.py" do "%notebook -e foo.py". To convert + "foo.ipynb" to "foo.json" do "%notebook -f json foo.ipynb". Possible + formats include (json/ipynb, py). + """ + args = magic_arguments.parse_argstring(self.magic_notebook, s) - %who function str + from IPython.nbformat import current + args.filename = unquote_filename(args.filename) + if args.export: + fname, name, format = current.parse_filename(args.filename) + cells = [] + hist = list(self.shell.history_manager.get_range()) + for session, prompt_number, input in hist[:-1]: + cells.append(current.new_code_cell(prompt_number=prompt_number, + input=input)) + worksheet = current.new_worksheet(cells=cells) + nb = current.new_notebook(name=name,worksheets=[worksheet]) + with io.open(fname, 'w', encoding='utf-8') as f: + current.write(nb, f, format); + elif args.format is not None: + old_fname, old_name, old_format = current.parse_filename(args.filename) + new_format = args.format + if new_format == u'xml': + raise ValueError('Notebooks cannot be written as xml.') + elif new_format == u'ipynb' or new_format == u'json': + new_fname = old_name + u'.ipynb' + new_format = u'json' + elif new_format == u'py': + new_fname = old_name + u'.py' + else: + raise ValueError('Invalid notebook format: %s' % new_format) + with io.open(old_fname, 'r', encoding='utf-8') as f: + nb = current.read(f, old_format) + with io.open(new_fname, 'w', encoding='utf-8') as f: + current.write(nb, f, new_format) - will only list functions and strings, excluding all other types of - variables. To find the proper type names, simply use type(var) at a - command line to see how python prints type names. For example: - :: +class CodeMagics(MagicFunctions): + """Magics related to code management (loading, saving, editing, ...).""" - In [1]: type('hello')\\ - Out[1]: + def magic_save(self,parameter_s = ''): + """Save a set of lines or a macro to a given filename. - indicates that the type name for strings is 'str'. + Usage:\\ + %save [options] filename n1-n2 n3-n4 ... n5 .. n6 ... - ``%who`` always excludes executed names loaded through your configuration - file and things which are internal to IPython. + Options: - This is deliberate, as typically you may load many modules and the - purpose of %who is to show you only what you've manually defined. + -r: use 'raw' input. By default, the 'processed' history is used, + so that magics are loaded in their transformed version to valid + Python. If this option is given, the raw input as typed as the + command line is used instead. - Examples - -------- + This function uses the same syntax as %history for input ranges, + then saves the lines to the filename you specify. - Define two variables and list them with who:: + It adds a '.py' extension to the file if you don't do so yourself, and + it asks for confirmation before overwriting existing files.""" - In [1]: alpha = 123 + opts,args = self.parse_options(parameter_s,'r',mode='list') + fname, codefrom = unquote_filename(args[0]), " ".join(args[1:]) + if not fname.endswith('.py'): + fname += '.py' + if os.path.isfile(fname): + ans = raw_input('File `%s` exists. Overwrite (y/[N])? ' % fname) + if ans.lower() not in ['y','yes']: + print 'Operation cancelled.' + return + try: + cmds = self.shell.find_user_code(codefrom, 'r' in opts) + except (TypeError, ValueError) as e: + print e.args[0] + return + with io.open(fname,'w', encoding="utf-8") as f: + f.write(u"# coding: utf-8\n") + f.write(py3compat.cast_unicode(cmds)) + print 'The following commands were written to file `%s`:' % fname + print cmds - In [2]: beta = 'test' + def magic_pastebin(self, parameter_s = ''): + """Upload code to Github's Gist paste bin, returning the URL. - In [3]: %who - alpha beta + Usage:\\ + %pastebin [-d "Custom description"] 1-7 - In [4]: %who int - alpha + The argument can be an input history range, a filename, or the name of a + string or macro. - In [5]: %who str - beta + Options: + + -d: Pass a custom description for the gist. The default will say + "Pasted from IPython". """ + opts, args = self.parse_options(parameter_s, 'd:') - varlist = self.magic_who_ls(parameter_s) - if not varlist: - if parameter_s: - print 'No variables match your requested type.' - else: - print 'Interactive namespace is empty.' + try: + code = self.shell.find_user_code(args) + except (ValueError, TypeError) as e: + print e.args[0] return - # if we have variables, move on... - count = 0 - for i in varlist: - print i+'\t', - count += 1 - if count > 8: - count = 0 - print - print - - @skip_doctest - def magic_whos(self, parameter_s=''): - """Like %who, but gives some extra information about each variable. + post_data = json.dumps({ + "description": opts.get('d', "Pasted from IPython"), + "public": True, + "files": { + "file1.py": { + "content": code + } + } + }).encode('utf-8') - The same type filtering of %who can be applied here. + response = urlopen("https://api.github.com/gists", post_data) + response_data = json.loads(response.read().decode('utf-8')) + return response_data['html_url'] - For all variables, the type is printed. Additionally it prints: + def magic_loadpy(self, arg_s): + """Alias of `%load` + + `%loadpy` has gained some flexibility and droped the requirement of a `.py` + extension. So it has been renamed simply into %load. You can look at + `%load`'s docstring for more info. + """ + self.magic_load(arg_s) - - For {},[],(): their length. + def magic_load(self, arg_s): + """Load code into the current frontend. - - For numpy arrays, a summary with shape, number of - elements, typecode and size in memory. + Usage:\\ + %load [options] source - - Everything else: a string representation, snipping their middle if - too long. + where source can be a filename, URL, input history range or macro - Examples + Options: -------- + -y : Don't ask confirmation for loading source above 200 000 characters. - Define two variables and list them with whos:: - - In [1]: alpha = 123 - - In [2]: beta = 'test' + This magic command can either take a local filename, a URL, an history + range (see %history) or a macro as argument, it will prompt for + confirmation before loading source with more than 200 000 characters, unless + -y flag is passed or if the frontend does not support raw_input:: - In [3]: %whos - Variable Type Data/Info - -------------------------------- - alpha int 123 - beta str test + %load myscript.py + %load 7-27 + %load myMacro + %load http://www.example.com/myscript.py """ + opts,args = self.parse_options(arg_s,'y') - varnames = self.magic_who_ls(parameter_s) - if not varnames: - if parameter_s: - print 'No variables match your requested type.' - else: - print 'Interactive namespace is empty.' - return - - # if we have variables, move on... + contents = self.shell.find_user_code(args) + l = len(contents) - # for these types, show len() instead of data: - seq_types = ['dict', 'list', 'tuple'] + # 200 000 is ~ 2500 full 80 caracter lines + # so in average, more than 5000 lines + if l > 200000 and 'y' not in opts: + try: + ans = self.shell.ask_yes_no(("The text you're trying to load seems pretty big"\ + " (%d characters). Continue (y/[N]) ?" % l), default='n' ) + except StdinNotImplementedError: + #asume yes if raw input not implemented + ans = True - # for numpy arrays, display summary info - ndarray_type = None - if 'numpy' in sys.modules: + if ans is False : + print 'Operation cancelled.' + return + + self.set_next_input(contents) + + def _find_edit_target(self, args, opts, last_call): + """Utility method used by magic_edit to find what to edit.""" + + def make_filename(arg): + "Make a filename from the given args" + arg = unquote_filename(arg) try: - from numpy import ndarray - except ImportError: - pass - else: - ndarray_type = ndarray.__name__ + filename = get_py_filename(arg) + except IOError: + # If it ends with .py but doesn't already exist, assume we want + # a new file. + if arg.endswith('.py'): + filename = arg + else: + filename = None + return filename - # Find all variable names and types so we can figure out column sizes - def get_vars(i): - return self.shell.user_ns[i] + # Set a few locals from the options for convenience: + opts_prev = 'p' in opts + opts_raw = 'r' in opts - # some types are well known and can be shorter - abbrevs = {'IPython.core.macro.Macro' : 'Macro'} - def type_name(v): - tn = type(v).__name__ - return abbrevs.get(tn,tn) + # custom exceptions + class DataIsObject(Exception): pass - varlist = map(get_vars,varnames) + # Default line number value + lineno = opts.get('n',None) - typelist = [] - for vv in varlist: - tt = type_name(vv) + if opts_prev: + args = '_%s' % last_call[0] + if not self.shell.user_ns.has_key(args): + args = last_call[1] - if tt=='instance': - typelist.append( abbrevs.get(str(vv.__class__), - str(vv.__class__))) - else: - typelist.append(tt) + # use last_call to remember the state of the previous call, but don't + # let it be clobbered by successive '-p' calls. + try: + last_call[0] = self.shell.displayhook.prompt_count + if not opts_prev: + last_call[1] = args + except: + pass - # column labels and # of spaces as separator - varlabel = 'Variable' - typelabel = 'Type' - datalabel = 'Data/Info' - colsep = 3 - # variable format strings - vformat = "{0:<{varwidth}}{1:<{typewidth}}" - aformat = "%s: %s elems, type `%s`, %s bytes" - # find the size of the columns to format the output nicely - varwidth = max(max(map(len,varnames)), len(varlabel)) + colsep - typewidth = max(max(map(len,typelist)), len(typelabel)) + colsep - # table header - print varlabel.ljust(varwidth) + typelabel.ljust(typewidth) + \ - ' '+datalabel+'\n' + '-'*(varwidth+typewidth+len(datalabel)+1) - # and the table itself - kb = 1024 - Mb = 1048576 # kb**2 - for vname,var,vtype in zip(varnames,varlist,typelist): - print vformat.format(vname, vtype, varwidth=varwidth, typewidth=typewidth), - if vtype in seq_types: - print "n="+str(len(var)) - elif vtype == ndarray_type: - vshape = str(var.shape).replace(',','').replace(' ','x')[1:-1] - if vtype==ndarray_type: - # numpy - vsize = var.size - vbytes = vsize*var.itemsize - vdtype = var.dtype + # by default this is done with temp files, except when the given + # arg is a filename + use_temp = True - if vbytes < 100000: - print aformat % (vshape,vsize,vdtype,vbytes) - else: - print aformat % (vshape,vsize,vdtype,vbytes), - if vbytes < Mb: - print '(%s kb)' % (vbytes/kb,) - else: - print '(%s Mb)' % (vbytes/Mb,) - else: + data = '' + + # First, see if the arguments should be a filename. + filename = make_filename(args) + if filename: + use_temp = False + elif args: + # Mode where user specifies ranges of lines, like in %macro. + data = self.shell.extract_input_lines(args, opts_raw) + if not data: try: - vstr = str(var) - except UnicodeEncodeError: - vstr = unicode(var).encode(DEFAULT_ENCODING, - 'backslashreplace') - except: - vstr = "" % id(var) - vstr = vstr.replace('\n','\\n') - if len(vstr) < 50: - print vstr - else: - print vstr[:25] + "<...>" + vstr[-25:] + # Load the parameter given as a variable. If not a string, + # process it as an object instead (below) - def magic_reset(self, parameter_s=''): - """Resets the namespace by removing all names defined by the user, if - called without arguments, or by removing some types of objects, such - as everything currently in IPython's In[] and Out[] containers (see - the parameters for details). + #print '*** args',args,'type',type(args) # dbg + data = eval(args, self.shell.user_ns) + if not isinstance(data, basestring): + raise DataIsObject - Parameters - ---------- - -f : force reset without asking for confirmation. + except (NameError,SyntaxError): + # given argument is not a variable, try as a filename + filename = make_filename(args) + if filename is None: + warn("Argument given (%s) can't be found as a variable " + "or as a filename." % args) + return + use_temp = False - -s : 'Soft' reset: Only clears your namespace, leaving history intact. - References to objects may be kept. By default (without this option), - we do a 'hard' reset, giving you a new session and removing all - references to objects from the current session. + except DataIsObject: + # macros have a special edit function + if isinstance(data, Macro): + raise MacroToEdit(data) - in : reset input history - - out : reset output history - - dhist : reset directory history - - array : reset only variables that are NumPy arrays + # For objects, try to edit the file where they are defined + try: + filename = inspect.getabsfile(data) + if 'fakemodule' in filename.lower() and inspect.isclass(data): + # class created by %edit? Try to find source + # by looking for method definitions instead, the + # __module__ in those classes is FakeModule. + attrs = [getattr(data, aname) for aname in dir(data)] + for attr in attrs: + if not inspect.ismethod(attr): + continue + filename = inspect.getabsfile(attr) + if filename and 'fakemodule' not in filename.lower(): + # change the attribute to be the edit target instead + data = attr + break - See Also - -------- - magic_reset_selective : invoked as ``%reset_selective`` + datafile = 1 + except TypeError: + filename = make_filename(args) + datafile = 1 + warn('Could not find file where `%s` is defined.\n' + 'Opening a file named `%s`' % (args,filename)) + # Now, make sure we can actually read the source (if it was in + # a temp file it's gone by now). + if datafile: + try: + if lineno is None: + lineno = inspect.getsourcelines(data)[1] + except IOError: + filename = make_filename(args) + if filename is None: + warn('The file `%s` where `%s` was defined cannot ' + 'be read.' % (filename,data)) + return + use_temp = False - Examples - -------- - :: + if use_temp: + filename = self.shell.mktempfile(data) + print 'IPython will make a temporary file named:',filename - In [6]: a = 1 + return filename, lineno, use_temp - In [7]: a - Out[7]: 1 + def _edit_macro(self,mname,macro): + """open an editor with the macro data in a file""" + filename = self.shell.mktempfile(macro.value) + self.shell.hooks.editor(filename) - In [8]: 'a' in _ip.user_ns - Out[8]: True + # and make a new macro object, to replace the old one + mfile = open(filename) + mvalue = mfile.read() + mfile.close() + self.shell.user_ns[mname] = Macro(mvalue) - In [9]: %reset -f + def magic_ed(self,parameter_s=''): + """Alias to %edit.""" + return self.magic_edit(parameter_s) - In [1]: 'a' in _ip.user_ns - Out[1]: False + @skip_doctest + def magic_edit(self,parameter_s='',last_call=['','']): + """Bring up an editor and execute the resulting code. - In [2]: %reset -f in - Flushing input history + Usage: + %edit [options] [args] - In [3]: %reset -f dhist in - Flushing directory history - Flushing input history + %edit runs IPython's editor hook. The default version of this hook is + set to call the editor specified by your $EDITOR environment variable. + If this isn't found, it will default to vi under Linux/Unix and to + notepad under Windows. See the end of this docstring for how to change + the editor hook. - Notes - ----- - Calling this magic from clients that do not implement standard input, - such as the ipython notebook interface, will reset the namespace - without confirmation. - """ - opts, args = self.parse_options(parameter_s,'sf', mode='list') - if 'f' in opts: - ans = True - else: - try: - ans = self.shell.ask_yes_no( - "Once deleted, variables cannot be recovered. Proceed (y/[n])? ", default='n') - except StdinNotImplementedError: - ans = True - if not ans: - print 'Nothing done.' - return - - if 's' in opts: # Soft reset - user_ns = self.shell.user_ns - for i in self.magic_who_ls(): - del(user_ns[i]) - elif len(args) == 0: # Hard reset - self.shell.reset(new_session = False) - - # reset in/out/dhist/array: previously extensinions/clearcmd.py - ip = self.shell - user_ns = self.shell.user_ns # local lookup, heavily used + You can also set the value of this editor via the + ``TerminalInteractiveShell.editor`` option in your configuration file. + This is useful if you wish to use a different editor from your typical + default with IPython (and for Windows users who typically don't set + environment variables). - for target in args: - target = target.lower() # make matches case insensitive - if target == 'out': - print "Flushing output cache (%d entries)" % len(user_ns['_oh']) - self.shell.displayhook.flush() + This command allows you to conveniently edit multi-line code right in + your IPython session. - elif target == 'in': - print "Flushing input history" - pc = self.shell.displayhook.prompt_count + 1 - for n in range(1, pc): - key = '_i'+repr(n) - user_ns.pop(key,None) - user_ns.update(dict(_i=u'',_ii=u'',_iii=u'')) - hm = ip.history_manager - # don't delete these, as %save and %macro depending on the length - # of these lists to be preserved - hm.input_hist_parsed[:] = [''] * pc - hm.input_hist_raw[:] = [''] * pc - # hm has internal machinery for _i,_ii,_iii, clear it out - hm._i = hm._ii = hm._iii = hm._i00 = u'' + If called without arguments, %edit opens up an empty editor with a + temporary file and will execute the contents of this file when you + close it (don't forget to save it!). - elif target == 'array': - # Support cleaning up numpy arrays - try: - from numpy import ndarray - # This must be done with items and not iteritems because we're - # going to modify the dict in-place. - for x,val in user_ns.items(): - if isinstance(val,ndarray): - del user_ns[x] - except ImportError: - print "reset array only works if Numpy is available." - elif target == 'dhist': - print "Flushing directory history" - del user_ns['_dh'][:] + Options: - else: - print "Don't know how to reset ", - print target + ", please run `%reset?` for details" + -n : open the editor at a specified line number. By default, + the IPython editor hook uses the unix syntax 'editor +N filename', but + you can configure this by providing your own modified hook if your + favorite editor supports line-number specifications with a different + syntax. - gc.collect() + -p: this will call the editor with the same data as the previous time + it was used, regardless of how long ago (in your current session) it + was. - def magic_reset_selective(self, parameter_s=''): - """Resets the namespace by removing names defined by the user. + -r: use 'raw' input. This option only applies to input taken from the + user's history. By default, the 'processed' history is used, so that + magics are loaded in their transformed version to valid Python. If + this option is given, the raw input as typed as the command line is + used instead. When you exit the editor, it will be executed by + IPython's own processor. - Input/Output history are left around in case you need them. + -x: do not execute the edited code immediately upon exit. This is + mainly useful if you are editing programs which need to be called with + command line arguments, which you can then do using %run. - %reset_selective [-f] regex - No action is taken if regex is not included + Arguments: - Options - -f : force reset without asking for confirmation. + If arguments are given, the following possibilities exist: - See Also - -------- - magic_reset : invoked as ``%reset`` + - If the argument is a filename, IPython will load that into the + editor. It will execute its contents with execfile() when you exit, + loading any code in the file into your interactive namespace. - Examples - -------- + - The arguments are ranges of input history, e.g. "7 ~1/4-6". + The syntax is the same as in the %history magic. - We first fully reset the namespace so your output looks identical to - this example for pedagogical reasons; in practice you do not need a - full reset:: + - If the argument is a string variable, its contents are loaded + into the editor. You can thus edit any string which contains + python code (including the result of previous edits). - In [1]: %reset -f + - If the argument is the name of an object (other than a string), + IPython will try to locate the file where it was defined and open the + editor at the point where it is defined. You can use `%edit function` + to load an editor exactly at the point where 'function' is defined, + edit it and have the file be executed automatically. - Now, with a clean namespace we can make a few variables and use - ``%reset_selective`` to only delete names that match our regexp:: + - If the object is a macro (see %macro for details), this opens up your + specified editor with a temporary file containing the macro's data. + Upon exit, the macro is reloaded with the contents of the file. - In [2]: a=1; b=2; c=3; b1m=4; b2m=5; b3m=6; b4m=7; b2s=8 + Note: opening at an exact line is only supported under Unix, and some + editors (like kedit and gedit up to Gnome 2.8) do not understand the + '+NUMBER' parameter necessary for this feature. Good editors like + (X)Emacs, vi, jed, pico and joe all do. - In [3]: who_ls - Out[3]: ['a', 'b', 'b1m', 'b2m', 'b2s', 'b3m', 'b4m', 'c'] + After executing your code, %edit will return as output the code you + typed in the editor (except when it was an existing file). This way + you can reload the code in further invocations of %edit as a variable, + via _ or Out[], where is the prompt number of + the output. - In [4]: %reset_selective -f b[2-3]m + Note that %edit is also available through the alias %ed. - In [5]: who_ls - Out[5]: ['a', 'b', 'b1m', 'b2s', 'b4m', 'c'] + This is an example of creating a simple function inside the editor and + then modifying it. First, start up the editor:: - In [6]: %reset_selective -f d + In [1]: ed + Editing... done. Executing edited code... + Out[1]: 'def foo():\\n print "foo() was defined in an editing + session"\\n' - In [7]: who_ls - Out[7]: ['a', 'b', 'b1m', 'b2s', 'b4m', 'c'] + We can then call the function foo():: - In [8]: %reset_selective -f c + In [2]: foo() + foo() was defined in an editing session - In [9]: who_ls - Out[9]: ['a', 'b', 'b1m', 'b2s', 'b4m'] + Now we edit foo. IPython automatically loads the editor with the + (temporary) file where foo() was previously defined:: - In [10]: %reset_selective -f b + In [3]: ed foo + Editing... done. Executing edited code... - In [11]: who_ls - Out[11]: ['a'] + And if we call foo() again we get the modified version:: - Notes - ----- - Calling this magic from clients that do not implement standard input, - such as the ipython notebook interface, will reset the namespace - without confirmation. - """ + In [4]: foo() + foo() has now been changed! - opts, regex = self.parse_options(parameter_s,'f') + Here is an example of how to edit a code snippet successive + times. First we call the editor:: - if opts.has_key('f'): - ans = True - else: - try: - ans = self.shell.ask_yes_no( - "Once deleted, variables cannot be recovered. Proceed (y/[n])? ", - default='n') - except StdinNotImplementedError: - ans = True - if not ans: - print 'Nothing done.' - return - user_ns = self.shell.user_ns - if not regex: - print 'No regex pattern specified. Nothing done.' - return - else: - try: - m = re.compile(regex) - except TypeError: - raise TypeError('regex must be a string or compiled pattern') - for i in self.magic_who_ls(): - if m.search(i): - del(user_ns[i]) + In [5]: ed + Editing... done. Executing edited code... + hello + Out[5]: "print 'hello'\\n" - def magic_xdel(self, parameter_s=''): - """Delete a variable, trying to clear it from anywhere that - IPython's machinery has references to it. By default, this uses - the identity of the named object in the user namespace to remove - references held under other names. The object is also removed - from the output history. + Now we call it again with the previous output (stored in _):: - Options - -n : Delete the specified name from all namespaces, without - checking their identity. - """ - opts, varname = self.parse_options(parameter_s,'n') - try: - self.shell.del_var(varname, ('n' in opts)) - except (NameError, ValueError) as e: - print type(e).__name__ +": "+ str(e) + In [6]: ed _ + Editing... done. Executing edited code... + hello world + Out[6]: "print 'hello world'\\n" - def magic_logstart(self,parameter_s=''): - """Start logging anywhere in a session. + Now we call it with the output #8 (stored in _8, also as Out[8]):: - %logstart [-o|-r|-t] [log_name [log_mode]] + In [7]: ed _8 + Editing... done. Executing edited code... + hello again + Out[7]: "print 'hello again'\\n" - If no name is given, it defaults to a file named 'ipython_log.py' in your - current directory, in 'rotate' mode (see below). - '%logstart name' saves to file 'name' in 'backup' mode. It saves your - history up to that point and then continues logging. + Changing the default editor hook: - %logstart takes a second optional parameter: logging mode. This can be one - of (note that the modes are given unquoted):\\ - append: well, that says it.\\ - backup: rename (if exists) to name~ and start name.\\ - global: single logfile in your home dir, appended to.\\ - over : overwrite existing log.\\ - rotate: create rotating logs name.1~, name.2~, etc. + If you wish to write your own editor hook, you can put it in a + configuration file which you load at startup time. The default hook + is defined in the IPython.core.hooks module, and you can use that as a + starting example for further modifications. That file also has + general instructions on how to set a new hook for use once you've + defined it.""" + opts,args = self.parse_options(parameter_s,'prxn:') - Options: + try: + filename, lineno, is_temp = self._find_edit_target(args, opts, last_call) + except MacroToEdit as e: + self._edit_macro(args, e.args[0]) + return - -o: log also IPython's output. In this mode, all commands which - generate an Out[NN] prompt are recorded to the logfile, right after - their corresponding input line. The output lines are always - prepended with a '#[Out]# ' marker, so that the log remains valid - Python code. + # do actual editing here + print 'Editing...', + sys.stdout.flush() + try: + # Quote filenames that may have spaces in them + if ' ' in filename: + filename = "'%s'" % filename + self.shell.hooks.editor(filename,lineno) + except TryNext: + warn('Could not open editor') + return - Since this marker is always the same, filtering only the output from - a log is very easy, using for example a simple awk call:: + # XXX TODO: should this be generalized for all string vars? + # For now, this is special-cased to blocks created by cpaste + if args.strip() == 'pasted_block': + self.shell.user_ns['pasted_block'] = file_read(filename) - awk -F'#\\[Out\\]# ' '{if($2) {print $2}}' ipython_log.py + if 'x' in opts: # -x prevents actual execution + print + else: + print 'done. Executing edited code...' + if 'r' in opts: # Untranslated IPython code + self.shell.run_cell(file_read(filename), + store_history=False) + else: + self.shell.safe_execfile(filename, self.shell.user_ns, + self.shell.user_ns) - -r: log 'raw' input. Normally, IPython's logs contain the processed - input, so that user lines are logged in their final form, converted - into valid Python. For example, %Exit is logged as - _ip.magic("Exit"). If the -r flag is given, all input is logged - exactly as typed, with no transformations applied. + if is_temp: + try: + return open(filename).read() + except IOError,msg: + if msg.filename == filename: + warn('File not found. Did you forget to save?') + return + else: + self.shell.showtraceback() - -t: put timestamps before each input line logged (these are put in - comments).""" - opts,par = self.parse_options(parameter_s,'ort') - log_output = 'o' in opts - log_raw_input = 'r' in opts - timestamp = 't' in opts +class ConfigMagics(MagicFunctions): - logger = self.shell.logger + def __init__(self, shell): + super(ProfileMagics, self).__init__(shell) + self.configurables = [] - # if no args are given, the defaults set in the logger constructor by - # ipython remain valid - if par: - try: - logfname,logmode = par.split() - except: - logfname = par - logmode = 'backup' - else: - logfname = logger.logfname - logmode = logger.logmode - # put logfname into rc struct as if it had been called on the command - # line, so it ends up saved in the log header Save it in case we need - # to restore it... - old_logfile = self.shell.logfile - if logfname: - logfname = os.path.expanduser(logfname) - self.shell.logfile = logfname + def magic_config(self, s): + """configure IPython - loghead = '# IPython log file\n\n' - try: - started = logger.logstart(logfname,loghead,logmode, - log_output,timestamp,log_raw_input) - except: - self.shell.logfile = old_logfile - warn("Couldn't start log: %s" % sys.exc_info()[1]) - else: - # log input history up to this point, optionally interleaving - # output if requested + %config Class[.trait=value] - if timestamp: - # disable timestamping for the previous history, since we've - # lost those already (no time machine here). - logger.timestamp = False + This magic exposes most of the IPython config system. Any + Configurable class should be able to be configured with the simple + line:: - if log_raw_input: - input_hist = self.shell.history_manager.input_hist_raw - else: - input_hist = self.shell.history_manager.input_hist_parsed + %config Class.trait=value - if log_output: - log_write = logger.log_write - output_hist = self.shell.history_manager.output_hist - for n in range(1,len(input_hist)-1): - log_write(input_hist[n].rstrip() + '\n') - if n in output_hist: - log_write(repr(output_hist[n]),'output') - else: - logger.log_write('\n'.join(input_hist[1:])) - logger.log_write('\n') - if timestamp: - # re-enable timestamping - logger.timestamp = True + Where `value` will be resolved in the user's namespace, if it is an + expression or variable name. - print ('Activating auto-logging. ' - 'Current session state plus future input saved.') - logger.logstate() + Examples + -------- - def magic_logstop(self,parameter_s=''): - """Fully stop logging and close log file. + To see what classes are available for config, pass no arguments:: - In order to start logging again, a new %logstart call needs to be made, - possibly (though not necessarily) with a new filename, mode and other - options.""" - self.logger.logstop() + In [1]: %config + Available objects for config: + TerminalInteractiveShell + HistoryManager + PrefilterManager + AliasManager + IPCompleter + PromptManager + DisplayFormatter - def magic_logoff(self,parameter_s=''): - """Temporarily stop logging. + To view what is configurable on a given class, just pass the class + name:: - You must have previously started logging.""" - self.shell.logger.switch_log(0) + In [2]: %config IPCompleter + IPCompleter options + ----------------- + IPCompleter.omit__names= + Current: 2 + Choices: (0, 1, 2) + Instruct the completer to omit private method names + Specifically, when completing on ``object.``. + When 2 [default]: all names that start with '_' will be excluded. + When 1: all 'magic' names (``__foo__``) will be excluded. + When 0: nothing will be excluded. + IPCompleter.merge_completions= + Current: True + Whether to merge completion results into a single list + If False, only the completion results from the first non-empty completer + will be returned. + IPCompleter.limit_to__all__= + Current: False + Instruct the completer to use __all__ for the completion + Specifically, when completing on ``object.``. + When True: only those names in obj.__all__ will be included. + When False [default]: the __all__ attribute is ignored + IPCompleter.greedy= + Current: False + Activate greedy completion + This will enable completion on elements of lists, results of function calls, + etc., but can be unsafe because the code is actually evaluated on TAB. - def magic_logon(self,parameter_s=''): - """Restart logging. + but the real use is in setting values:: - This function is for restarting logging which you've temporarily - stopped with %logoff. For starting logging for the first time, you - must use the %logstart function, which allows you to specify an - optional log filename.""" + In [3]: %config IPCompleter.greedy = True - self.shell.logger.switch_log(1) + and these values are read from the user_ns if they are variables:: - def magic_logstate(self,parameter_s=''): - """Print the status of the logging system.""" + In [4]: feeling_greedy=False - self.shell.logger.logstate() + In [5]: %config IPCompleter.greedy = feeling_greedy - def magic_pdb(self, parameter_s=''): - """Control the automatic calling of the pdb interactive debugger. + """ + from IPython.config.loader import Config + # some IPython objects are Configurable, but do not yet have + # any configurable traits. Exclude them from the effects of + # this magic, as their presence is just noise: + configurables = [ c for c in self.shell.configurables + if c.__class__.class_traits(config=True) ] + classnames = [ c.__class__.__name__ for c in configurables ] - Call as '%pdb on', '%pdb 1', '%pdb off' or '%pdb 0'. If called without - argument it works as a toggle. + line = s.strip() + if not line: + # print available configurable names + print "Available objects for config:" + for name in classnames: + print " ", name + return + elif line in classnames: + # `%config TerminalInteractiveShell` will print trait info for + # TerminalInteractiveShell + c = configurables[classnames.index(line)] + cls = c.__class__ + help = cls.class_get_help(c) + # strip leading '--' from cl-args: + help = re.sub(re.compile(r'^--', re.MULTILINE), '', help) + print help + return + elif '=' not in line: + raise UsageError("Invalid config statement: %r, should be Class.trait = value" % line) - When an exception is triggered, IPython can optionally call the - interactive pdb debugger after the traceback printout. %pdb toggles - this feature on and off. - The initial state of this feature is set in your configuration - file (the option is ``InteractiveShell.pdb``). + # otherwise, assume we are setting configurables. + # leave quotes on args when splitting, because we want + # unquoted args to eval in user_ns + cfg = Config() + exec "cfg."+line in locals(), self.shell.user_ns - If you want to just activate the debugger AFTER an exception has fired, - without having to type '%pdb on' and rerunning your code, you can use - the %debug magic.""" + for configurable in configurables: + try: + configurable.update_config(cfg) + except Exception as e: + error(e) - par = parameter_s.strip().lower() - if par: - try: - new_pdb = {'off':0,'0':0,'on':1,'1':1}[par] - except KeyError: - print ('Incorrect argument. Use on/1, off/0, ' - 'or nothing for a toggle.') - return - else: - # toggle - new_pdb = not self.shell.call_pdb +class NamespaceMagics(MagicFunctions): + """Magics to manage various aspects of the user's namespace. - # set on the shell - self.shell.call_pdb = new_pdb - print 'Automatic pdb calling has been turned',on_off(new_pdb) + These include listing variables, introspecting into them, etc. + """ - def magic_debug(self, parameter_s=''): - """Activate the interactive debugger in post-mortem mode. + def magic_pinfo(self, parameter_s='', namespaces=None): + """Provide detailed information about an object. - If an exception has just occurred, this lets you inspect its stack - frames interactively. Note that this will always work only on the last - traceback that occurred, so you must call this quickly after an - exception that you wish to inspect has fired, because if another one - occurs, it clobbers the previous one. + '%pinfo object' is just a synonym for object? or ?object.""" - If you want IPython to automatically do this on every exception, see - the %pdb magic for more details. - """ - self.shell.debugger(force=True) + #print 'pinfo par: <%s>' % parameter_s # dbg - @skip_doctest - def magic_prun(self, parameter_s ='',user_mode=1, - opts=None,arg_lst=None,prog_ns=None): - """Run a statement through the python code profiler. + # detail_level: 0 -> obj? , 1 -> obj?? + detail_level = 0 + # We need to detect if we got called as 'pinfo pinfo foo', which can + # happen if the user types 'pinfo foo?' at the cmd line. + pinfo,qmark1,oname,qmark2 = \ + re.match('(pinfo )?(\?*)(.*?)(\??$)',parameter_s).groups() + if pinfo or qmark1 or qmark2: + detail_level = 1 + if "*" in oname: + self.magic_psearch(oname) + else: + self.shell._inspect('pinfo', oname, detail_level=detail_level, + namespaces=namespaces) - Usage: - %prun [options] statement + def magic_pinfo2(self, parameter_s='', namespaces=None): + """Provide extra detailed information about an object. - The given statement (which doesn't require quote marks) is run via the - python profiler in a manner similar to the profile.run() function. - Namespaces are internally managed to work correctly; profile.run - cannot be used in IPython because it makes certain assumptions about - namespaces which do not hold under IPython. + '%pinfo2 object' is just a synonym for object?? or ??object.""" + self.shell._inspect('pinfo', parameter_s, detail_level=1, + namespaces=namespaces) - Options: + @skip_doctest + def magic_pdef(self, parameter_s='', namespaces=None): + """Print the definition header for any callable object. - -l : you can place restrictions on what or how much of the - profile gets printed. The limit value can be: + If the object is a class, print the constructor information. - * A string: only information for function names containing this string - is printed. + Examples + -------- + :: - * An integer: only these many lines are printed. + In [3]: %pdef urllib.urlopen + urllib.urlopen(url, data=None, proxies=None) + """ + self._inspect('pdef',parameter_s, namespaces) - * A float (between 0 and 1): this fraction of the report is printed - (for example, use a limit of 0.4 to see the topmost 40% only). + def magic_pdoc(self, parameter_s='', namespaces=None): + """Print the docstring for an object. - You can combine several limits with repeated use of the option. For - example, '-l __init__ -l 5' will print only the topmost 5 lines of - information about class constructors. + If the given object is a class, it will print both the class and the + constructor docstrings.""" + self._inspect('pdoc',parameter_s, namespaces) - -r: return the pstats.Stats object generated by the profiling. This - object has all the information about the profile in it, and you can - later use it for further analysis or in other functions. + def magic_psource(self, parameter_s='', namespaces=None): + """Print (or run through pager) the source code for an object.""" + self._inspect('psource',parameter_s, namespaces) - -s : sort profile by given key. You can provide more than one key - by using the option several times: '-s key1 -s key2 -s key3...'. The - default sorting key is 'time'. + def magic_pfile(self, parameter_s=''): + """Print (or run through pager) the file where an object is defined. - The following is copied verbatim from the profile documentation - referenced below: + The file opens at the line where the object definition begins. IPython + will honor the environment variable PAGER if set, and otherwise will + do its best to print the file in a convenient form. - When more than one key is provided, additional keys are used as - secondary criteria when the there is equality in all keys selected - before them. + If the given argument is not an object currently defined, IPython will + try to interpret it as a filename (automatically adding a .py extension + if needed). You can thus use %pfile as a syntax highlighting code + viewer.""" - Abbreviations can be used for any key names, as long as the - abbreviation is unambiguous. The following are the keys currently - defined: + # first interpret argument as an object name + out = self._inspect('pfile',parameter_s) + # if not, try the input as a filename + if out == 'not found': + try: + filename = get_py_filename(parameter_s) + except IOError,msg: + print msg + return + page.page(self.shell.inspector.format(open(filename).read())) - Valid Arg Meaning - "calls" call count - "cumulative" cumulative time - "file" file name - "module" file name - "pcalls" primitive call count - "line" line number - "name" function name - "nfl" name/file/line - "stdname" standard name - "time" internal time + def magic_psearch(self, parameter_s=''): + """Search for object in namespaces by wildcard. - Note that all sorts on statistics are in descending order (placing - most time consuming items first), where as name, file, and line number - searches are in ascending order (i.e., alphabetical). The subtle - distinction between "nfl" and "stdname" is that the standard name is a - sort of the name as printed, which means that the embedded line - numbers get compared in an odd way. For example, lines 3, 20, and 40 - would (if the file names were the same) appear in the string order - "20" "3" and "40". In contrast, "nfl" does a numeric compare of the - line numbers. In fact, sort_stats("nfl") is the same as - sort_stats("name", "file", "line"). + %psearch [options] PATTERN [OBJECT TYPE] - -T : save profile results as shown on screen to a text - file. The profile is still shown on screen. + Note: ? can be used as a synonym for %psearch, at the beginning or at + the end: both a*? and ?a* are equivalent to '%psearch a*'. Still, the + rest of the command line must be unchanged (options come first), so + for example the following forms are equivalent - -D : save (via dump_stats) profile statistics to given - filename. This data is in a format understood by the pstats module, and - is generated by a call to the dump_stats() method of profile - objects. The profile is still shown on screen. + %psearch -i a* function + -i a* function? + ?-i a* function - -q: suppress output to the pager. Best used with -T and/or -D above. + Arguments: - If you want to run complete programs under the profiler's control, use - '%run -p [prof_opts] filename.py [args to program]' where prof_opts - contains profiler specific options as described here. + PATTERN - You can read the complete documentation for the profile module with:: + where PATTERN is a string containing * as a wildcard similar to its + use in a shell. The pattern is matched in all namespaces on the + search path. By default objects starting with a single _ are not + matched, many IPython generated objects have a single + underscore. The default is case insensitive matching. Matching is + also done on the attributes of objects and not only on the objects + in a module. - In [1]: import profile; profile.help() - """ + [OBJECT TYPE] - opts_def = Struct(D=[''],l=[],s=['time'],T=['']) + Is the name of a python type from the types module. The name is + given in lowercase without the ending type, ex. StringType is + written string. By adding a type here only objects matching the + given type are matched. Using all here makes the pattern match all + types (this is the default). - if user_mode: # regular user call - opts,arg_str = self.parse_options(parameter_s,'D:l:rs:T:q', - list_all=1, posix=False) - namespace = self.shell.user_ns - else: # called to run a program by %run -p - try: - filename = get_py_filename(arg_lst[0]) - except IOError as e: - try: - msg = str(e) - except UnicodeError: - msg = e.message - error(msg) - return + Options: - arg_str = 'execfile(filename,prog_ns)' - namespace = { - 'execfile': self.shell.safe_execfile, - 'prog_ns': prog_ns, - 'filename': filename - } + -a: makes the pattern match even objects whose names start with a + single underscore. These names are normally omitted from the + search. - opts.merge(opts_def) + -i/-c: make the pattern case insensitive/sensitive. If neither of + these options are given, the default is read from your configuration + file, with the option ``InteractiveShell.wildcards_case_sensitive``. + If this option is not specified in your configuration file, IPython's + internal default is to do a case sensitive search. - prof = profile.Profile() - try: - prof = prof.runctx(arg_str,namespace,namespace) - sys_exit = '' - except SystemExit: - sys_exit = """*** SystemExit exception caught in code being profiled.""" + -e/-s NAMESPACE: exclude/search a given namespace. The pattern you + specify can be searched in any of the following namespaces: + 'builtin', 'user', 'user_global','internal', 'alias', where + 'builtin' and 'user' are the search defaults. Note that you should + not use quotes when specifying namespaces. - stats = pstats.Stats(prof).strip_dirs().sort_stats(*opts.s) + 'Builtin' contains the python module builtin, 'user' contains all + user data, 'alias' only contain the shell aliases and no python + objects, 'internal' contains objects used by IPython. The + 'user_global' namespace is only used by embedded IPython instances, + and it contains module-level globals. You can add namespaces to the + search with -s or exclude them with -e (these options can be given + more than once). - lims = opts.l - if lims: - lims = [] # rebuild lims with ints/floats/strings - for lim in opts.l: - try: - lims.append(int(lim)) - except ValueError: - try: - lims.append(float(lim)) - except ValueError: - lims.append(lim) + Examples + -------- + :: - # Trap output. - stdout_trap = StringIO() + %psearch a* -> objects beginning with an a + %psearch -e builtin a* -> objects NOT in the builtin space starting in a + %psearch a* function -> all functions beginning with an a + %psearch re.e* -> objects beginning with an e in module re + %psearch r*.e* -> objects that start with e in modules starting in r + %psearch r*.* string -> all strings in modules beginning with r - if hasattr(stats,'stream'): - # In newer versions of python, the stats object has a 'stream' - # attribute to write into. - stats.stream = stdout_trap - stats.print_stats(*lims) - else: - # For older versions, we manually redirect stdout during printing - sys_stdout = sys.stdout - try: - sys.stdout = stdout_trap - stats.print_stats(*lims) - finally: - sys.stdout = sys_stdout + Case sensitive search:: - output = stdout_trap.getvalue() - output = output.rstrip() + %psearch -c a* list all object beginning with lower case a - if 'q' not in opts: - page.page(output) - print sys_exit, + Show objects beginning with a single _:: - dump_file = opts.D[0] - text_file = opts.T[0] - if dump_file: - dump_file = unquote_filename(dump_file) - prof.dump_stats(dump_file) - print '\n*** Profile stats marshalled to file',\ - `dump_file`+'.',sys_exit - if text_file: - text_file = unquote_filename(text_file) - pfile = open(text_file,'w') - pfile.write(output) - pfile.close() - print '\n*** Profile printout saved to text file',\ - `text_file`+'.',sys_exit + %psearch -a _* list objects beginning with a single underscore""" + try: + parameter_s.encode('ascii') + except UnicodeEncodeError: + print 'Python identifiers can only contain ascii characters.' + return - if opts.has_key('r'): - return stats - else: - return None + # default namespaces to be searched + def_search = ['user_local', 'user_global', 'builtin'] - @skip_doctest - def magic_run(self, parameter_s ='', runner=None, - file_finder=get_py_filename): - """Run the named file inside IPython as a program. + # Process options/args + opts,args = self.parse_options(parameter_s,'cias:e:',list_all=True) + opt = opts.get + shell = self.shell + psearch = shell.inspector.psearch - Usage:\\ - %run [-n -i -t [-N] -d [-b] -p [profile options]] file [args] + # select case options + if opts.has_key('i'): + ignore_case = True + elif opts.has_key('c'): + ignore_case = False + else: + ignore_case = not shell.wildcards_case_sensitive - Parameters after the filename are passed as command-line arguments to - the program (put in sys.argv). Then, control returns to IPython's - prompt. + # Build list of namespaces to search from user options + def_search.extend(opt('s',[])) + ns_exclude = ns_exclude=opt('e',[]) + ns_search = [nm for nm in def_search if nm not in ns_exclude] - This is similar to running at a system prompt:\\ - $ python file args\\ - but with the advantage of giving you IPython's tracebacks, and of - loading all variables into your interactive namespace for further use - (unless -p is used, see below). + # Call the actual search + try: + psearch(args,shell.ns_table,ns_search, + show_all=opt('a'),ignore_case=ignore_case) + except: + shell.showtraceback() - The file is executed in a namespace initially consisting only of - __name__=='__main__' and sys.argv constructed as indicated. It thus - sees its environment as if it were being run as a stand-alone program - (except for sharing global objects such as previously imported - modules). But after execution, the IPython interactive namespace gets - updated with all variables defined in the program (except for __name__ - and sys.argv). This allows for very convenient loading of code for - interactive work, while giving each program a 'clean sheet' to run in. + @skip_doctest + def magic_who_ls(self, parameter_s=''): + """Return a sorted list of all interactive variables. - Options: + If arguments are given, only variables of types matching these + arguments are returned. - -n: __name__ is NOT set to '__main__', but to the running file's name - without extension (as python does under import). This allows running - scripts and reloading the definitions in them without calling code - protected by an ' if __name__ == "__main__" ' clause. + Examples + -------- - -i: run the file in IPython's namespace instead of an empty one. This - is useful if you are experimenting with code written in a text editor - which depends on variables defined interactively. + Define two variables and list them with who_ls:: - -e: ignore sys.exit() calls or SystemExit exceptions in the script - being run. This is particularly useful if IPython is being used to - run unittests, which always exit with a sys.exit() call. In such - cases you are interested in the output of the test results, not in - seeing a traceback of the unittest module. + In [1]: alpha = 123 - -t: print timing information at the end of the run. IPython will give - you an estimated CPU time consumption for your script, which under - Unix uses the resource module to avoid the wraparound problems of - time.clock(). Under Unix, an estimate of time spent on system tasks - is also given (for Windows platforms this is reported as 0.0). + In [2]: beta = 'test' - If -t is given, an additional -N option can be given, where - must be an integer indicating how many times you want the script to - run. The final timing report will include total and per run results. + In [3]: %who_ls + Out[3]: ['alpha', 'beta'] - For example (testing the script uniq_stable.py):: + In [4]: %who_ls int + Out[4]: ['alpha'] - In [1]: run -t uniq_stable + In [5]: %who_ls str + Out[5]: ['beta'] + """ - IPython CPU timings (estimated):\\ - User : 0.19597 s.\\ - System: 0.0 s.\\ + user_ns = self.shell.user_ns + user_ns_hidden = self.shell.user_ns_hidden + out = [ i for i in user_ns + if not i.startswith('_') \ + and not i in user_ns_hidden ] - In [2]: run -t -N5 uniq_stable + typelist = parameter_s.split() + if typelist: + typeset = set(typelist) + out = [i for i in out if type(user_ns[i]).__name__ in typeset] - IPython CPU timings (estimated):\\ - Total runs performed: 5\\ - Times : Total Per run\\ - User : 0.910862 s, 0.1821724 s.\\ - System: 0.0 s, 0.0 s. + out.sort() + return out - -d: run your program under the control of pdb, the Python debugger. - This allows you to execute your program step by step, watch variables, - etc. Internally, what IPython does is similar to calling: + @skip_doctest + def magic_who(self, parameter_s=''): + """Print all interactive variables, with some minimal formatting. - pdb.run('execfile("YOURFILENAME")') + If any arguments are given, only variables whose type matches one of + these are printed. For example:: - with a breakpoint set on line 1 of your file. You can change the line - number for this automatic breakpoint to be by using the -bN option - (where N must be an integer). For example:: + %who function str - %run -d -b40 myscript + will only list functions and strings, excluding all other types of + variables. To find the proper type names, simply use type(var) at a + command line to see how python prints type names. For example: - will set the first breakpoint at line 40 in myscript.py. Note that - the first breakpoint must be set on a line which actually does - something (not a comment or docstring) for it to stop execution. + :: - When the pdb debugger starts, you will see a (Pdb) prompt. You must - first enter 'c' (without quotes) to start execution up to the first - breakpoint. + In [1]: type('hello')\\ + Out[1]: - Entering 'help' gives information about the use of the debugger. You - can easily see pdb's full documentation with "import pdb;pdb.help()" - at a prompt. + indicates that the type name for strings is 'str'. - -p: run program under the control of the Python profiler module (which - prints a detailed report of execution times, function calls, etc). + ``%who`` always excludes executed names loaded through your configuration + file and things which are internal to IPython. - You can pass other options after -p which affect the behavior of the - profiler itself. See the docs for %prun for details. + This is deliberate, as typically you may load many modules and the + purpose of %who is to show you only what you've manually defined. - In this mode, the program's variables do NOT propagate back to the - IPython interactive namespace (because they remain in the namespace - where the profiler executes them). + Examples + -------- - Internally this triggers a call to %prun, see its documentation for - details on the options available specifically for profiling. + Define two variables and list them with who:: - There is one special usage for which the text above doesn't apply: - if the filename ends with .ipy, the file is run as ipython script, - just as if the commands were written on IPython prompt. + In [1]: alpha = 123 - -m: specify module name to load instead of script path. Similar to - the -m option for the python interpreter. Use this option last if you - want to combine with other %run options. Unlike the python interpreter - only source modules are allowed no .pyc or .pyo files. - For example:: + In [2]: beta = 'test' - %run -m example + In [3]: %who + alpha beta - will run the example module. + In [4]: %who int + alpha + In [5]: %who str + beta """ - # get arguments and set sys.argv for program to be run. - opts, arg_lst = self.parse_options(parameter_s, 'nidtN:b:pD:l:rs:T:em:', - mode='list', list_all=1) - if "m" in opts: - modulename = opts["m"][0] - modpath = find_mod(modulename) - if modpath is None: - warn('%r is not a valid modulename on sys.path'%modulename) - return - arg_lst = [modpath] + arg_lst - try: - filename = file_finder(arg_lst[0]) - except IndexError: - warn('you must provide at least a filename.') - print '\n%run:\n', oinspect.getdoc(self.magic_run) - return - except IOError as e: - try: - msg = str(e) - except UnicodeError: - msg = e.message - error(msg) + varlist = self.magic_who_ls(parameter_s) + if not varlist: + if parameter_s: + print 'No variables match your requested type.' + else: + print 'Interactive namespace is empty.' return - if filename.lower().endswith('.ipy'): - self.shell.safe_execfile_ipy(filename) - return + # if we have variables, move on... + count = 0 + for i in varlist: + print i+'\t', + count += 1 + if count > 8: + count = 0 + print + print - # Control the response to exit() calls made by the script being run - exit_ignore = 'e' in opts + @skip_doctest + def magic_whos(self, parameter_s=''): + """Like %who, but gives some extra information about each variable. - # Make sure that the running script gets a proper sys.argv as if it - # were run from a system shell. - save_argv = sys.argv # save it for later restoring + The same type filtering of %who can be applied here. - # simulate shell expansion on arguments, at least tilde expansion - args = [ os.path.expanduser(a) for a in arg_lst[1:] ] + For all variables, the type is printed. Additionally it prints: - sys.argv = [filename] + args # put in the proper filename - # protect sys.argv from potential unicode strings on Python 2: - if not py3compat.PY3: - sys.argv = [ py3compat.cast_bytes(a) for a in sys.argv ] + - For {},[],(): their length. - if 'i' in opts: - # Run in user's interactive namespace - prog_ns = self.shell.user_ns - __name__save = self.shell.user_ns['__name__'] - prog_ns['__name__'] = '__main__' - main_mod = self.shell.new_main_mod(prog_ns) - else: - # Run in a fresh, empty namespace - if 'n' in opts: - name = os.path.splitext(os.path.basename(filename))[0] - else: - name = '__main__' + - For numpy arrays, a summary with shape, number of + elements, typecode and size in memory. - main_mod = self.shell.new_main_mod() - prog_ns = main_mod.__dict__ - prog_ns['__name__'] = name + - Everything else: a string representation, snipping their middle if + too long. - # Since '%run foo' emulates 'python foo.py' at the cmd line, we must - # set the __file__ global in the script's namespace - prog_ns['__file__'] = filename + Examples + -------- - # pickle fix. See interactiveshell for an explanation. But we need to make sure - # that, if we overwrite __main__, we replace it at the end - main_mod_name = prog_ns['__name__'] + Define two variables and list them with whos:: - if main_mod_name == '__main__': - restore_main = sys.modules['__main__'] - else: - restore_main = False + In [1]: alpha = 123 - # This needs to be undone at the end to prevent holding references to - # every single object ever created. - sys.modules[main_mod_name] = main_mod + In [2]: beta = 'test' - try: - stats = None - with self.shell.readline_no_record: - if 'p' in opts: - stats = self.magic_prun('', 0, opts, arg_lst, prog_ns) - else: - if 'd' in opts: - deb = debugger.Pdb(self.shell.colors) - # reset Breakpoint state, which is moronically kept - # in a class - bdb.Breakpoint.next = 1 - bdb.Breakpoint.bplist = {} - bdb.Breakpoint.bpbynumber = [None] - # Set an initial breakpoint to stop execution - maxtries = 10 - bp = int(opts.get('b', [1])[0]) - checkline = deb.checkline(filename, bp) - if not checkline: - for bp in range(bp + 1, bp + maxtries + 1): - if deb.checkline(filename, bp): - break - else: - msg = ("\nI failed to find a valid line to set " - "a breakpoint\n" - "after trying up to line: %s.\n" - "Please set a valid breakpoint manually " - "with the -b option." % bp) - error(msg) - return - # if we find a good linenumber, set the breakpoint - deb.do_break('%s:%s' % (filename, bp)) - # Start file run - print "NOTE: Enter 'c' at the", - print "%s prompt to start your script." % deb.prompt - ns = {'execfile': py3compat.execfile, 'prog_ns': prog_ns} - try: - deb.run('execfile("%s", prog_ns)' % filename, ns) + In [3]: %whos + Variable Type Data/Info + -------------------------------- + alpha int 123 + beta str test + """ - except: - etype, value, tb = sys.exc_info() - # Skip three frames in the traceback: the %run one, - # one inside bdb.py, and the command-line typed by the - # user (run by exec in pdb itself). - self.shell.InteractiveTB(etype, value, tb, tb_offset=3) - else: - if runner is None: - runner = self.default_runner - if runner is None: - runner = self.shell.safe_execfile - if 't' in opts: - # timed execution - try: - nruns = int(opts['N'][0]) - if nruns < 1: - error('Number of runs must be >=1') - return - except (KeyError): - nruns = 1 - twall0 = time.time() - if nruns == 1: - t0 = clock2() - runner(filename, prog_ns, prog_ns, - exit_ignore=exit_ignore) - t1 = clock2() - t_usr = t1[0] - t0[0] - t_sys = t1[1] - t0[1] - print "\nIPython CPU timings (estimated):" - print " User : %10.2f s." % t_usr - print " System : %10.2f s." % t_sys - else: - runs = range(nruns) - t0 = clock2() - for nr in runs: - runner(filename, prog_ns, prog_ns, - exit_ignore=exit_ignore) - t1 = clock2() - t_usr = t1[0] - t0[0] - t_sys = t1[1] - t0[1] - print "\nIPython CPU timings (estimated):" - print "Total runs performed:", nruns - print " Times : %10.2f %10.2f" % ('Total', 'Per run') - print " User : %10.2f s, %10.2f s." % (t_usr, t_usr / nruns) - print " System : %10.2f s, %10.2f s." % (t_sys, t_sys / nruns) - twall1 = time.time() - print "Wall time: %10.2f s." % (twall1 - twall0) + varnames = self.magic_who_ls(parameter_s) + if not varnames: + if parameter_s: + print 'No variables match your requested type.' + else: + print 'Interactive namespace is empty.' + return - else: - # regular execution - runner(filename, prog_ns, prog_ns, exit_ignore=exit_ignore) + # if we have variables, move on... - if 'i' in opts: - self.shell.user_ns['__name__'] = __name__save - else: - # The shell MUST hold a reference to prog_ns so after %run - # exits, the python deletion mechanism doesn't zero it out - # (leaving dangling references). - self.shell.cache_main_mod(prog_ns, filename) - # update IPython interactive namespace + # for these types, show len() instead of data: + seq_types = ['dict', 'list', 'tuple'] - # Some forms of read errors on the file may mean the - # __name__ key was never set; using pop we don't have to - # worry about a possible KeyError. - prog_ns.pop('__name__', None) + # for numpy arrays, display summary info + ndarray_type = None + if 'numpy' in sys.modules: + try: + from numpy import ndarray + except ImportError: + pass + else: + ndarray_type = ndarray.__name__ - self.shell.user_ns.update(prog_ns) - finally: - # It's a bit of a mystery why, but __builtins__ can change from - # being a module to becoming a dict missing some key data after - # %run. As best I can see, this is NOT something IPython is doing - # at all, and similar problems have been reported before: - # http://coding.derkeiler.com/Archive/Python/comp.lang.python/2004-10/0188.html - # Since this seems to be done by the interpreter itself, the best - # we can do is to at least restore __builtins__ for the user on - # exit. - self.shell.user_ns['__builtins__'] = builtin_mod + # Find all variable names and types so we can figure out column sizes + def get_vars(i): + return self.shell.user_ns[i] - # Ensure key global structures are restored - sys.argv = save_argv - if restore_main: - sys.modules['__main__'] = restore_main + # some types are well known and can be shorter + abbrevs = {'IPython.core.macro.Macro' : 'Macro'} + def type_name(v): + tn = type(v).__name__ + return abbrevs.get(tn,tn) + + varlist = map(get_vars,varnames) + + typelist = [] + for vv in varlist: + tt = type_name(vv) + + if tt=='instance': + typelist.append( abbrevs.get(str(vv.__class__), + str(vv.__class__))) else: - # Remove from sys.modules the reference to main_mod we'd - # added. Otherwise it will trap references to objects - # contained therein. - del sys.modules[main_mod_name] + typelist.append(tt) - return stats + # column labels and # of spaces as separator + varlabel = 'Variable' + typelabel = 'Type' + datalabel = 'Data/Info' + colsep = 3 + # variable format strings + vformat = "{0:<{varwidth}}{1:<{typewidth}}" + aformat = "%s: %s elems, type `%s`, %s bytes" + # find the size of the columns to format the output nicely + varwidth = max(max(map(len,varnames)), len(varlabel)) + colsep + typewidth = max(max(map(len,typelist)), len(typelabel)) + colsep + # table header + print varlabel.ljust(varwidth) + typelabel.ljust(typewidth) + \ + ' '+datalabel+'\n' + '-'*(varwidth+typewidth+len(datalabel)+1) + # and the table itself + kb = 1024 + Mb = 1048576 # kb**2 + for vname,var,vtype in zip(varnames,varlist,typelist): + print vformat.format(vname, vtype, varwidth=varwidth, typewidth=typewidth), + if vtype in seq_types: + print "n="+str(len(var)) + elif vtype == ndarray_type: + vshape = str(var.shape).replace(',','').replace(' ','x')[1:-1] + if vtype==ndarray_type: + # numpy + vsize = var.size + vbytes = vsize*var.itemsize + vdtype = var.dtype - @skip_doctest - def magic_timeit(self, parameter_s =''): - """Time execution of a Python statement or expression + if vbytes < 100000: + print aformat % (vshape,vsize,vdtype,vbytes) + else: + print aformat % (vshape,vsize,vdtype,vbytes), + if vbytes < Mb: + print '(%s kb)' % (vbytes/kb,) + else: + print '(%s Mb)' % (vbytes/Mb,) + else: + try: + vstr = str(var) + except UnicodeEncodeError: + vstr = unicode(var).encode(DEFAULT_ENCODING, + 'backslashreplace') + except: + vstr = "" % id(var) + vstr = vstr.replace('\n','\\n') + if len(vstr) < 50: + print vstr + else: + print vstr[:25] + "<...>" + vstr[-25:] - Usage:\\ - %timeit [-n -r [-t|-c]] statement + def magic_reset(self, parameter_s=''): + """Resets the namespace by removing all names defined by the user, if + called without arguments, or by removing some types of objects, such + as everything currently in IPython's In[] and Out[] containers (see + the parameters for details). - Time execution of a Python statement or expression using the timeit - module. + Parameters + ---------- + -f : force reset without asking for confirmation. - Options: - -n: execute the given statement times in a loop. If this value - is not given, a fitting value is chosen. + -s : 'Soft' reset: Only clears your namespace, leaving history intact. + References to objects may be kept. By default (without this option), + we do a 'hard' reset, giving you a new session and removing all + references to objects from the current session. + + in : reset input history + + out : reset output history + + dhist : reset directory history + + array : reset only variables that are NumPy arrays + + See Also + -------- + magic_reset_selective : invoked as ``%reset_selective`` + + Examples + -------- + :: + + In [6]: a = 1 + + In [7]: a + Out[7]: 1 + + In [8]: 'a' in _ip.user_ns + Out[8]: True + + In [9]: %reset -f + + In [1]: 'a' in _ip.user_ns + Out[1]: False + + In [2]: %reset -f in + Flushing input history + + In [3]: %reset -f dhist in + Flushing directory history + Flushing input history + + Notes + ----- + Calling this magic from clients that do not implement standard input, + such as the ipython notebook interface, will reset the namespace + without confirmation. + """ + opts, args = self.parse_options(parameter_s,'sf', mode='list') + if 'f' in opts: + ans = True + else: + try: + ans = self.shell.ask_yes_no( + "Once deleted, variables cannot be recovered. Proceed (y/[n])? ", default='n') + except StdinNotImplementedError: + ans = True + if not ans: + print 'Nothing done.' + return + + if 's' in opts: # Soft reset + user_ns = self.shell.user_ns + for i in self.magic_who_ls(): + del(user_ns[i]) + elif len(args) == 0: # Hard reset + self.shell.reset(new_session = False) + + # reset in/out/dhist/array: previously extensinions/clearcmd.py + ip = self.shell + user_ns = self.shell.user_ns # local lookup, heavily used + + for target in args: + target = target.lower() # make matches case insensitive + if target == 'out': + print "Flushing output cache (%d entries)" % len(user_ns['_oh']) + self.shell.displayhook.flush() + + elif target == 'in': + print "Flushing input history" + pc = self.shell.displayhook.prompt_count + 1 + for n in range(1, pc): + key = '_i'+repr(n) + user_ns.pop(key,None) + user_ns.update(dict(_i=u'',_ii=u'',_iii=u'')) + hm = ip.history_manager + # don't delete these, as %save and %macro depending on the length + # of these lists to be preserved + hm.input_hist_parsed[:] = [''] * pc + hm.input_hist_raw[:] = [''] * pc + # hm has internal machinery for _i,_ii,_iii, clear it out + hm._i = hm._ii = hm._iii = hm._i00 = u'' + + elif target == 'array': + # Support cleaning up numpy arrays + try: + from numpy import ndarray + # This must be done with items and not iteritems because we're + # going to modify the dict in-place. + for x,val in user_ns.items(): + if isinstance(val,ndarray): + del user_ns[x] + except ImportError: + print "reset array only works if Numpy is available." + + elif target == 'dhist': + print "Flushing directory history" + del user_ns['_dh'][:] + + else: + print "Don't know how to reset ", + print target + ", please run `%reset?` for details" + + gc.collect() + + def magic_reset_selective(self, parameter_s=''): + """Resets the namespace by removing names defined by the user. + + Input/Output history are left around in case you need them. + + %reset_selective [-f] regex + + No action is taken if regex is not included + + Options + -f : force reset without asking for confirmation. + + See Also + -------- + magic_reset : invoked as ``%reset`` + + Examples + -------- + + We first fully reset the namespace so your output looks identical to + this example for pedagogical reasons; in practice you do not need a + full reset:: + + In [1]: %reset -f + + Now, with a clean namespace we can make a few variables and use + ``%reset_selective`` to only delete names that match our regexp:: + + In [2]: a=1; b=2; c=3; b1m=4; b2m=5; b3m=6; b4m=7; b2s=8 + + In [3]: who_ls + Out[3]: ['a', 'b', 'b1m', 'b2m', 'b2s', 'b3m', 'b4m', 'c'] + + In [4]: %reset_selective -f b[2-3]m + + In [5]: who_ls + Out[5]: ['a', 'b', 'b1m', 'b2s', 'b4m', 'c'] + + In [6]: %reset_selective -f d + + In [7]: who_ls + Out[7]: ['a', 'b', 'b1m', 'b2s', 'b4m', 'c'] + + In [8]: %reset_selective -f c + + In [9]: who_ls + Out[9]: ['a', 'b', 'b1m', 'b2s', 'b4m'] + + In [10]: %reset_selective -f b + + In [11]: who_ls + Out[11]: ['a'] + + Notes + ----- + Calling this magic from clients that do not implement standard input, + such as the ipython notebook interface, will reset the namespace + without confirmation. + """ + + opts, regex = self.parse_options(parameter_s,'f') + + if opts.has_key('f'): + ans = True + else: + try: + ans = self.shell.ask_yes_no( + "Once deleted, variables cannot be recovered. Proceed (y/[n])? ", + default='n') + except StdinNotImplementedError: + ans = True + if not ans: + print 'Nothing done.' + return + user_ns = self.shell.user_ns + if not regex: + print 'No regex pattern specified. Nothing done.' + return + else: + try: + m = re.compile(regex) + except TypeError: + raise TypeError('regex must be a string or compiled pattern') + for i in self.magic_who_ls(): + if m.search(i): + del(user_ns[i]) + + def magic_xdel(self, parameter_s=''): + """Delete a variable, trying to clear it from anywhere that + IPython's machinery has references to it. By default, this uses + the identity of the named object in the user namespace to remove + references held under other names. The object is also removed + from the output history. + + Options + -n : Delete the specified name from all namespaces, without + checking their identity. + """ + opts, varname = self.parse_options(parameter_s,'n') + try: + self.shell.del_var(varname, ('n' in opts)) + except (NameError, ValueError) as e: + print type(e).__name__ +": "+ str(e) + + +class ExecutionMagics(MagicFunctions): + """Magics related to code execution, debugging, profiling, etc. + + """ + + def __init__(self, shell): + super(ProfileMagics, self).__init__(shell) + if profile is None: + self.magic_prun = self.profile_missing_notice + # Default execution function used to actually run user code. + self.default_runner = None + + def profile_missing_notice(self, *args, **kwargs): + error("""\ +The profile module could not be found. It has been removed from the standard +python packages because of its non-free license. To use profiling, install the +python-profiler package from non-free.""") + + @skip_doctest + def magic_prun(self, parameter_s ='',user_mode=1, + opts=None,arg_lst=None,prog_ns=None): + + """Run a statement through the python code profiler. + + Usage: + %prun [options] statement + + The given statement (which doesn't require quote marks) is run via the + python profiler in a manner similar to the profile.run() function. + Namespaces are internally managed to work correctly; profile.run + cannot be used in IPython because it makes certain assumptions about + namespaces which do not hold under IPython. + + Options: + + -l : you can place restrictions on what or how much of the + profile gets printed. The limit value can be: + + * A string: only information for function names containing this string + is printed. + + * An integer: only these many lines are printed. + + * A float (between 0 and 1): this fraction of the report is printed + (for example, use a limit of 0.4 to see the topmost 40% only). + + You can combine several limits with repeated use of the option. For + example, '-l __init__ -l 5' will print only the topmost 5 lines of + information about class constructors. + + -r: return the pstats.Stats object generated by the profiling. This + object has all the information about the profile in it, and you can + later use it for further analysis or in other functions. + + -s : sort profile by given key. You can provide more than one key + by using the option several times: '-s key1 -s key2 -s key3...'. The + default sorting key is 'time'. + + The following is copied verbatim from the profile documentation + referenced below: + + When more than one key is provided, additional keys are used as + secondary criteria when the there is equality in all keys selected + before them. + + Abbreviations can be used for any key names, as long as the + abbreviation is unambiguous. The following are the keys currently + defined: + + Valid Arg Meaning + "calls" call count + "cumulative" cumulative time + "file" file name + "module" file name + "pcalls" primitive call count + "line" line number + "name" function name + "nfl" name/file/line + "stdname" standard name + "time" internal time + + Note that all sorts on statistics are in descending order (placing + most time consuming items first), where as name, file, and line number + searches are in ascending order (i.e., alphabetical). The subtle + distinction between "nfl" and "stdname" is that the standard name is a + sort of the name as printed, which means that the embedded line + numbers get compared in an odd way. For example, lines 3, 20, and 40 + would (if the file names were the same) appear in the string order + "20" "3" and "40". In contrast, "nfl" does a numeric compare of the + line numbers. In fact, sort_stats("nfl") is the same as + sort_stats("name", "file", "line"). + + -T : save profile results as shown on screen to a text + file. The profile is still shown on screen. + + -D : save (via dump_stats) profile statistics to given + filename. This data is in a format understood by the pstats module, and + is generated by a call to the dump_stats() method of profile + objects. The profile is still shown on screen. + + -q: suppress output to the pager. Best used with -T and/or -D above. + + If you want to run complete programs under the profiler's control, use + '%run -p [prof_opts] filename.py [args to program]' where prof_opts + contains profiler specific options as described here. + + You can read the complete documentation for the profile module with:: + + In [1]: import profile; profile.help() + """ + + opts_def = Struct(D=[''],l=[],s=['time'],T=['']) + + if user_mode: # regular user call + opts,arg_str = self.parse_options(parameter_s,'D:l:rs:T:q', + list_all=1, posix=False) + namespace = self.shell.user_ns + else: # called to run a program by %run -p + try: + filename = get_py_filename(arg_lst[0]) + except IOError as e: + try: + msg = str(e) + except UnicodeError: + msg = e.message + error(msg) + return + + arg_str = 'execfile(filename,prog_ns)' + namespace = { + 'execfile': self.shell.safe_execfile, + 'prog_ns': prog_ns, + 'filename': filename + } + + opts.merge(opts_def) + + prof = profile.Profile() + try: + prof = prof.runctx(arg_str,namespace,namespace) + sys_exit = '' + except SystemExit: + sys_exit = """*** SystemExit exception caught in code being profiled.""" + + stats = pstats.Stats(prof).strip_dirs().sort_stats(*opts.s) + + lims = opts.l + if lims: + lims = [] # rebuild lims with ints/floats/strings + for lim in opts.l: + try: + lims.append(int(lim)) + except ValueError: + try: + lims.append(float(lim)) + except ValueError: + lims.append(lim) + + # Trap output. + stdout_trap = StringIO() + + if hasattr(stats,'stream'): + # In newer versions of python, the stats object has a 'stream' + # attribute to write into. + stats.stream = stdout_trap + stats.print_stats(*lims) + else: + # For older versions, we manually redirect stdout during printing + sys_stdout = sys.stdout + try: + sys.stdout = stdout_trap + stats.print_stats(*lims) + finally: + sys.stdout = sys_stdout - -r: repeat the loop iteration times and take the best result. - Default: 3 + output = stdout_trap.getvalue() + output = output.rstrip() - -t: use time.time to measure the time, which is the default on Unix. - This function measures wall time. + if 'q' not in opts: + page.page(output) + print sys_exit, - -c: use time.clock to measure the time, which is the default on - Windows and measures wall time. On Unix, resource.getrusage is used - instead and returns the CPU user time. + dump_file = opts.D[0] + text_file = opts.T[0] + if dump_file: + dump_file = unquote_filename(dump_file) + prof.dump_stats(dump_file) + print '\n*** Profile stats marshalled to file',\ + `dump_file`+'.',sys_exit + if text_file: + text_file = unquote_filename(text_file) + pfile = open(text_file,'w') + pfile.write(output) + pfile.close() + print '\n*** Profile printout saved to text file',\ + `text_file`+'.',sys_exit - -p

: use a precision of

digits to display the timing result. - Default: 3 + if opts.has_key('r'): + return stats + else: + return None + def magic_pdb(self, parameter_s=''): + """Control the automatic calling of the pdb interactive debugger. - Examples - -------- - :: + Call as '%pdb on', '%pdb 1', '%pdb off' or '%pdb 0'. If called without + argument it works as a toggle. - In [1]: %timeit pass - 10000000 loops, best of 3: 53.3 ns per loop + When an exception is triggered, IPython can optionally call the + interactive pdb debugger after the traceback printout. %pdb toggles + this feature on and off. - In [2]: u = None + The initial state of this feature is set in your configuration + file (the option is ``InteractiveShell.pdb``). - In [3]: %timeit u is None - 10000000 loops, best of 3: 184 ns per loop + If you want to just activate the debugger AFTER an exception has fired, + without having to type '%pdb on' and rerunning your code, you can use + the %debug magic.""" - In [4]: %timeit -r 4 u == None - 1000000 loops, best of 4: 242 ns per loop + par = parameter_s.strip().lower() - In [5]: import time + if par: + try: + new_pdb = {'off':0,'0':0,'on':1,'1':1}[par] + except KeyError: + print ('Incorrect argument. Use on/1, off/0, ' + 'or nothing for a toggle.') + return + else: + # toggle + new_pdb = not self.shell.call_pdb - In [6]: %timeit -n1 time.sleep(2) - 1 loops, best of 3: 2 s per loop + # set on the shell + self.shell.call_pdb = new_pdb + print 'Automatic pdb calling has been turned',on_off(new_pdb) + def magic_debug(self, parameter_s=''): + """Activate the interactive debugger in post-mortem mode. - The times reported by %timeit will be slightly higher than those - reported by the timeit.py script when variables are accessed. This is - due to the fact that %timeit executes the statement in the namespace - of the shell, compared with timeit.py, which uses a single setup - statement to import function or create variables. Generally, the bias - does not matter as long as results from timeit.py are not mixed with - those from %timeit.""" + If an exception has just occurred, this lets you inspect its stack + frames interactively. Note that this will always work only on the last + traceback that occurred, so you must call this quickly after an + exception that you wish to inspect has fired, because if another one + occurs, it clobbers the previous one. - import timeit - import math + If you want IPython to automatically do this on every exception, see + the %pdb magic for more details. + """ + self.shell.debugger(force=True) - # XXX: Unfortunately the unicode 'micro' symbol can cause problems in - # certain terminals. Until we figure out a robust way of - # auto-detecting if the terminal can deal with it, use plain 'us' for - # microseconds. I am really NOT happy about disabling the proper - # 'micro' prefix, but crashing is worse... If anyone knows what the - # right solution for this is, I'm all ears... - # - # Note: using - # - # s = u'\xb5' - # s.encode(sys.getdefaultencoding()) - # - # is not sufficient, as I've seen terminals where that fails but - # print s - # - # succeeds - # - # See bug: https://bugs.launchpad.net/ipython/+bug/348466 + def magic_tb(self, s): + """Print the last traceback with the currently active exception mode. - #units = [u"s", u"ms",u'\xb5',"ns"] - units = [u"s", u"ms",u'us',"ns"] + See %xmode for changing exception reporting modes.""" + self.shell.showtraceback() - scaling = [1, 1e3, 1e6, 1e9] + @skip_doctest + def magic_run(self, parameter_s ='', runner=None, + file_finder=get_py_filename): + """Run the named file inside IPython as a program. - opts, stmt = self.parse_options(parameter_s,'n:r:tcp:', - posix=False, strict=False) - if stmt == "": - return - timefunc = timeit.default_timer - number = int(getattr(opts, "n", 0)) - repeat = int(getattr(opts, "r", timeit.default_repeat)) - precision = int(getattr(opts, "p", 3)) - if hasattr(opts, "t"): - timefunc = time.time - if hasattr(opts, "c"): - timefunc = clock + Usage:\\ + %run [-n -i -t [-N] -d [-b] -p [profile options]] file [args] - timer = timeit.Timer(timer=timefunc) - # this code has tight coupling to the inner workings of timeit.Timer, - # but is there a better way to achieve that the code stmt has access - # to the shell namespace? + Parameters after the filename are passed as command-line arguments to + the program (put in sys.argv). Then, control returns to IPython's + prompt. - src = timeit.template % {'stmt': timeit.reindent(stmt, 8), - 'setup': "pass"} - # Track compilation time so it can be reported if too long - # Minimum time above which compilation time will be reported - tc_min = 0.1 + This is similar to running at a system prompt:\\ + $ python file args\\ + but with the advantage of giving you IPython's tracebacks, and of + loading all variables into your interactive namespace for further use + (unless -p is used, see below). - t0 = clock() - code = compile(src, "", "exec") - tc = clock()-t0 + The file is executed in a namespace initially consisting only of + __name__=='__main__' and sys.argv constructed as indicated. It thus + sees its environment as if it were being run as a stand-alone program + (except for sharing global objects such as previously imported + modules). But after execution, the IPython interactive namespace gets + updated with all variables defined in the program (except for __name__ + and sys.argv). This allows for very convenient loading of code for + interactive work, while giving each program a 'clean sheet' to run in. - ns = {} - exec code in self.shell.user_ns, ns - timer.inner = ns["inner"] + Options: - if number == 0: - # determine number so that 0.2 <= total time < 2.0 - number = 1 - for i in range(1, 10): - if timer.timeit(number) >= 0.2: - break - number *= 10 + -n: __name__ is NOT set to '__main__', but to the running file's name + without extension (as python does under import). This allows running + scripts and reloading the definitions in them without calling code + protected by an ' if __name__ == "__main__" ' clause. - best = min(timer.repeat(repeat, number)) / number + -i: run the file in IPython's namespace instead of an empty one. This + is useful if you are experimenting with code written in a text editor + which depends on variables defined interactively. - if best > 0.0 and best < 1000.0: - order = min(-int(math.floor(math.log10(best)) // 3), 3) - elif best >= 1000.0: - order = 0 - else: - order = 3 - print u"%d loops, best of %d: %.*g %s per loop" % (number, repeat, - precision, - best * scaling[order], - units[order]) - if tc > tc_min: - print "Compiler time: %.2f s" % tc + -e: ignore sys.exit() calls or SystemExit exceptions in the script + being run. This is particularly useful if IPython is being used to + run unittests, which always exit with a sys.exit() call. In such + cases you are interested in the output of the test results, not in + seeing a traceback of the unittest module. - @skip_doctest - @needs_local_scope - def magic_time(self,parameter_s, user_locals): - """Time execution of a Python statement or expression. + -t: print timing information at the end of the run. IPython will give + you an estimated CPU time consumption for your script, which under + Unix uses the resource module to avoid the wraparound problems of + time.clock(). Under Unix, an estimate of time spent on system tasks + is also given (for Windows platforms this is reported as 0.0). - The CPU and wall clock times are printed, and the value of the - expression (if any) is returned. Note that under Win32, system time - is always reported as 0, since it can not be measured. + If -t is given, an additional -N option can be given, where + must be an integer indicating how many times you want the script to + run. The final timing report will include total and per run results. - This function provides very basic timing functionality. In Python - 2.3, the timeit module offers more control and sophistication, so this - could be rewritten to use it (patches welcome). + For example (testing the script uniq_stable.py):: - Examples - -------- - :: + In [1]: run -t uniq_stable - In [1]: time 2**128 - CPU times: user 0.00 s, sys: 0.00 s, total: 0.00 s - Wall time: 0.00 - Out[1]: 340282366920938463463374607431768211456L + IPython CPU timings (estimated):\\ + User : 0.19597 s.\\ + System: 0.0 s.\\ - In [2]: n = 1000000 + In [2]: run -t -N5 uniq_stable - In [3]: time sum(range(n)) - CPU times: user 1.20 s, sys: 0.05 s, total: 1.25 s - Wall time: 1.37 - Out[3]: 499999500000L + IPython CPU timings (estimated):\\ + Total runs performed: 5\\ + Times : Total Per run\\ + User : 0.910862 s, 0.1821724 s.\\ + System: 0.0 s, 0.0 s. - In [4]: time print 'hello world' - hello world - CPU times: user 0.00 s, sys: 0.00 s, total: 0.00 s - Wall time: 0.00 + -d: run your program under the control of pdb, the Python debugger. + This allows you to execute your program step by step, watch variables, + etc. Internally, what IPython does is similar to calling: - Note that the time needed by Python to compile the given expression - will be reported if it is more than 0.1s. In this example, the - actual exponentiation is done by Python at compilation time, so while - the expression can take a noticeable amount of time to compute, that - time is purely due to the compilation: + pdb.run('execfile("YOURFILENAME")') - In [5]: time 3**9999; - CPU times: user 0.00 s, sys: 0.00 s, total: 0.00 s - Wall time: 0.00 s + with a breakpoint set on line 1 of your file. You can change the line + number for this automatic breakpoint to be by using the -bN option + (where N must be an integer). For example:: - In [6]: time 3**999999; - CPU times: user 0.00 s, sys: 0.00 s, total: 0.00 s - Wall time: 0.00 s - Compiler : 0.78 s - """ + %run -d -b40 myscript - # fail immediately if the given expression can't be compiled + will set the first breakpoint at line 40 in myscript.py. Note that + the first breakpoint must be set on a line which actually does + something (not a comment or docstring) for it to stop execution. - expr = self.shell.prefilter(parameter_s,False) + When the pdb debugger starts, you will see a (Pdb) prompt. You must + first enter 'c' (without quotes) to start execution up to the first + breakpoint. - # Minimum time above which compilation time will be reported - tc_min = 0.1 + Entering 'help' gives information about the use of the debugger. You + can easily see pdb's full documentation with "import pdb;pdb.help()" + at a prompt. - try: - mode = 'eval' - t0 = clock() - code = compile(expr,'',mode) - tc = clock()-t0 - except SyntaxError: - mode = 'exec' - t0 = clock() - code = compile(expr,'',mode) - tc = clock()-t0 - # skew measurement as little as possible - glob = self.shell.user_ns - wtime = time.time - # time execution - wall_st = wtime() - if mode=='eval': - st = clock2() - out = eval(code, glob, user_locals) - end = clock2() - else: - st = clock2() - exec code in glob, user_locals - end = clock2() - out = None - wall_end = wtime() - # Compute actual times and report - wall_time = wall_end-wall_st - cpu_user = end[0]-st[0] - cpu_sys = end[1]-st[1] - cpu_tot = cpu_user+cpu_sys - print "CPU times: user %.2f s, sys: %.2f s, total: %.2f s" % \ - (cpu_user,cpu_sys,cpu_tot) - print "Wall time: %.2f s" % wall_time - if tc > tc_min: - print "Compiler : %.2f s" % tc - return out + -p: run program under the control of the Python profiler module (which + prints a detailed report of execution times, function calls, etc). - @skip_doctest - def magic_macro(self,parameter_s = ''): - """Define a macro for future re-execution. It accepts ranges of history, - filenames or string objects. + You can pass other options after -p which affect the behavior of the + profiler itself. See the docs for %prun for details. - Usage:\\ - %macro [options] name n1-n2 n3-n4 ... n5 .. n6 ... + In this mode, the program's variables do NOT propagate back to the + IPython interactive namespace (because they remain in the namespace + where the profiler executes them). - Options: + Internally this triggers a call to %prun, see its documentation for + details on the options available specifically for profiling. - -r: use 'raw' input. By default, the 'processed' history is used, - so that magics are loaded in their transformed version to valid - Python. If this option is given, the raw input as typed as the - command line is used instead. + There is one special usage for which the text above doesn't apply: + if the filename ends with .ipy, the file is run as ipython script, + just as if the commands were written on IPython prompt. - This will define a global variable called `name` which is a string - made of joining the slices and lines you specify (n1,n2,... numbers - above) from your input history into a single string. This variable - acts like an automatic function which re-executes those lines as if - you had typed them. You just type 'name' at the prompt and the code - executes. + -m: specify module name to load instead of script path. Similar to + the -m option for the python interpreter. Use this option last if you + want to combine with other %run options. Unlike the python interpreter + only source modules are allowed no .pyc or .pyo files. + For example:: - The syntax for indicating input ranges is described in %history. + %run -m example - Note: as a 'hidden' feature, you can also use traditional python slice - notation, where N:M means numbers N through M-1. + will run the example module. - For example, if your history contains (%hist prints it):: + """ - 44: x=1 - 45: y=3 - 46: z=x+y - 47: print x - 48: a=5 - 49: print 'x',x,'y',y + # get arguments and set sys.argv for program to be run. + opts, arg_lst = self.parse_options(parameter_s, 'nidtN:b:pD:l:rs:T:em:', + mode='list', list_all=1) + if "m" in opts: + modulename = opts["m"][0] + modpath = find_mod(modulename) + if modpath is None: + warn('%r is not a valid modulename on sys.path'%modulename) + return + arg_lst = [modpath] + arg_lst + try: + filename = file_finder(arg_lst[0]) + except IndexError: + warn('you must provide at least a filename.') + print '\n%run:\n', oinspect.getdoc(self.magic_run) + return + except IOError as e: + try: + msg = str(e) + except UnicodeError: + msg = e.message + error(msg) + return - you can create a macro with lines 44 through 47 (included) and line 49 - called my_macro with:: + if filename.lower().endswith('.ipy'): + self.shell.safe_execfile_ipy(filename) + return - In [55]: %macro my_macro 44-47 49 + # Control the response to exit() calls made by the script being run + exit_ignore = 'e' in opts - Now, typing `my_macro` (without quotes) will re-execute all this code - in one pass. + # Make sure that the running script gets a proper sys.argv as if it + # were run from a system shell. + save_argv = sys.argv # save it for later restoring - You don't need to give the line-numbers in order, and any given line - number can appear multiple times. You can assemble macros with any - lines from your input history in any order. + # simulate shell expansion on arguments, at least tilde expansion + args = [ os.path.expanduser(a) for a in arg_lst[1:] ] - The macro is a simple object which holds its value in an attribute, - but IPython's display system checks for macros and executes them as - code instead of printing them when you type their name. + sys.argv = [filename] + args # put in the proper filename + # protect sys.argv from potential unicode strings on Python 2: + if not py3compat.PY3: + sys.argv = [ py3compat.cast_bytes(a) for a in sys.argv ] - You can view a macro's contents by explicitly printing it with:: + if 'i' in opts: + # Run in user's interactive namespace + prog_ns = self.shell.user_ns + __name__save = self.shell.user_ns['__name__'] + prog_ns['__name__'] = '__main__' + main_mod = self.shell.new_main_mod(prog_ns) + else: + # Run in a fresh, empty namespace + if 'n' in opts: + name = os.path.splitext(os.path.basename(filename))[0] + else: + name = '__main__' - print macro_name + main_mod = self.shell.new_main_mod() + prog_ns = main_mod.__dict__ + prog_ns['__name__'] = name - """ - opts,args = self.parse_options(parameter_s,'r',mode='list') - if not args: # List existing macros - return sorted(k for k,v in self.shell.user_ns.iteritems() if\ - isinstance(v, Macro)) - if len(args) == 1: - raise UsageError( - "%macro insufficient args; usage '%macro name n1-n2 n3-4...") - name, codefrom = args[0], " ".join(args[1:]) + # Since '%run foo' emulates 'python foo.py' at the cmd line, we must + # set the __file__ global in the script's namespace + prog_ns['__file__'] = filename - #print 'rng',ranges # dbg - try: - lines = self.shell.find_user_code(codefrom, 'r' in opts) - except (ValueError, TypeError) as e: - print e.args[0] - return - macro = Macro(lines) - self.shell.define_macro(name, macro) - print 'Macro `%s` created. To execute, type its name (without quotes).' % name - print '=== Macro contents: ===' - print macro, + # pickle fix. See interactiveshell for an explanation. But we need to make sure + # that, if we overwrite __main__, we replace it at the end + main_mod_name = prog_ns['__name__'] - def magic_save(self,parameter_s = ''): - """Save a set of lines or a macro to a given filename. + if main_mod_name == '__main__': + restore_main = sys.modules['__main__'] + else: + restore_main = False - Usage:\\ - %save [options] filename n1-n2 n3-n4 ... n5 .. n6 ... + # This needs to be undone at the end to prevent holding references to + # every single object ever created. + sys.modules[main_mod_name] = main_mod + + try: + stats = None + with self.shell.readline_no_record: + if 'p' in opts: + stats = self.magic_prun('', 0, opts, arg_lst, prog_ns) + else: + if 'd' in opts: + deb = debugger.Pdb(self.shell.colors) + # reset Breakpoint state, which is moronically kept + # in a class + bdb.Breakpoint.next = 1 + bdb.Breakpoint.bplist = {} + bdb.Breakpoint.bpbynumber = [None] + # Set an initial breakpoint to stop execution + maxtries = 10 + bp = int(opts.get('b', [1])[0]) + checkline = deb.checkline(filename, bp) + if not checkline: + for bp in range(bp + 1, bp + maxtries + 1): + if deb.checkline(filename, bp): + break + else: + msg = ("\nI failed to find a valid line to set " + "a breakpoint\n" + "after trying up to line: %s.\n" + "Please set a valid breakpoint manually " + "with the -b option." % bp) + error(msg) + return + # if we find a good linenumber, set the breakpoint + deb.do_break('%s:%s' % (filename, bp)) + # Start file run + print "NOTE: Enter 'c' at the", + print "%s prompt to start your script." % deb.prompt + ns = {'execfile': py3compat.execfile, 'prog_ns': prog_ns} + try: + deb.run('execfile("%s", prog_ns)' % filename, ns) - Options: + except: + etype, value, tb = sys.exc_info() + # Skip three frames in the traceback: the %run one, + # one inside bdb.py, and the command-line typed by the + # user (run by exec in pdb itself). + self.shell.InteractiveTB(etype, value, tb, tb_offset=3) + else: + if runner is None: + runner = self.default_runner + if runner is None: + runner = self.shell.safe_execfile + if 't' in opts: + # timed execution + try: + nruns = int(opts['N'][0]) + if nruns < 1: + error('Number of runs must be >=1') + return + except (KeyError): + nruns = 1 + twall0 = time.time() + if nruns == 1: + t0 = clock2() + runner(filename, prog_ns, prog_ns, + exit_ignore=exit_ignore) + t1 = clock2() + t_usr = t1[0] - t0[0] + t_sys = t1[1] - t0[1] + print "\nIPython CPU timings (estimated):" + print " User : %10.2f s." % t_usr + print " System : %10.2f s." % t_sys + else: + runs = range(nruns) + t0 = clock2() + for nr in runs: + runner(filename, prog_ns, prog_ns, + exit_ignore=exit_ignore) + t1 = clock2() + t_usr = t1[0] - t0[0] + t_sys = t1[1] - t0[1] + print "\nIPython CPU timings (estimated):" + print "Total runs performed:", nruns + print " Times : %10.2f %10.2f" % ('Total', 'Per run') + print " User : %10.2f s, %10.2f s." % (t_usr, t_usr / nruns) + print " System : %10.2f s, %10.2f s." % (t_sys, t_sys / nruns) + twall1 = time.time() + print "Wall time: %10.2f s." % (twall1 - twall0) - -r: use 'raw' input. By default, the 'processed' history is used, - so that magics are loaded in their transformed version to valid - Python. If this option is given, the raw input as typed as the - command line is used instead. + else: + # regular execution + runner(filename, prog_ns, prog_ns, exit_ignore=exit_ignore) - This function uses the same syntax as %history for input ranges, - then saves the lines to the filename you specify. + if 'i' in opts: + self.shell.user_ns['__name__'] = __name__save + else: + # The shell MUST hold a reference to prog_ns so after %run + # exits, the python deletion mechanism doesn't zero it out + # (leaving dangling references). + self.shell.cache_main_mod(prog_ns, filename) + # update IPython interactive namespace - It adds a '.py' extension to the file if you don't do so yourself, and - it asks for confirmation before overwriting existing files.""" + # Some forms of read errors on the file may mean the + # __name__ key was never set; using pop we don't have to + # worry about a possible KeyError. + prog_ns.pop('__name__', None) - opts,args = self.parse_options(parameter_s,'r',mode='list') - fname, codefrom = unquote_filename(args[0]), " ".join(args[1:]) - if not fname.endswith('.py'): - fname += '.py' - if os.path.isfile(fname): - overwrite = self.shell.ask_yes_no('File `%s` exists. Overwrite (y/[N])? ' % fname, default='n') - if not overwrite : - print 'Operation cancelled.' - return - try: - cmds = self.shell.find_user_code(codefrom, 'r' in opts) - except (TypeError, ValueError) as e: - print e.args[0] - return - with io.open(fname,'w', encoding="utf-8") as f: - f.write(u"# coding: utf-8\n") - f.write(py3compat.cast_unicode(cmds)) - print 'The following commands were written to file `%s`:' % fname - print cmds + self.shell.user_ns.update(prog_ns) + finally: + # It's a bit of a mystery why, but __builtins__ can change from + # being a module to becoming a dict missing some key data after + # %run. As best I can see, this is NOT something IPython is doing + # at all, and similar problems have been reported before: + # http://coding.derkeiler.com/Archive/Python/comp.lang.python/2004-10/0188.html + # Since this seems to be done by the interpreter itself, the best + # we can do is to at least restore __builtins__ for the user on + # exit. + self.shell.user_ns['__builtins__'] = builtin_mod - def magic_pastebin(self, parameter_s = ''): - """Upload code to Github's Gist paste bin, returning the URL. - - Usage:\\ - %pastebin [-d "Custom description"] 1-7 - - The argument can be an input history range, a filename, or the name of a - string or macro. - - Options: - - -d: Pass a custom description for the gist. The default will say - "Pasted from IPython". - """ - opts, args = self.parse_options(parameter_s, 'd:') - - try: - code = self.shell.find_user_code(args) - except (ValueError, TypeError) as e: - print e.args[0] - return - - post_data = json.dumps({ - "description": opts.get('d', "Pasted from IPython"), - "public": True, - "files": { - "file1.py": { - "content": code - } - } - }).encode('utf-8') - - response = urlopen("https://api.github.com/gists", post_data) - response_data = json.loads(response.read().decode('utf-8')) - return response_data['html_url'] + # Ensure key global structures are restored + sys.argv = save_argv + if restore_main: + sys.modules['__main__'] = restore_main + else: + # Remove from sys.modules the reference to main_mod we'd + # added. Otherwise it will trap references to objects + # contained therein. + del sys.modules[main_mod_name] - def magic_loadpy(self, arg_s): - """Alias of `%load` - - `%loadpy` has gained some flexibility and droped the requirement of a `.py` - extension. So it has been renamed simply into %load. You can look at - `%load`'s docstring for more info. - """ - self.magic_load(arg_s) + return stats - def magic_load(self, arg_s): - """Load code into the current frontend. + @skip_doctest + def magic_timeit(self, parameter_s =''): + """Time execution of a Python statement or expression Usage:\\ - %load [options] source + %timeit [-n -r [-t|-c]] statement - where source can be a filename, URL, input history range or macro + Time execution of a Python statement or expression using the timeit + module. Options: - -------- - -y : Don't ask confirmation for loading source above 200 000 characters. + -n: execute the given statement times in a loop. If this value + is not given, a fitting value is chosen. - This magic command can either take a local filename, a URL, an history - range (see %history) or a macro as argument, it will prompt for - confirmation before loading source with more than 200 000 characters, unless - -y flag is passed or if the frontend does not support raw_input:: + -r: repeat the loop iteration times and take the best result. + Default: 3 - %load myscript.py - %load 7-27 - %load myMacro - %load http://www.example.com/myscript.py - """ - opts,args = self.parse_options(arg_s,'y') + -t: use time.time to measure the time, which is the default on Unix. + This function measures wall time. - contents = self.shell.find_user_code(args) - l = len(contents) + -c: use time.clock to measure the time, which is the default on + Windows and measures wall time. On Unix, resource.getrusage is used + instead and returns the CPU user time. - # 200 000 is ~ 2500 full 80 caracter lines - # so in average, more than 5000 lines - if l > 200000 and 'y' not in opts: - try: - ans = self.shell.ask_yes_no(("The text you're trying to load seems pretty big"\ - " (%d characters). Continue (y/[N]) ?" % l), default='n' ) - except StdinNotImplementedError: - #asume yes if raw input not implemented - ans = True + -p

: use a precision of

digits to display the timing result. + Default: 3 - if ans is False : - print 'Operation cancelled.' - return - self.set_next_input(contents) + Examples + -------- + :: - def _find_edit_target(self, args, opts, last_call): - """Utility method used by magic_edit to find what to edit.""" + In [1]: %timeit pass + 10000000 loops, best of 3: 53.3 ns per loop - def make_filename(arg): - "Make a filename from the given args" - arg = unquote_filename(arg) - try: - filename = get_py_filename(arg) - except IOError: - # If it ends with .py but doesn't already exist, assume we want - # a new file. - if arg.endswith('.py'): - filename = arg - else: - filename = None - return filename + In [2]: u = None - # Set a few locals from the options for convenience: - opts_prev = 'p' in opts - opts_raw = 'r' in opts + In [3]: %timeit u is None + 10000000 loops, best of 3: 184 ns per loop - # custom exceptions - class DataIsObject(Exception): pass + In [4]: %timeit -r 4 u == None + 1000000 loops, best of 4: 242 ns per loop - # Default line number value - lineno = opts.get('n',None) + In [5]: import time - if opts_prev: - args = '_%s' % last_call[0] - if not self.shell.user_ns.has_key(args): - args = last_call[1] + In [6]: %timeit -n1 time.sleep(2) + 1 loops, best of 3: 2 s per loop - # use last_call to remember the state of the previous call, but don't - # let it be clobbered by successive '-p' calls. - try: - last_call[0] = self.shell.displayhook.prompt_count - if not opts_prev: - last_call[1] = args - except: - pass - # by default this is done with temp files, except when the given - # arg is a filename - use_temp = True + The times reported by %timeit will be slightly higher than those + reported by the timeit.py script when variables are accessed. This is + due to the fact that %timeit executes the statement in the namespace + of the shell, compared with timeit.py, which uses a single setup + statement to import function or create variables. Generally, the bias + does not matter as long as results from timeit.py are not mixed with + those from %timeit.""" - data = '' + import timeit + import math - # First, see if the arguments should be a filename. - filename = make_filename(args) - if filename: - use_temp = False - elif args: - # Mode where user specifies ranges of lines, like in %macro. - data = self.shell.extract_input_lines(args, opts_raw) - if not data: - try: - # Load the parameter given as a variable. If not a string, - # process it as an object instead (below) + # XXX: Unfortunately the unicode 'micro' symbol can cause problems in + # certain terminals. Until we figure out a robust way of + # auto-detecting if the terminal can deal with it, use plain 'us' for + # microseconds. I am really NOT happy about disabling the proper + # 'micro' prefix, but crashing is worse... If anyone knows what the + # right solution for this is, I'm all ears... + # + # Note: using + # + # s = u'\xb5' + # s.encode(sys.getdefaultencoding()) + # + # is not sufficient, as I've seen terminals where that fails but + # print s + # + # succeeds + # + # See bug: https://bugs.launchpad.net/ipython/+bug/348466 - #print '*** args',args,'type',type(args) # dbg - data = eval(args, self.shell.user_ns) - if not isinstance(data, basestring): - raise DataIsObject + #units = [u"s", u"ms",u'\xb5',"ns"] + units = [u"s", u"ms",u'us',"ns"] - except (NameError,SyntaxError): - # given argument is not a variable, try as a filename - filename = make_filename(args) - if filename is None: - warn("Argument given (%s) can't be found as a variable " - "or as a filename." % args) - return - use_temp = False + scaling = [1, 1e3, 1e6, 1e9] - except DataIsObject: - # macros have a special edit function - if isinstance(data, Macro): - raise MacroToEdit(data) + opts, stmt = self.parse_options(parameter_s,'n:r:tcp:', + posix=False, strict=False) + if stmt == "": + return + timefunc = timeit.default_timer + number = int(getattr(opts, "n", 0)) + repeat = int(getattr(opts, "r", timeit.default_repeat)) + precision = int(getattr(opts, "p", 3)) + if hasattr(opts, "t"): + timefunc = time.time + if hasattr(opts, "c"): + timefunc = clock - # For objects, try to edit the file where they are defined - try: - filename = inspect.getabsfile(data) - if 'fakemodule' in filename.lower() and inspect.isclass(data): - # class created by %edit? Try to find source - # by looking for method definitions instead, the - # __module__ in those classes is FakeModule. - attrs = [getattr(data, aname) for aname in dir(data)] - for attr in attrs: - if not inspect.ismethod(attr): - continue - filename = inspect.getabsfile(attr) - if filename and 'fakemodule' not in filename.lower(): - # change the attribute to be the edit target instead - data = attr - break + timer = timeit.Timer(timer=timefunc) + # this code has tight coupling to the inner workings of timeit.Timer, + # but is there a better way to achieve that the code stmt has access + # to the shell namespace? - datafile = 1 - except TypeError: - filename = make_filename(args) - datafile = 1 - warn('Could not find file where `%s` is defined.\n' - 'Opening a file named `%s`' % (args,filename)) - # Now, make sure we can actually read the source (if it was in - # a temp file it's gone by now). - if datafile: - try: - if lineno is None: - lineno = inspect.getsourcelines(data)[1] - except IOError: - filename = make_filename(args) - if filename is None: - warn('The file `%s` where `%s` was defined cannot ' - 'be read.' % (filename,data)) - return - use_temp = False + src = timeit.template % {'stmt': timeit.reindent(stmt, 8), + 'setup': "pass"} + # Track compilation time so it can be reported if too long + # Minimum time above which compilation time will be reported + tc_min = 0.1 - if use_temp: - filename = self.shell.mktempfile(data) - print 'IPython will make a temporary file named:',filename + t0 = clock() + code = compile(src, "", "exec") + tc = clock()-t0 - return filename, lineno, use_temp + ns = {} + exec code in self.shell.user_ns, ns + timer.inner = ns["inner"] - def _edit_macro(self,mname,macro): - """open an editor with the macro data in a file""" - filename = self.shell.mktempfile(macro.value) - self.shell.hooks.editor(filename) + if number == 0: + # determine number so that 0.2 <= total time < 2.0 + number = 1 + for i in range(1, 10): + if timer.timeit(number) >= 0.2: + break + number *= 10 - # and make a new macro object, to replace the old one - mfile = open(filename) - mvalue = mfile.read() - mfile.close() - self.shell.user_ns[mname] = Macro(mvalue) + best = min(timer.repeat(repeat, number)) / number - def magic_ed(self,parameter_s=''): - """Alias to %edit.""" - return self.magic_edit(parameter_s) + if best > 0.0 and best < 1000.0: + order = min(-int(math.floor(math.log10(best)) // 3), 3) + elif best >= 1000.0: + order = 0 + else: + order = 3 + print u"%d loops, best of %d: %.*g %s per loop" % (number, repeat, + precision, + best * scaling[order], + units[order]) + if tc > tc_min: + print "Compiler time: %.2f s" % tc @skip_doctest - def magic_edit(self,parameter_s='',last_call=['','']): - """Bring up an editor and execute the resulting code. - - Usage: - %edit [options] [args] - - %edit runs IPython's editor hook. The default version of this hook is - set to call the editor specified by your $EDITOR environment variable. - If this isn't found, it will default to vi under Linux/Unix and to - notepad under Windows. See the end of this docstring for how to change - the editor hook. + @needs_local_scope + def magic_time(self,parameter_s, user_locals): + """Time execution of a Python statement or expression. - You can also set the value of this editor via the - ``TerminalInteractiveShell.editor`` option in your configuration file. - This is useful if you wish to use a different editor from your typical - default with IPython (and for Windows users who typically don't set - environment variables). + The CPU and wall clock times are printed, and the value of the + expression (if any) is returned. Note that under Win32, system time + is always reported as 0, since it can not be measured. - This command allows you to conveniently edit multi-line code right in - your IPython session. + This function provides very basic timing functionality. In Python + 2.3, the timeit module offers more control and sophistication, so this + could be rewritten to use it (patches welcome). - If called without arguments, %edit opens up an empty editor with a - temporary file and will execute the contents of this file when you - close it (don't forget to save it!). + Examples + -------- + :: + In [1]: time 2**128 + CPU times: user 0.00 s, sys: 0.00 s, total: 0.00 s + Wall time: 0.00 + Out[1]: 340282366920938463463374607431768211456L - Options: + In [2]: n = 1000000 - -n : open the editor at a specified line number. By default, - the IPython editor hook uses the unix syntax 'editor +N filename', but - you can configure this by providing your own modified hook if your - favorite editor supports line-number specifications with a different - syntax. + In [3]: time sum(range(n)) + CPU times: user 1.20 s, sys: 0.05 s, total: 1.25 s + Wall time: 1.37 + Out[3]: 499999500000L - -p: this will call the editor with the same data as the previous time - it was used, regardless of how long ago (in your current session) it - was. + In [4]: time print 'hello world' + hello world + CPU times: user 0.00 s, sys: 0.00 s, total: 0.00 s + Wall time: 0.00 - -r: use 'raw' input. This option only applies to input taken from the - user's history. By default, the 'processed' history is used, so that - magics are loaded in their transformed version to valid Python. If - this option is given, the raw input as typed as the command line is - used instead. When you exit the editor, it will be executed by - IPython's own processor. + Note that the time needed by Python to compile the given expression + will be reported if it is more than 0.1s. In this example, the + actual exponentiation is done by Python at compilation time, so while + the expression can take a noticeable amount of time to compute, that + time is purely due to the compilation: - -x: do not execute the edited code immediately upon exit. This is - mainly useful if you are editing programs which need to be called with - command line arguments, which you can then do using %run. + In [5]: time 3**9999; + CPU times: user 0.00 s, sys: 0.00 s, total: 0.00 s + Wall time: 0.00 s + In [6]: time 3**999999; + CPU times: user 0.00 s, sys: 0.00 s, total: 0.00 s + Wall time: 0.00 s + Compiler : 0.78 s + """ - Arguments: + # fail immediately if the given expression can't be compiled - If arguments are given, the following possibilities exist: + expr = self.shell.prefilter(parameter_s,False) - - If the argument is a filename, IPython will load that into the - editor. It will execute its contents with execfile() when you exit, - loading any code in the file into your interactive namespace. + # Minimum time above which compilation time will be reported + tc_min = 0.1 - - The arguments are ranges of input history, e.g. "7 ~1/4-6". - The syntax is the same as in the %history magic. + try: + mode = 'eval' + t0 = clock() + code = compile(expr,'',mode) + tc = clock()-t0 + except SyntaxError: + mode = 'exec' + t0 = clock() + code = compile(expr,'',mode) + tc = clock()-t0 + # skew measurement as little as possible + glob = self.shell.user_ns + wtime = time.time + # time execution + wall_st = wtime() + if mode=='eval': + st = clock2() + out = eval(code, glob, user_locals) + end = clock2() + else: + st = clock2() + exec code in glob, user_locals + end = clock2() + out = None + wall_end = wtime() + # Compute actual times and report + wall_time = wall_end-wall_st + cpu_user = end[0]-st[0] + cpu_sys = end[1]-st[1] + cpu_tot = cpu_user+cpu_sys + print "CPU times: user %.2f s, sys: %.2f s, total: %.2f s" % \ + (cpu_user,cpu_sys,cpu_tot) + print "Wall time: %.2f s" % wall_time + if tc > tc_min: + print "Compiler : %.2f s" % tc + return out - - If the argument is a string variable, its contents are loaded - into the editor. You can thus edit any string which contains - python code (including the result of previous edits). + @skip_doctest + def magic_macro(self,parameter_s = ''): + """Define a macro for future re-execution. It accepts ranges of history, + filenames or string objects. - - If the argument is the name of an object (other than a string), - IPython will try to locate the file where it was defined and open the - editor at the point where it is defined. You can use `%edit function` - to load an editor exactly at the point where 'function' is defined, - edit it and have the file be executed automatically. + Usage:\\ + %macro [options] name n1-n2 n3-n4 ... n5 .. n6 ... - - If the object is a macro (see %macro for details), this opens up your - specified editor with a temporary file containing the macro's data. - Upon exit, the macro is reloaded with the contents of the file. + Options: - Note: opening at an exact line is only supported under Unix, and some - editors (like kedit and gedit up to Gnome 2.8) do not understand the - '+NUMBER' parameter necessary for this feature. Good editors like - (X)Emacs, vi, jed, pico and joe all do. + -r: use 'raw' input. By default, the 'processed' history is used, + so that magics are loaded in their transformed version to valid + Python. If this option is given, the raw input as typed as the + command line is used instead. - After executing your code, %edit will return as output the code you - typed in the editor (except when it was an existing file). This way - you can reload the code in further invocations of %edit as a variable, - via _ or Out[], where is the prompt number of - the output. + This will define a global variable called `name` which is a string + made of joining the slices and lines you specify (n1,n2,... numbers + above) from your input history into a single string. This variable + acts like an automatic function which re-executes those lines as if + you had typed them. You just type 'name' at the prompt and the code + executes. - Note that %edit is also available through the alias %ed. + The syntax for indicating input ranges is described in %history. - This is an example of creating a simple function inside the editor and - then modifying it. First, start up the editor:: + Note: as a 'hidden' feature, you can also use traditional python slice + notation, where N:M means numbers N through M-1. - In [1]: ed - Editing... done. Executing edited code... - Out[1]: 'def foo():\\n print "foo() was defined in an editing - session"\\n' + For example, if your history contains (%hist prints it):: - We can then call the function foo():: + 44: x=1 + 45: y=3 + 46: z=x+y + 47: print x + 48: a=5 + 49: print 'x',x,'y',y - In [2]: foo() - foo() was defined in an editing session + you can create a macro with lines 44 through 47 (included) and line 49 + called my_macro with:: - Now we edit foo. IPython automatically loads the editor with the - (temporary) file where foo() was previously defined:: + In [55]: %macro my_macro 44-47 49 - In [3]: ed foo - Editing... done. Executing edited code... + Now, typing `my_macro` (without quotes) will re-execute all this code + in one pass. - And if we call foo() again we get the modified version:: + You don't need to give the line-numbers in order, and any given line + number can appear multiple times. You can assemble macros with any + lines from your input history in any order. - In [4]: foo() - foo() has now been changed! + The macro is a simple object which holds its value in an attribute, + but IPython's display system checks for macros and executes them as + code instead of printing them when you type their name. - Here is an example of how to edit a code snippet successive - times. First we call the editor:: + You can view a macro's contents by explicitly printing it with:: - In [5]: ed - Editing... done. Executing edited code... - hello - Out[5]: "print 'hello'\\n" + print macro_name - Now we call it again with the previous output (stored in _):: + """ + opts,args = self.parse_options(parameter_s,'r',mode='list') + if not args: # List existing macros + return sorted(k for k,v in self.shell.user_ns.iteritems() if\ + isinstance(v, Macro)) + if len(args) == 1: + raise UsageError( + "%macro insufficient args; usage '%macro name n1-n2 n3-4...") + name, codefrom = args[0], " ".join(args[1:]) - In [6]: ed _ - Editing... done. Executing edited code... - hello world - Out[6]: "print 'hello world'\\n" + #print 'rng',ranges # dbg + try: + lines = self.shell.find_user_code(codefrom, 'r' in opts) + except (ValueError, TypeError) as e: + print e.args[0] + return + macro = Macro(lines) + self.shell.define_macro(name, macro) + print 'Macro `%s` created. To execute, type its name (without quotes).' % name + print '=== Macro contents: ===' + print macro, - Now we call it with the output #8 (stored in _8, also as Out[8]):: - In [7]: ed _8 - Editing... done. Executing edited code... - hello again - Out[7]: "print 'hello again'\\n" +class AutoMagics(MagicFunctions): + """Magics that control various autoX behaviors.""" + def __init__(self, shell): + super(ProfileMagics, self).__init__(shell) + # namespace for holding state we may need + self._magic_state = Bunch() - Changing the default editor hook: + def magic_automagic(self, parameter_s = ''): + """Make magic functions callable without having to type the initial %. - If you wish to write your own editor hook, you can put it in a - configuration file which you load at startup time. The default hook - is defined in the IPython.core.hooks module, and you can use that as a - starting example for further modifications. That file also has - general instructions on how to set a new hook for use once you've - defined it.""" - opts,args = self.parse_options(parameter_s,'prxn:') + Without argumentsl toggles on/off (when off, you must call it as + %automagic, of course). With arguments it sets the value, and you can + use any of (case insensitive): - try: - filename, lineno, is_temp = self._find_edit_target(args, opts, last_call) - except MacroToEdit as e: - self._edit_macro(args, e.args[0]) - return + - on,1,True: to activate - # do actual editing here - print 'Editing...', - sys.stdout.flush() - try: - # Quote filenames that may have spaces in them - if ' ' in filename: - filename = "'%s'" % filename - self.shell.hooks.editor(filename,lineno) - except TryNext: - warn('Could not open editor') - return + - off,0,False: to deactivate. - # XXX TODO: should this be generalized for all string vars? - # For now, this is special-cased to blocks created by cpaste - if args.strip() == 'pasted_block': - self.shell.user_ns['pasted_block'] = file_read(filename) + Note that magic functions have lowest priority, so if there's a + variable whose name collides with that of a magic fn, automagic won't + work for that function (you get the variable instead). However, if you + delete the variable (del var), the previously shadowed magic function + becomes visible to automagic again.""" - if 'x' in opts: # -x prevents actual execution - print + arg = parameter_s.lower() + if parameter_s in ('on','1','true'): + self.shell.automagic = True + elif parameter_s in ('off','0','false'): + self.shell.automagic = False else: - print 'done. Executing edited code...' - if 'r' in opts: # Untranslated IPython code - self.shell.run_cell(file_read(filename), - store_history=False) - else: - self.shell.safe_execfile(filename, self.shell.user_ns, - self.shell.user_ns) - - if is_temp: - try: - return open(filename).read() - except IOError,msg: - if msg.filename == filename: - warn('File not found. Did you forget to save?') - return - else: - self.shell.showtraceback() - - def magic_xmode(self,parameter_s = ''): - """Switch modes for the exception handlers. - - Valid modes: Plain, Context and Verbose. + self.shell.automagic = not self.shell.automagic + print '\n' + Magic.auto_status[self.shell.automagic] - If called without arguments, acts as a toggle.""" + @skip_doctest + def magic_autocall(self, parameter_s = ''): + """Make functions callable without having to type parentheses. - def xmode_switch_err(name): - warn('Error changing %s exception modes.\n%s' % - (name,sys.exc_info()[1])) + Usage: - shell = self.shell - new_mode = parameter_s.strip().capitalize() - try: - shell.InteractiveTB.set_mode(mode=new_mode) - print 'Exception reporting mode:',shell.InteractiveTB.mode - except: - xmode_switch_err('user') + %autocall [mode] - def magic_colors(self,parameter_s = ''): - """Switch color scheme for prompts, info system and exception handlers. + The mode can be one of: 0->Off, 1->Smart, 2->Full. If not given, the + value is toggled on and off (remembering the previous state). - Currently implemented schemes: NoColor, Linux, LightBG. + In more detail, these values mean: - Color scheme names are not case-sensitive. + 0 -> fully disabled - Examples - -------- - To get a plain black and white terminal:: + 1 -> active, but do not apply if there are no arguments on the line. - %colors nocolor - """ + In this mode, you get:: - def color_switch_err(name): - warn('Error changing %s color schemes.\n%s' % - (name,sys.exc_info()[1])) + In [1]: callable + Out[1]: + In [2]: callable 'hello' + ------> callable('hello') + Out[2]: False - new_scheme = parameter_s.strip() - if not new_scheme: - raise UsageError( - "%colors: you must specify a color scheme. See '%colors?'") - return - # local shortcut - shell = self.shell + 2 -> Active always. Even if no arguments are present, the callable + object is called:: - import IPython.utils.rlineimpl as readline + In [2]: float + ------> float() + Out[2]: 0.0 - if not shell.colors_force and \ - not readline.have_readline and sys.platform == "win32": - msg = """\ -Proper color support under MS Windows requires the pyreadline library. -You can find it at: -http://ipython.org/pyreadline.html -Gary's readline needs the ctypes module, from: -http://starship.python.net/crew/theller/ctypes -(Note that ctypes is already part of Python versions 2.5 and newer). + Note that even with autocall off, you can still use '/' at the start of + a line to treat the first argument on the command line as a function + and add parentheses to it:: -Defaulting color scheme to 'NoColor'""" - new_scheme = 'NoColor' - warn(msg) + In [8]: /str 43 + ------> str(43) + Out[8]: '43' - # readline option is 0 - if not shell.colors_force and not shell.has_readline: - new_scheme = 'NoColor' + # all-random (note for auto-testing) + """ - # Set prompt colors - try: - shell.prompt_manager.color_scheme = new_scheme - except: - color_switch_err('prompt') + if parameter_s: + arg = int(parameter_s) else: - shell.colors = \ - shell.prompt_manager.color_scheme_table.active_scheme_name - # Set exception colors - try: - shell.InteractiveTB.set_colors(scheme = new_scheme) - shell.SyntaxTB.set_colors(scheme = new_scheme) - except: - color_switch_err('exception') + arg = 'toggle' - # Set info (for 'object?') colors - if shell.color_info: - try: - shell.inspector.set_active_scheme(new_scheme) - except: - color_switch_err('object inspector') - else: - shell.inspector.set_active_scheme('NoColor') + if not arg in (0,1,2,'toggle'): + error('Valid modes: (0->Off, 1->Smart, 2->Full') + return + + if arg in (0,1,2): + self.shell.autocall = arg + else: # toggle + if self.shell.autocall: + self._magic_state.autocall_save = self.shell.autocall + self.shell.autocall = 0 + else: + try: + self.shell.autocall = self._magic_state.autocall_save + except AttributeError: + self.shell.autocall = self._magic_state.autocall_save = 1 + + print "Automatic calling is:",['OFF','Smart','Full'][self.shell.autocall] - def magic_pprint(self, parameter_s=''): - """Toggle pretty printing on/off.""" - ptformatter = self.shell.display_formatter.formatters['text/plain'] - ptformatter.pprint = bool(1 - ptformatter.pprint) - print 'Pretty printing has been turned', \ - ['OFF','ON'][ptformatter.pprint] - #...................................................................... - # Functions to implement unix shell-type things +class OSMagics(MagicFunctions): + """Magics to interact with the underlying OS (shell-type functionality). + """ @skip_doctest def magic_alias(self, parameter_s = ''): @@ -3345,133 +3614,146 @@ def magic_pycat(self, parameter_s=''): page.page(self.shell.pycolorize(cont)) - def magic_quickref(self,arg): - """ Show a quick reference sheet """ - import IPython.core.usage - qr = IPython.core.usage.quick_reference + self.magic_magic('-brief') - page.page(qr) +class LoggingMagics(MagicFunctions): + """Magics related to all logging machinery.""" + def magic_logstart(self,parameter_s=''): + """Start logging anywhere in a session. - def magic_doctest_mode(self,parameter_s=''): - """Toggle doctest mode on and off. + %logstart [-o|-r|-t] [log_name [log_mode]] - This mode is intended to make IPython behave as much as possible like a - plain Python shell, from the perspective of how its prompts, exceptions - and output look. This makes it easy to copy and paste parts of a - session into doctests. It does so by: + If no name is given, it defaults to a file named 'ipython_log.py' in your + current directory, in 'rotate' mode (see below). - - Changing the prompts to the classic ``>>>`` ones. - - Changing the exception reporting mode to 'Plain'. - - Disabling pretty-printing of output. + '%logstart name' saves to file 'name' in 'backup' mode. It saves your + history up to that point and then continues logging. - Note that IPython also supports the pasting of code snippets that have - leading '>>>' and '...' prompts in them. This means that you can paste - doctests from files or docstrings (even if they have leading - whitespace), and the code will execute correctly. You can then use - '%history -t' to see the translated history; this will give you the - input after removal of all the leading prompts and whitespace, which - can be pasted back into an editor. + %logstart takes a second optional parameter: logging mode. This can be one + of (note that the modes are given unquoted):\\ + append: well, that says it.\\ + backup: rename (if exists) to name~ and start name.\\ + global: single logfile in your home dir, appended to.\\ + over : overwrite existing log.\\ + rotate: create rotating logs name.1~, name.2~, etc. - With these features, you can switch into this mode easily whenever you - need to do testing and changes to doctests, without having to leave - your existing IPython session. - """ + Options: - from IPython.utils.ipstruct import Struct + -o: log also IPython's output. In this mode, all commands which + generate an Out[NN] prompt are recorded to the logfile, right after + their corresponding input line. The output lines are always + prepended with a '#[Out]# ' marker, so that the log remains valid + Python code. - # Shorthands - shell = self.shell - pm = shell.prompt_manager - meta = shell.meta - disp_formatter = self.shell.display_formatter - ptformatter = disp_formatter.formatters['text/plain'] - # dstore is a data store kept in the instance metadata bag to track any - # changes we make, so we can undo them later. - dstore = meta.setdefault('doctest_mode',Struct()) - save_dstore = dstore.setdefault + Since this marker is always the same, filtering only the output from + a log is very easy, using for example a simple awk call:: - # save a few values we'll need to recover later - mode = save_dstore('mode',False) - save_dstore('rc_pprint',ptformatter.pprint) - save_dstore('xmode',shell.InteractiveTB.mode) - save_dstore('rc_separate_out',shell.separate_out) - save_dstore('rc_separate_out2',shell.separate_out2) - save_dstore('rc_prompts_pad_left',pm.justify) - save_dstore('rc_separate_in',shell.separate_in) - save_dstore('rc_plain_text_only',disp_formatter.plain_text_only) - save_dstore('prompt_templates',(pm.in_template, pm.in2_template, pm.out_template)) + awk -F'#\\[Out\\]# ' '{if($2) {print $2}}' ipython_log.py - if mode == False: - # turn on - pm.in_template = '>>> ' - pm.in2_template = '... ' - pm.out_template = '' + -r: log 'raw' input. Normally, IPython's logs contain the processed + input, so that user lines are logged in their final form, converted + into valid Python. For example, %Exit is logged as + _ip.magic("Exit"). If the -r flag is given, all input is logged + exactly as typed, with no transformations applied. - # Prompt separators like plain python - shell.separate_in = '' - shell.separate_out = '' - shell.separate_out2 = '' + -t: put timestamps before each input line logged (these are put in + comments).""" - pm.justify = False + opts,par = self.parse_options(parameter_s,'ort') + log_output = 'o' in opts + log_raw_input = 'r' in opts + timestamp = 't' in opts - ptformatter.pprint = False - disp_formatter.plain_text_only = True + logger = self.shell.logger - shell.magic('xmode Plain') + # if no args are given, the defaults set in the logger constructor by + # ipython remain valid + if par: + try: + logfname,logmode = par.split() + except: + logfname = par + logmode = 'backup' else: - # turn off - pm.in_template, pm.in2_template, pm.out_template = dstore.prompt_templates + logfname = logger.logfname + logmode = logger.logmode + # put logfname into rc struct as if it had been called on the command + # line, so it ends up saved in the log header Save it in case we need + # to restore it... + old_logfile = self.shell.logfile + if logfname: + logfname = os.path.expanduser(logfname) + self.shell.logfile = logfname - shell.separate_in = dstore.rc_separate_in + loghead = '# IPython log file\n\n' + try: + started = logger.logstart(logfname,loghead,logmode, + log_output,timestamp,log_raw_input) + except: + self.shell.logfile = old_logfile + warn("Couldn't start log: %s" % sys.exc_info()[1]) + else: + # log input history up to this point, optionally interleaving + # output if requested - shell.separate_out = dstore.rc_separate_out - shell.separate_out2 = dstore.rc_separate_out2 + if timestamp: + # disable timestamping for the previous history, since we've + # lost those already (no time machine here). + logger.timestamp = False - pm.justify = dstore.rc_prompts_pad_left + if log_raw_input: + input_hist = self.shell.history_manager.input_hist_raw + else: + input_hist = self.shell.history_manager.input_hist_parsed - ptformatter.pprint = dstore.rc_pprint - disp_formatter.plain_text_only = dstore.rc_plain_text_only + if log_output: + log_write = logger.log_write + output_hist = self.shell.history_manager.output_hist + for n in range(1,len(input_hist)-1): + log_write(input_hist[n].rstrip() + '\n') + if n in output_hist: + log_write(repr(output_hist[n]),'output') + else: + logger.log_write('\n'.join(input_hist[1:])) + logger.log_write('\n') + if timestamp: + # re-enable timestamping + logger.timestamp = True - shell.magic('xmode ' + dstore.xmode) + print ('Activating auto-logging. ' + 'Current session state plus future input saved.') + logger.logstate() - # Store new mode and inform - dstore.mode = bool(1-int(mode)) - mode_label = ['OFF','ON'][dstore.mode] - print 'Doctest mode is:', mode_label + def magic_logstop(self,parameter_s=''): + """Fully stop logging and close log file. - def magic_gui(self, parameter_s=''): - """Enable or disable IPython GUI event loop integration. + In order to start logging again, a new %logstart call needs to be made, + possibly (though not necessarily) with a new filename, mode and other + options.""" + self.logger.logstop() - %gui [GUINAME] + def magic_logoff(self,parameter_s=''): + """Temporarily stop logging. - This magic replaces IPython's threaded shells that were activated - using the (pylab/wthread/etc.) command line flags. GUI toolkits - can now be enabled at runtime and keyboard - interrupts should work without any problems. The following toolkits - are supported: wxPython, PyQt4, PyGTK, Tk and Cocoa (OSX):: + You must have previously started logging.""" + self.shell.logger.switch_log(0) + + def magic_logon(self,parameter_s=''): + """Restart logging. + + This function is for restarting logging which you've temporarily + stopped with %logoff. For starting logging for the first time, you + must use the %logstart function, which allows you to specify an + optional log filename.""" + + self.shell.logger.switch_log(1) - %gui wx # enable wxPython event loop integration - %gui qt4|qt # enable PyQt4 event loop integration - %gui gtk # enable PyGTK event loop integration - %gui gtk3 # enable Gtk3 event loop integration - %gui tk # enable Tk event loop integration - %gui OSX # enable Cocoa event loop integration - # (requires %matplotlib 1.1) - %gui # disable all event loop integration + def magic_logstate(self,parameter_s=''): + """Print the status of the logging system.""" - WARNING: after any of these has been called you can simply create - an application object, but DO NOT start the event loop yourself, as - we have already handled that. - """ - opts, arg = self.parse_options(parameter_s, '') - if arg=='': arg = None - try: - return self.enable_gui(arg) - except Exception as e: - # print simple error message, rather than traceback if we can't - # hook up the GUI - error(str(e)) - + self.shell.logger.logstate() + +class ExtensionsMagics(MagicFunctions): + """Magics to manage the IPython extensions system.""" def magic_install_ext(self, parameter_s): """Download and install an extension from a URL, e.g.:: @@ -3510,6 +3792,9 @@ def magic_reload_ext(self, module_str): """Reload an IPython extension by its module name.""" self.shell.extension_manager.reload_extension(module_str) + +class DeprecatedMagics(MagicFunctions): + """Magics slated for later removal.""" def magic_install_profiles(self, s): """%install_profiles has been deprecated.""" print '\n'.join([ @@ -3529,6 +3814,10 @@ def magic_install_default_config(self, s): "Add `--reset` to overwrite already existing config files with defaults." ]) + +class PylabMagics(MagicFunctions): + """Magics related to matplotlib's pylab support""" + @skip_doctest def magic_pylab(self, s): """Load numpy and matplotlib to work interactively. @@ -3589,234 +3878,3 @@ def magic_pylab(self, s): import_all_status = True self.shell.enable_pylab(s, import_all=import_all_status) - - def magic_tb(self, s): - """Print the last traceback with the currently active exception mode. - - See %xmode for changing exception reporting modes.""" - self.shell.showtraceback() - - @skip_doctest - def magic_precision(self, s=''): - """Set floating point precision for pretty printing. - - Can set either integer precision or a format string. - - If numpy has been imported and precision is an int, - numpy display precision will also be set, via ``numpy.set_printoptions``. - - If no argument is given, defaults will be restored. - - Examples - -------- - :: - - In [1]: from math import pi - - In [2]: %precision 3 - Out[2]: u'%.3f' - - In [3]: pi - Out[3]: 3.142 - - In [4]: %precision %i - Out[4]: u'%i' - - In [5]: pi - Out[5]: 3 - - In [6]: %precision %e - Out[6]: u'%e' - - In [7]: pi**10 - Out[7]: 9.364805e+04 - - In [8]: %precision - Out[8]: u'%r' - - In [9]: pi**10 - Out[9]: 93648.047476082982 - - """ - - ptformatter = self.shell.display_formatter.formatters['text/plain'] - ptformatter.float_precision = s - return ptformatter.float_format - - - @magic_arguments.magic_arguments() - @magic_arguments.argument( - '-e', '--export', action='store_true', default=False, - help='Export IPython history as a notebook. The filename argument ' - 'is used to specify the notebook name and format. For example ' - 'a filename of notebook.ipynb will result in a notebook name ' - 'of "notebook" and a format of "xml". Likewise using a ".json" ' - 'or ".py" file extension will write the notebook in the json ' - 'or py formats.' - ) - @magic_arguments.argument( - '-f', '--format', - help='Convert an existing IPython notebook to a new format. This option ' - 'specifies the new format and can have the values: xml, json, py. ' - 'The target filename is chosen automatically based on the new ' - 'format. The filename argument gives the name of the source file.' - ) - @magic_arguments.argument( - 'filename', type=unicode, - help='Notebook name or filename' - ) - def magic_notebook(self, s): - """Export and convert IPython notebooks. - - This function can export the current IPython history to a notebook file - or can convert an existing notebook file into a different format. For - example, to export the history to "foo.ipynb" do "%notebook -e foo.ipynb". - To export the history to "foo.py" do "%notebook -e foo.py". To convert - "foo.ipynb" to "foo.json" do "%notebook -f json foo.ipynb". Possible - formats include (json/ipynb, py). - """ - args = magic_arguments.parse_argstring(self.magic_notebook, s) - - from IPython.nbformat import current - args.filename = unquote_filename(args.filename) - if args.export: - fname, name, format = current.parse_filename(args.filename) - cells = [] - hist = list(self.shell.history_manager.get_range()) - for session, prompt_number, input in hist[:-1]: - cells.append(current.new_code_cell(prompt_number=prompt_number, - input=input)) - worksheet = current.new_worksheet(cells=cells) - nb = current.new_notebook(name=name,worksheets=[worksheet]) - with io.open(fname, 'w', encoding='utf-8') as f: - current.write(nb, f, format); - elif args.format is not None: - old_fname, old_name, old_format = current.parse_filename(args.filename) - new_format = args.format - if new_format == u'xml': - raise ValueError('Notebooks cannot be written as xml.') - elif new_format == u'ipynb' or new_format == u'json': - new_fname = old_name + u'.ipynb' - new_format = u'json' - elif new_format == u'py': - new_fname = old_name + u'.py' - else: - raise ValueError('Invalid notebook format: %s' % new_format) - with io.open(old_fname, 'r', encoding='utf-8') as f: - nb = current.read(f, old_format) - with io.open(new_fname, 'w', encoding='utf-8') as f: - current.write(nb, f, new_format) - - def magic_config(self, s): - """configure IPython - - %config Class[.trait=value] - - This magic exposes most of the IPython config system. Any - Configurable class should be able to be configured with the simple - line:: - - %config Class.trait=value - - Where `value` will be resolved in the user's namespace, if it is an - expression or variable name. - - Examples - -------- - - To see what classes are available for config, pass no arguments:: - - In [1]: %config - Available objects for config: - TerminalInteractiveShell - HistoryManager - PrefilterManager - AliasManager - IPCompleter - PromptManager - DisplayFormatter - - To view what is configurable on a given class, just pass the class - name:: - - In [2]: %config IPCompleter - IPCompleter options - ----------------- - IPCompleter.omit__names= - Current: 2 - Choices: (0, 1, 2) - Instruct the completer to omit private method names - Specifically, when completing on ``object.``. - When 2 [default]: all names that start with '_' will be excluded. - When 1: all 'magic' names (``__foo__``) will be excluded. - When 0: nothing will be excluded. - IPCompleter.merge_completions= - Current: True - Whether to merge completion results into a single list - If False, only the completion results from the first non-empty completer - will be returned. - IPCompleter.limit_to__all__= - Current: False - Instruct the completer to use __all__ for the completion - Specifically, when completing on ``object.``. - When True: only those names in obj.__all__ will be included. - When False [default]: the __all__ attribute is ignored - IPCompleter.greedy= - Current: False - Activate greedy completion - This will enable completion on elements of lists, results of function calls, - etc., but can be unsafe because the code is actually evaluated on TAB. - - but the real use is in setting values:: - - In [3]: %config IPCompleter.greedy = True - - and these values are read from the user_ns if they are variables:: - - In [4]: feeling_greedy=False - - In [5]: %config IPCompleter.greedy = feeling_greedy - - """ - from IPython.config.loader import Config - # some IPython objects are Configurable, but do not yet have - # any configurable traits. Exclude them from the effects of - # this magic, as their presence is just noise: - configurables = [ c for c in self.shell.configurables - if c.__class__.class_traits(config=True) ] - classnames = [ c.__class__.__name__ for c in configurables ] - - line = s.strip() - if not line: - # print available configurable names - print "Available objects for config:" - for name in classnames: - print " ", name - return - elif line in classnames: - # `%config TerminalInteractiveShell` will print trait info for - # TerminalInteractiveShell - c = configurables[classnames.index(line)] - cls = c.__class__ - help = cls.class_get_help(c) - # strip leading '--' from cl-args: - help = re.sub(re.compile(r'^--', re.MULTILINE), '', help) - print help - return - elif '=' not in line: - raise UsageError("Invalid config statement: %r, should be Class.trait = value" % line) - - - # otherwise, assume we are setting configurables. - # leave quotes on args when splitting, because we want - # unquoted args to eval in user_ns - cfg = Config() - exec "cfg."+line in locals(), self.shell.user_ns - - for configurable in configurables: - try: - configurable.update_config(cfg) - except Exception as e: - error(e) - -# end Magic From b0d78b68763887d0261e137985aebc9b221aaefb Mon Sep 17 00:00:00 2001 From: Fernando Perez Date: Sun, 13 May 2012 13:51:39 -0700 Subject: [PATCH 013/103] Create class for user-defined magics. --- IPython/core/magic.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/IPython/core/magic.py b/IPython/core/magic.py index 4bb0f5bd733..d71aeb25364 100644 --- a/IPython/core/magic.py +++ b/IPython/core/magic.py @@ -282,6 +282,15 @@ def default_option(self,fn,optstr): self.options_table[fn] = optstr +class UserMagics(MagicFunctions): + """Placeholder for user-defined magics to be added at runtime. + + All magics are eventually merged into a single namespace at runtime, but we + use this class to isolate the magics defined dynamically by the user into + their own class. + """ + + class BasicMagics(MagicFunctions): """Magics that provide central IPython functionality. From da012c2ecc36090ad7ed0e6afda4fa5a87f190ee Mon Sep 17 00:00:00 2001 From: Fernando Perez Date: Sun, 13 May 2012 15:49:07 -0700 Subject: [PATCH 014/103] Separate magic code into base file and implementation of magics. Ran a first solid pass with Pyflakes to clean the code. --- IPython/core/magic.py | 3753 +------------------------------ IPython/core/magic_functions.py | 3639 ++++++++++++++++++++++++++++++ 2 files changed, 3734 insertions(+), 3658 deletions(-) create mode 100644 IPython/core/magic_functions.py diff --git a/IPython/core/magic.py b/IPython/core/magic.py index d71aeb25364..62943a3d369 100644 --- a/IPython/core/magic.py +++ b/IPython/core/magic.py @@ -14,62 +14,28 @@ #----------------------------------------------------------------------------- # Imports #----------------------------------------------------------------------------- - -import __builtin__ as builtin_mod -import __future__ -import bdb -import gc -import imp -import inspect -import io -import json +# Stdlib import os import re -import shutil import sys -import time -from StringIO import StringIO -from getopt import getopt,GetoptError -from pprint import pformat -from urllib2 import urlopen - -# cProfile was added in Python2.5 -try: - import cProfile as profile - import pstats -except ImportError: - # profile isn't bundled by default in Debian for license reasons - try: - import profile,pstats - except ImportError: - profile = pstats = None - -import IPython -from IPython.config.application import Application +from getopt import getopt, GetoptError + +# Our own from IPython.config.configurable import Configurable -from IPython.core import debugger, oinspect -from IPython.core import magic_arguments, page -from IPython.core.error import StdinNotImplementedError -from IPython.core.error import TryNext +from IPython.core import oinspect from IPython.core.error import UsageError -from IPython.core.fakemodule import FakeModule -from IPython.core.macro import Macro from IPython.core.prefilter import ESC_MAGIC -from IPython.core.profiledir import ProfileDir -from IPython.testing.skipdoctest import skip_doctest -from IPython.utils import openpy -from IPython.utils import py3compat -from IPython.utils.encoding import DEFAULT_ENCODING -from IPython.utils.io import file_read, nlprint +from IPython.external.decorator import decorator from IPython.utils.ipstruct import Struct -from IPython.utils.module_paths import find_mod -from IPython.utils.path import get_py_filename, unquote_filename -from IPython.utils.process import arg_split, abbrev_cwd -from IPython.utils.terminal import set_term_title -from IPython.utils.text import format_screen -from IPython.utils.timing import clock, clock2 -from IPython.utils.traitlets import Bool, Dict, Instance, Integer, List, Unicode -from IPython.utils.warn import warn, error +from IPython.utils.process import arg_split +from IPython.utils.traitlets import Dict, Enum, Instance +from IPython.utils.warn import error + +#----------------------------------------------------------------------------- +# Globals +#----------------------------------------------------------------------------- +line_magics = {} +cell_magics = {} #----------------------------------------------------------------------------- # Utility classes and functions @@ -106,7 +72,72 @@ def needs_local_scope(func): func.needs_local_scope = True return func -#*************************************************************************** +#----------------------------------------------------------------------------- +# Class and method decorators for registering magics +#----------------------------------------------------------------------------- + +def register_magics(cls): + global line_magics, cell_magics + + cls.line_magics = line_magics + cls.cell_magics = cell_magics + cls.registered = True + line_magics = {} + cell_magics = {} + return cls + + +def _magic_marker(magic_type): + global line_magics, cell_magics + + if magic_type not in ('line', 'cell'): + raise ValueError('magic_type must be one of ["line", "cell"], %s given' + % magic_type) + if magic_type == 'line': + line_magics = {} + else: + cell_magics = {} + + # This is a closure to capture the magic_type. We could also use a class, + # but it's overkill for just that one bit of state. + def magic_deco(arg): + global line_magics, cell_magics + call = lambda f, *a, **k: f(*a, **k) + + if callable(arg): + # "Naked" decorator call (just @foo, no args) + func = arg + name = func.func_name + func.magic_name = name + retval = decorator(call, func) + elif isinstance(arg, basestring): + # Decorator called with arguments (@foo('bar')) + name = arg + def mark(func, *a, **kw): + func.magic_name = name + return decorator(call, func) + retval = mark + else: + raise ValueError("Decorator can only be called with " + "string or function") + # Record the magic function in the global table that will then be + # appended to the class via the register_magics class decorator + if magic_type == 'line': + line_magics[name] = retval + else: + cell_magics[name] = retval + + return retval + + return magic_deco + + +line_magic = _magic_marker('line') +cell_magic = _magic_marker('cell') + +#----------------------------------------------------------------------------- +# Core Magic classes +#----------------------------------------------------------------------------- class MagicManager(Configurable): """Object that handles all magic-related functionality for IPython. @@ -149,14 +180,27 @@ def lsmagic(self): out.sort() return out +# Key base class that provides the central functionality for magics. -class MagicFunctions(object): +class Magics(object): """Base class for implementing magic functions. Shell functions which can be reached as %function_name. All magic functions should accept a string, which they can parse for their own needs. This can make some functions easier to type, eg `%cd ../` vs. `%cd("../")` + + Classes providing magic functions need to subclass this class, and they + MUST: + + - Use the method decorators `@line_magic` and `@cell_magic` to decorate + individual methods as magic functions, AND + + - Use the class decorator `@register_magics` to ensure that the magic + methods are properly registered at the instance level upon instance + initialization. + + See :mod:`magic_functions` for examples of actual implementation classes. """ options_table = Dict(config=True, @@ -280,3610 +324,3 @@ def default_option(self,fn,optstr): if fn not in self.lsmagic(): error("%s is not a magic function" % fn) self.options_table[fn] = optstr - - -class UserMagics(MagicFunctions): - """Placeholder for user-defined magics to be added at runtime. - - All magics are eventually merged into a single namespace at runtime, but we - use this class to isolate the magics defined dynamically by the user into - their own class. - """ - - -class BasicMagics(MagicFunctions): - """Magics that provide central IPython functionality. - - These are various magics that don't fit into specific categories but that - are all part of the base 'IPython experience'.""" - - def magic_lsmagic(self, parameter_s = ''): - """List currently available magic functions.""" - mesc = ESC_MAGIC - print 'Available magic functions:\n'+mesc+\ - (' '+mesc).join(self.lsmagic()) - print '\n' + Magic.auto_status[self.shell.automagic] - return None - - def magic_magic(self, parameter_s = ''): - """Print information about the magic function system. - - Supported formats: -latex, -brief, -rest - """ - - mode = '' - try: - if parameter_s.split()[0] == '-latex': - mode = 'latex' - if parameter_s.split()[0] == '-brief': - mode = 'brief' - if parameter_s.split()[0] == '-rest': - mode = 'rest' - rest_docs = [] - except: - pass - - magic_docs = [] - for fname in self.lsmagic(): - mname = 'magic_' + fname - for space in (Magic, self, self.__class__): - try: - fn = space.__dict__[mname] - except KeyError: - pass - else: - break - if mode == 'brief': - # only first line - if fn.__doc__: - fndoc = fn.__doc__.split('\n',1)[0] - else: - fndoc = 'No documentation' - else: - if fn.__doc__: - fndoc = fn.__doc__.rstrip() - else: - fndoc = 'No documentation' - - - if mode == 'rest': - rest_docs.append('**%s%s**::\n\n\t%s\n\n' %(ESC_MAGIC, - fname,fndoc)) - - else: - magic_docs.append('%s%s:\n\t%s\n' %(ESC_MAGIC, - fname,fndoc)) - - magic_docs = ''.join(magic_docs) - - if mode == 'rest': - return "".join(rest_docs) - - if mode == 'latex': - print self.format_latex(magic_docs) - return - else: - magic_docs = format_screen(magic_docs) - if mode == 'brief': - return magic_docs - - outmsg = """ -IPython's 'magic' functions -=========================== - -The magic function system provides a series of functions which allow you to -control the behavior of IPython itself, plus a lot of system-type -features. All these functions are prefixed with a % character, but parameters -are given without parentheses or quotes. - -NOTE: If you have 'automagic' enabled (via the command line option or with the -%automagic function), you don't need to type in the % explicitly. By default, -IPython ships with automagic on, so you should only rarely need the % escape. - -Example: typing '%cd mydir' (without the quotes) changes you working directory -to 'mydir', if it exists. - -For a list of the available magic functions, use %lsmagic. For a description -of any of them, type %magic_name?, e.g. '%cd?'. - -Currently the magic system has the following functions:\n""" - - mesc = ESC_MAGIC - outmsg = ("%s\n%s\n\nSummary of magic functions (from %slsmagic):" - "\n\n%s%s\n\n%s" % (outmsg, - magic_docs,mesc,mesc, - (' '+mesc).join(self.lsmagic()), - Magic.auto_status[self.shell.automagic] ) ) - page.page(outmsg) - - - def magic_page(self, parameter_s=''): - """Pretty print the object and display it through a pager. - - %page [options] OBJECT - - If no object is given, use _ (last output). - - Options: - - -r: page str(object), don't pretty-print it.""" - - # After a function contributed by Olivier Aubert, slightly modified. - - # Process options/args - opts,args = self.parse_options(parameter_s,'r') - raw = 'r' in opts - - oname = args and args or '_' - info = self._ofind(oname) - if info['found']: - txt = (raw and str or pformat)( info['obj'] ) - page.page(txt) - else: - print 'Object `%s` not found' % oname - - def magic_profile(self, parameter_s=''): - """Print your currently active IPython profile.""" - from IPython.core.application import BaseIPythonApplication - if BaseIPythonApplication.initialized(): - print BaseIPythonApplication.instance().profile - else: - error("profile is an application-level value, but you don't appear to be in an IPython application") - - def magic_pprint(self, parameter_s=''): - """Toggle pretty printing on/off.""" - ptformatter = self.shell.display_formatter.formatters['text/plain'] - ptformatter.pprint = bool(1 - ptformatter.pprint) - print 'Pretty printing has been turned', \ - ['OFF','ON'][ptformatter.pprint] - - def magic_colors(self,parameter_s = ''): - """Switch color scheme for prompts, info system and exception handlers. - - Currently implemented schemes: NoColor, Linux, LightBG. - - Color scheme names are not case-sensitive. - - Examples - -------- - To get a plain black and white terminal:: - - %colors nocolor - """ - - def color_switch_err(name): - warn('Error changing %s color schemes.\n%s' % - (name,sys.exc_info()[1])) - - - new_scheme = parameter_s.strip() - if not new_scheme: - raise UsageError( - "%colors: you must specify a color scheme. See '%colors?'") - return - # local shortcut - shell = self.shell - - import IPython.utils.rlineimpl as readline - - if not shell.colors_force and \ - not readline.have_readline and sys.platform == "win32": - msg = """\ -Proper color support under MS Windows requires the pyreadline library. -You can find it at: -http://ipython.org/pyreadline.html -Gary's readline needs the ctypes module, from: -http://starship.python.net/crew/theller/ctypes -(Note that ctypes is already part of Python versions 2.5 and newer). - -Defaulting color scheme to 'NoColor'""" - new_scheme = 'NoColor' - warn(msg) - - # readline option is 0 - if not shell.colors_force and not shell.has_readline: - new_scheme = 'NoColor' - - # Set prompt colors - try: - shell.prompt_manager.color_scheme = new_scheme - except: - color_switch_err('prompt') - else: - shell.colors = \ - shell.prompt_manager.color_scheme_table.active_scheme_name - # Set exception colors - try: - shell.InteractiveTB.set_colors(scheme = new_scheme) - shell.SyntaxTB.set_colors(scheme = new_scheme) - except: - color_switch_err('exception') - - # Set info (for 'object?') colors - if shell.color_info: - try: - shell.inspector.set_active_scheme(new_scheme) - except: - color_switch_err('object inspector') - else: - shell.inspector.set_active_scheme('NoColor') - - def magic_xmode(self,parameter_s = ''): - """Switch modes for the exception handlers. - - Valid modes: Plain, Context and Verbose. - - If called without arguments, acts as a toggle.""" - - def xmode_switch_err(name): - warn('Error changing %s exception modes.\n%s' % - (name,sys.exc_info()[1])) - - shell = self.shell - new_mode = parameter_s.strip().capitalize() - try: - shell.InteractiveTB.set_mode(mode=new_mode) - print 'Exception reporting mode:',shell.InteractiveTB.mode - except: - xmode_switch_err('user') - - def magic_quickref(self,arg): - """ Show a quick reference sheet """ - import IPython.core.usage - qr = IPython.core.usage.quick_reference + self.magic_magic('-brief') - page.page(qr) - - def magic_doctest_mode(self,parameter_s=''): - """Toggle doctest mode on and off. - - This mode is intended to make IPython behave as much as possible like a - plain Python shell, from the perspective of how its prompts, exceptions - and output look. This makes it easy to copy and paste parts of a - session into doctests. It does so by: - - - Changing the prompts to the classic ``>>>`` ones. - - Changing the exception reporting mode to 'Plain'. - - Disabling pretty-printing of output. - - Note that IPython also supports the pasting of code snippets that have - leading '>>>' and '...' prompts in them. This means that you can paste - doctests from files or docstrings (even if they have leading - whitespace), and the code will execute correctly. You can then use - '%history -t' to see the translated history; this will give you the - input after removal of all the leading prompts and whitespace, which - can be pasted back into an editor. - - With these features, you can switch into this mode easily whenever you - need to do testing and changes to doctests, without having to leave - your existing IPython session. - """ - - from IPython.utils.ipstruct import Struct - - # Shorthands - shell = self.shell - pm = shell.prompt_manager - meta = shell.meta - disp_formatter = self.shell.display_formatter - ptformatter = disp_formatter.formatters['text/plain'] - # dstore is a data store kept in the instance metadata bag to track any - # changes we make, so we can undo them later. - dstore = meta.setdefault('doctest_mode',Struct()) - save_dstore = dstore.setdefault - - # save a few values we'll need to recover later - mode = save_dstore('mode',False) - save_dstore('rc_pprint',ptformatter.pprint) - save_dstore('xmode',shell.InteractiveTB.mode) - save_dstore('rc_separate_out',shell.separate_out) - save_dstore('rc_separate_out2',shell.separate_out2) - save_dstore('rc_prompts_pad_left',pm.justify) - save_dstore('rc_separate_in',shell.separate_in) - save_dstore('rc_plain_text_only',disp_formatter.plain_text_only) - save_dstore('prompt_templates',(pm.in_template, pm.in2_template, pm.out_template)) - - if mode == False: - # turn on - pm.in_template = '>>> ' - pm.in2_template = '... ' - pm.out_template = '' - - # Prompt separators like plain python - shell.separate_in = '' - shell.separate_out = '' - shell.separate_out2 = '' - - pm.justify = False - - ptformatter.pprint = False - disp_formatter.plain_text_only = True - - shell.magic('xmode Plain') - else: - # turn off - pm.in_template, pm.in2_template, pm.out_template = dstore.prompt_templates - - shell.separate_in = dstore.rc_separate_in - - shell.separate_out = dstore.rc_separate_out - shell.separate_out2 = dstore.rc_separate_out2 - - pm.justify = dstore.rc_prompts_pad_left - - ptformatter.pprint = dstore.rc_pprint - disp_formatter.plain_text_only = dstore.rc_plain_text_only - - shell.magic('xmode ' + dstore.xmode) - - # Store new mode and inform - dstore.mode = bool(1-int(mode)) - mode_label = ['OFF','ON'][dstore.mode] - print 'Doctest mode is:', mode_label - - def magic_gui(self, parameter_s=''): - """Enable or disable IPython GUI event loop integration. - - %gui [GUINAME] - - This magic replaces IPython's threaded shells that were activated - using the (pylab/wthread/etc.) command line flags. GUI toolkits - can now be enabled at runtime and keyboard - interrupts should work without any problems. The following toolkits - are supported: wxPython, PyQt4, PyGTK, Tk and Cocoa (OSX):: - - %gui wx # enable wxPython event loop integration - %gui qt4|qt # enable PyQt4 event loop integration - %gui gtk # enable PyGTK event loop integration - %gui gtk3 # enable Gtk3 event loop integration - %gui tk # enable Tk event loop integration - %gui OSX # enable Cocoa event loop integration - # (requires %matplotlib 1.1) - %gui # disable all event loop integration - - WARNING: after any of these has been called you can simply create - an application object, but DO NOT start the event loop yourself, as - we have already handled that. - """ - opts, arg = self.parse_options(parameter_s, '') - if arg=='': arg = None - try: - return self.enable_gui(arg) - except Exception as e: - # print simple error message, rather than traceback if we can't - # hook up the GUI - error(str(e)) - - @skip_doctest - def magic_precision(self, s=''): - """Set floating point precision for pretty printing. - - Can set either integer precision or a format string. - - If numpy has been imported and precision is an int, - numpy display precision will also be set, via ``numpy.set_printoptions``. - - If no argument is given, defaults will be restored. - - Examples - -------- - :: - - In [1]: from math import pi - - In [2]: %precision 3 - Out[2]: u'%.3f' - - In [3]: pi - Out[3]: 3.142 - - In [4]: %precision %i - Out[4]: u'%i' - - In [5]: pi - Out[5]: 3 - - In [6]: %precision %e - Out[6]: u'%e' - - In [7]: pi**10 - Out[7]: 9.364805e+04 - - In [8]: %precision - Out[8]: u'%r' - - In [9]: pi**10 - Out[9]: 93648.047476082982 - """ - ptformatter = self.shell.display_formatter.formatters['text/plain'] - ptformatter.float_precision = s - return ptformatter.float_format - - @magic_arguments.magic_arguments() - @magic_arguments.argument( - '-e', '--export', action='store_true', default=False, - help='Export IPython history as a notebook. The filename argument ' - 'is used to specify the notebook name and format. For example ' - 'a filename of notebook.ipynb will result in a notebook name ' - 'of "notebook" and a format of "xml". Likewise using a ".json" ' - 'or ".py" file extension will write the notebook in the json ' - 'or py formats.' - ) - @magic_arguments.argument( - '-f', '--format', - help='Convert an existing IPython notebook to a new format. This option ' - 'specifies the new format and can have the values: xml, json, py. ' - 'The target filename is chosen automatically based on the new ' - 'format. The filename argument gives the name of the source file.' - ) - @magic_arguments.argument( - 'filename', type=unicode, - help='Notebook name or filename' - ) - def magic_notebook(self, s): - """Export and convert IPython notebooks. - - This function can export the current IPython history to a notebook file - or can convert an existing notebook file into a different format. For - example, to export the history to "foo.ipynb" do "%notebook -e foo.ipynb". - To export the history to "foo.py" do "%notebook -e foo.py". To convert - "foo.ipynb" to "foo.json" do "%notebook -f json foo.ipynb". Possible - formats include (json/ipynb, py). - """ - args = magic_arguments.parse_argstring(self.magic_notebook, s) - - from IPython.nbformat import current - args.filename = unquote_filename(args.filename) - if args.export: - fname, name, format = current.parse_filename(args.filename) - cells = [] - hist = list(self.shell.history_manager.get_range()) - for session, prompt_number, input in hist[:-1]: - cells.append(current.new_code_cell(prompt_number=prompt_number, - input=input)) - worksheet = current.new_worksheet(cells=cells) - nb = current.new_notebook(name=name,worksheets=[worksheet]) - with io.open(fname, 'w', encoding='utf-8') as f: - current.write(nb, f, format); - elif args.format is not None: - old_fname, old_name, old_format = current.parse_filename(args.filename) - new_format = args.format - if new_format == u'xml': - raise ValueError('Notebooks cannot be written as xml.') - elif new_format == u'ipynb' or new_format == u'json': - new_fname = old_name + u'.ipynb' - new_format = u'json' - elif new_format == u'py': - new_fname = old_name + u'.py' - else: - raise ValueError('Invalid notebook format: %s' % new_format) - with io.open(old_fname, 'r', encoding='utf-8') as f: - nb = current.read(f, old_format) - with io.open(new_fname, 'w', encoding='utf-8') as f: - current.write(nb, f, new_format) - - -class CodeMagics(MagicFunctions): - """Magics related to code management (loading, saving, editing, ...).""" - - def magic_save(self,parameter_s = ''): - """Save a set of lines or a macro to a given filename. - - Usage:\\ - %save [options] filename n1-n2 n3-n4 ... n5 .. n6 ... - - Options: - - -r: use 'raw' input. By default, the 'processed' history is used, - so that magics are loaded in their transformed version to valid - Python. If this option is given, the raw input as typed as the - command line is used instead. - - This function uses the same syntax as %history for input ranges, - then saves the lines to the filename you specify. - - It adds a '.py' extension to the file if you don't do so yourself, and - it asks for confirmation before overwriting existing files.""" - - opts,args = self.parse_options(parameter_s,'r',mode='list') - fname, codefrom = unquote_filename(args[0]), " ".join(args[1:]) - if not fname.endswith('.py'): - fname += '.py' - if os.path.isfile(fname): - ans = raw_input('File `%s` exists. Overwrite (y/[N])? ' % fname) - if ans.lower() not in ['y','yes']: - print 'Operation cancelled.' - return - try: - cmds = self.shell.find_user_code(codefrom, 'r' in opts) - except (TypeError, ValueError) as e: - print e.args[0] - return - with io.open(fname,'w', encoding="utf-8") as f: - f.write(u"# coding: utf-8\n") - f.write(py3compat.cast_unicode(cmds)) - print 'The following commands were written to file `%s`:' % fname - print cmds - - def magic_pastebin(self, parameter_s = ''): - """Upload code to Github's Gist paste bin, returning the URL. - - Usage:\\ - %pastebin [-d "Custom description"] 1-7 - - The argument can be an input history range, a filename, or the name of a - string or macro. - - Options: - - -d: Pass a custom description for the gist. The default will say - "Pasted from IPython". - """ - opts, args = self.parse_options(parameter_s, 'd:') - - try: - code = self.shell.find_user_code(args) - except (ValueError, TypeError) as e: - print e.args[0] - return - - post_data = json.dumps({ - "description": opts.get('d', "Pasted from IPython"), - "public": True, - "files": { - "file1.py": { - "content": code - } - } - }).encode('utf-8') - - response = urlopen("https://api.github.com/gists", post_data) - response_data = json.loads(response.read().decode('utf-8')) - return response_data['html_url'] - - def magic_loadpy(self, arg_s): - """Alias of `%load` - - `%loadpy` has gained some flexibility and droped the requirement of a `.py` - extension. So it has been renamed simply into %load. You can look at - `%load`'s docstring for more info. - """ - self.magic_load(arg_s) - - def magic_load(self, arg_s): - """Load code into the current frontend. - - Usage:\\ - %load [options] source - - where source can be a filename, URL, input history range or macro - - Options: - -------- - -y : Don't ask confirmation for loading source above 200 000 characters. - - This magic command can either take a local filename, a URL, an history - range (see %history) or a macro as argument, it will prompt for - confirmation before loading source with more than 200 000 characters, unless - -y flag is passed or if the frontend does not support raw_input:: - - %load myscript.py - %load 7-27 - %load myMacro - %load http://www.example.com/myscript.py - """ - opts,args = self.parse_options(arg_s,'y') - - contents = self.shell.find_user_code(args) - l = len(contents) - - # 200 000 is ~ 2500 full 80 caracter lines - # so in average, more than 5000 lines - if l > 200000 and 'y' not in opts: - try: - ans = self.shell.ask_yes_no(("The text you're trying to load seems pretty big"\ - " (%d characters). Continue (y/[N]) ?" % l), default='n' ) - except StdinNotImplementedError: - #asume yes if raw input not implemented - ans = True - - if ans is False : - print 'Operation cancelled.' - return - - self.set_next_input(contents) - - def _find_edit_target(self, args, opts, last_call): - """Utility method used by magic_edit to find what to edit.""" - - def make_filename(arg): - "Make a filename from the given args" - arg = unquote_filename(arg) - try: - filename = get_py_filename(arg) - except IOError: - # If it ends with .py but doesn't already exist, assume we want - # a new file. - if arg.endswith('.py'): - filename = arg - else: - filename = None - return filename - - # Set a few locals from the options for convenience: - opts_prev = 'p' in opts - opts_raw = 'r' in opts - - # custom exceptions - class DataIsObject(Exception): pass - - # Default line number value - lineno = opts.get('n',None) - - if opts_prev: - args = '_%s' % last_call[0] - if not self.shell.user_ns.has_key(args): - args = last_call[1] - - # use last_call to remember the state of the previous call, but don't - # let it be clobbered by successive '-p' calls. - try: - last_call[0] = self.shell.displayhook.prompt_count - if not opts_prev: - last_call[1] = args - except: - pass - - # by default this is done with temp files, except when the given - # arg is a filename - use_temp = True - - data = '' - - # First, see if the arguments should be a filename. - filename = make_filename(args) - if filename: - use_temp = False - elif args: - # Mode where user specifies ranges of lines, like in %macro. - data = self.shell.extract_input_lines(args, opts_raw) - if not data: - try: - # Load the parameter given as a variable. If not a string, - # process it as an object instead (below) - - #print '*** args',args,'type',type(args) # dbg - data = eval(args, self.shell.user_ns) - if not isinstance(data, basestring): - raise DataIsObject - - except (NameError,SyntaxError): - # given argument is not a variable, try as a filename - filename = make_filename(args) - if filename is None: - warn("Argument given (%s) can't be found as a variable " - "or as a filename." % args) - return - use_temp = False - - except DataIsObject: - # macros have a special edit function - if isinstance(data, Macro): - raise MacroToEdit(data) - - # For objects, try to edit the file where they are defined - try: - filename = inspect.getabsfile(data) - if 'fakemodule' in filename.lower() and inspect.isclass(data): - # class created by %edit? Try to find source - # by looking for method definitions instead, the - # __module__ in those classes is FakeModule. - attrs = [getattr(data, aname) for aname in dir(data)] - for attr in attrs: - if not inspect.ismethod(attr): - continue - filename = inspect.getabsfile(attr) - if filename and 'fakemodule' not in filename.lower(): - # change the attribute to be the edit target instead - data = attr - break - - datafile = 1 - except TypeError: - filename = make_filename(args) - datafile = 1 - warn('Could not find file where `%s` is defined.\n' - 'Opening a file named `%s`' % (args,filename)) - # Now, make sure we can actually read the source (if it was in - # a temp file it's gone by now). - if datafile: - try: - if lineno is None: - lineno = inspect.getsourcelines(data)[1] - except IOError: - filename = make_filename(args) - if filename is None: - warn('The file `%s` where `%s` was defined cannot ' - 'be read.' % (filename,data)) - return - use_temp = False - - if use_temp: - filename = self.shell.mktempfile(data) - print 'IPython will make a temporary file named:',filename - - return filename, lineno, use_temp - - def _edit_macro(self,mname,macro): - """open an editor with the macro data in a file""" - filename = self.shell.mktempfile(macro.value) - self.shell.hooks.editor(filename) - - # and make a new macro object, to replace the old one - mfile = open(filename) - mvalue = mfile.read() - mfile.close() - self.shell.user_ns[mname] = Macro(mvalue) - - def magic_ed(self,parameter_s=''): - """Alias to %edit.""" - return self.magic_edit(parameter_s) - - @skip_doctest - def magic_edit(self,parameter_s='',last_call=['','']): - """Bring up an editor and execute the resulting code. - - Usage: - %edit [options] [args] - - %edit runs IPython's editor hook. The default version of this hook is - set to call the editor specified by your $EDITOR environment variable. - If this isn't found, it will default to vi under Linux/Unix and to - notepad under Windows. See the end of this docstring for how to change - the editor hook. - - You can also set the value of this editor via the - ``TerminalInteractiveShell.editor`` option in your configuration file. - This is useful if you wish to use a different editor from your typical - default with IPython (and for Windows users who typically don't set - environment variables). - - This command allows you to conveniently edit multi-line code right in - your IPython session. - - If called without arguments, %edit opens up an empty editor with a - temporary file and will execute the contents of this file when you - close it (don't forget to save it!). - - - Options: - - -n : open the editor at a specified line number. By default, - the IPython editor hook uses the unix syntax 'editor +N filename', but - you can configure this by providing your own modified hook if your - favorite editor supports line-number specifications with a different - syntax. - - -p: this will call the editor with the same data as the previous time - it was used, regardless of how long ago (in your current session) it - was. - - -r: use 'raw' input. This option only applies to input taken from the - user's history. By default, the 'processed' history is used, so that - magics are loaded in their transformed version to valid Python. If - this option is given, the raw input as typed as the command line is - used instead. When you exit the editor, it will be executed by - IPython's own processor. - - -x: do not execute the edited code immediately upon exit. This is - mainly useful if you are editing programs which need to be called with - command line arguments, which you can then do using %run. - - - Arguments: - - If arguments are given, the following possibilities exist: - - - If the argument is a filename, IPython will load that into the - editor. It will execute its contents with execfile() when you exit, - loading any code in the file into your interactive namespace. - - - The arguments are ranges of input history, e.g. "7 ~1/4-6". - The syntax is the same as in the %history magic. - - - If the argument is a string variable, its contents are loaded - into the editor. You can thus edit any string which contains - python code (including the result of previous edits). - - - If the argument is the name of an object (other than a string), - IPython will try to locate the file where it was defined and open the - editor at the point where it is defined. You can use `%edit function` - to load an editor exactly at the point where 'function' is defined, - edit it and have the file be executed automatically. - - - If the object is a macro (see %macro for details), this opens up your - specified editor with a temporary file containing the macro's data. - Upon exit, the macro is reloaded with the contents of the file. - - Note: opening at an exact line is only supported under Unix, and some - editors (like kedit and gedit up to Gnome 2.8) do not understand the - '+NUMBER' parameter necessary for this feature. Good editors like - (X)Emacs, vi, jed, pico and joe all do. - - After executing your code, %edit will return as output the code you - typed in the editor (except when it was an existing file). This way - you can reload the code in further invocations of %edit as a variable, - via _ or Out[], where is the prompt number of - the output. - - Note that %edit is also available through the alias %ed. - - This is an example of creating a simple function inside the editor and - then modifying it. First, start up the editor:: - - In [1]: ed - Editing... done. Executing edited code... - Out[1]: 'def foo():\\n print "foo() was defined in an editing - session"\\n' - - We can then call the function foo():: - - In [2]: foo() - foo() was defined in an editing session - - Now we edit foo. IPython automatically loads the editor with the - (temporary) file where foo() was previously defined:: - - In [3]: ed foo - Editing... done. Executing edited code... - - And if we call foo() again we get the modified version:: - - In [4]: foo() - foo() has now been changed! - - Here is an example of how to edit a code snippet successive - times. First we call the editor:: - - In [5]: ed - Editing... done. Executing edited code... - hello - Out[5]: "print 'hello'\\n" - - Now we call it again with the previous output (stored in _):: - - In [6]: ed _ - Editing... done. Executing edited code... - hello world - Out[6]: "print 'hello world'\\n" - - Now we call it with the output #8 (stored in _8, also as Out[8]):: - - In [7]: ed _8 - Editing... done. Executing edited code... - hello again - Out[7]: "print 'hello again'\\n" - - - Changing the default editor hook: - - If you wish to write your own editor hook, you can put it in a - configuration file which you load at startup time. The default hook - is defined in the IPython.core.hooks module, and you can use that as a - starting example for further modifications. That file also has - general instructions on how to set a new hook for use once you've - defined it.""" - opts,args = self.parse_options(parameter_s,'prxn:') - - try: - filename, lineno, is_temp = self._find_edit_target(args, opts, last_call) - except MacroToEdit as e: - self._edit_macro(args, e.args[0]) - return - - # do actual editing here - print 'Editing...', - sys.stdout.flush() - try: - # Quote filenames that may have spaces in them - if ' ' in filename: - filename = "'%s'" % filename - self.shell.hooks.editor(filename,lineno) - except TryNext: - warn('Could not open editor') - return - - # XXX TODO: should this be generalized for all string vars? - # For now, this is special-cased to blocks created by cpaste - if args.strip() == 'pasted_block': - self.shell.user_ns['pasted_block'] = file_read(filename) - - if 'x' in opts: # -x prevents actual execution - print - else: - print 'done. Executing edited code...' - if 'r' in opts: # Untranslated IPython code - self.shell.run_cell(file_read(filename), - store_history=False) - else: - self.shell.safe_execfile(filename, self.shell.user_ns, - self.shell.user_ns) - - if is_temp: - try: - return open(filename).read() - except IOError,msg: - if msg.filename == filename: - warn('File not found. Did you forget to save?') - return - else: - self.shell.showtraceback() - - -class ConfigMagics(MagicFunctions): - - def __init__(self, shell): - super(ProfileMagics, self).__init__(shell) - self.configurables = [] - - def magic_config(self, s): - """configure IPython - - %config Class[.trait=value] - - This magic exposes most of the IPython config system. Any - Configurable class should be able to be configured with the simple - line:: - - %config Class.trait=value - - Where `value` will be resolved in the user's namespace, if it is an - expression or variable name. - - Examples - -------- - - To see what classes are available for config, pass no arguments:: - - In [1]: %config - Available objects for config: - TerminalInteractiveShell - HistoryManager - PrefilterManager - AliasManager - IPCompleter - PromptManager - DisplayFormatter - - To view what is configurable on a given class, just pass the class - name:: - - In [2]: %config IPCompleter - IPCompleter options - ----------------- - IPCompleter.omit__names= - Current: 2 - Choices: (0, 1, 2) - Instruct the completer to omit private method names - Specifically, when completing on ``object.``. - When 2 [default]: all names that start with '_' will be excluded. - When 1: all 'magic' names (``__foo__``) will be excluded. - When 0: nothing will be excluded. - IPCompleter.merge_completions= - Current: True - Whether to merge completion results into a single list - If False, only the completion results from the first non-empty completer - will be returned. - IPCompleter.limit_to__all__= - Current: False - Instruct the completer to use __all__ for the completion - Specifically, when completing on ``object.``. - When True: only those names in obj.__all__ will be included. - When False [default]: the __all__ attribute is ignored - IPCompleter.greedy= - Current: False - Activate greedy completion - This will enable completion on elements of lists, results of function calls, - etc., but can be unsafe because the code is actually evaluated on TAB. - - but the real use is in setting values:: - - In [3]: %config IPCompleter.greedy = True - - and these values are read from the user_ns if they are variables:: - - In [4]: feeling_greedy=False - - In [5]: %config IPCompleter.greedy = feeling_greedy - - """ - from IPython.config.loader import Config - # some IPython objects are Configurable, but do not yet have - # any configurable traits. Exclude them from the effects of - # this magic, as their presence is just noise: - configurables = [ c for c in self.shell.configurables - if c.__class__.class_traits(config=True) ] - classnames = [ c.__class__.__name__ for c in configurables ] - - line = s.strip() - if not line: - # print available configurable names - print "Available objects for config:" - for name in classnames: - print " ", name - return - elif line in classnames: - # `%config TerminalInteractiveShell` will print trait info for - # TerminalInteractiveShell - c = configurables[classnames.index(line)] - cls = c.__class__ - help = cls.class_get_help(c) - # strip leading '--' from cl-args: - help = re.sub(re.compile(r'^--', re.MULTILINE), '', help) - print help - return - elif '=' not in line: - raise UsageError("Invalid config statement: %r, should be Class.trait = value" % line) - - - # otherwise, assume we are setting configurables. - # leave quotes on args when splitting, because we want - # unquoted args to eval in user_ns - cfg = Config() - exec "cfg."+line in locals(), self.shell.user_ns - - for configurable in configurables: - try: - configurable.update_config(cfg) - except Exception as e: - error(e) - - -class NamespaceMagics(MagicFunctions): - """Magics to manage various aspects of the user's namespace. - - These include listing variables, introspecting into them, etc. - """ - - def magic_pinfo(self, parameter_s='', namespaces=None): - """Provide detailed information about an object. - - '%pinfo object' is just a synonym for object? or ?object.""" - - #print 'pinfo par: <%s>' % parameter_s # dbg - - - # detail_level: 0 -> obj? , 1 -> obj?? - detail_level = 0 - # We need to detect if we got called as 'pinfo pinfo foo', which can - # happen if the user types 'pinfo foo?' at the cmd line. - pinfo,qmark1,oname,qmark2 = \ - re.match('(pinfo )?(\?*)(.*?)(\??$)',parameter_s).groups() - if pinfo or qmark1 or qmark2: - detail_level = 1 - if "*" in oname: - self.magic_psearch(oname) - else: - self.shell._inspect('pinfo', oname, detail_level=detail_level, - namespaces=namespaces) - - def magic_pinfo2(self, parameter_s='', namespaces=None): - """Provide extra detailed information about an object. - - '%pinfo2 object' is just a synonym for object?? or ??object.""" - self.shell._inspect('pinfo', parameter_s, detail_level=1, - namespaces=namespaces) - - @skip_doctest - def magic_pdef(self, parameter_s='', namespaces=None): - """Print the definition header for any callable object. - - If the object is a class, print the constructor information. - - Examples - -------- - :: - - In [3]: %pdef urllib.urlopen - urllib.urlopen(url, data=None, proxies=None) - """ - self._inspect('pdef',parameter_s, namespaces) - - def magic_pdoc(self, parameter_s='', namespaces=None): - """Print the docstring for an object. - - If the given object is a class, it will print both the class and the - constructor docstrings.""" - self._inspect('pdoc',parameter_s, namespaces) - - def magic_psource(self, parameter_s='', namespaces=None): - """Print (or run through pager) the source code for an object.""" - self._inspect('psource',parameter_s, namespaces) - - def magic_pfile(self, parameter_s=''): - """Print (or run through pager) the file where an object is defined. - - The file opens at the line where the object definition begins. IPython - will honor the environment variable PAGER if set, and otherwise will - do its best to print the file in a convenient form. - - If the given argument is not an object currently defined, IPython will - try to interpret it as a filename (automatically adding a .py extension - if needed). You can thus use %pfile as a syntax highlighting code - viewer.""" - - # first interpret argument as an object name - out = self._inspect('pfile',parameter_s) - # if not, try the input as a filename - if out == 'not found': - try: - filename = get_py_filename(parameter_s) - except IOError,msg: - print msg - return - page.page(self.shell.inspector.format(open(filename).read())) - - def magic_psearch(self, parameter_s=''): - """Search for object in namespaces by wildcard. - - %psearch [options] PATTERN [OBJECT TYPE] - - Note: ? can be used as a synonym for %psearch, at the beginning or at - the end: both a*? and ?a* are equivalent to '%psearch a*'. Still, the - rest of the command line must be unchanged (options come first), so - for example the following forms are equivalent - - %psearch -i a* function - -i a* function? - ?-i a* function - - Arguments: - - PATTERN - - where PATTERN is a string containing * as a wildcard similar to its - use in a shell. The pattern is matched in all namespaces on the - search path. By default objects starting with a single _ are not - matched, many IPython generated objects have a single - underscore. The default is case insensitive matching. Matching is - also done on the attributes of objects and not only on the objects - in a module. - - [OBJECT TYPE] - - Is the name of a python type from the types module. The name is - given in lowercase without the ending type, ex. StringType is - written string. By adding a type here only objects matching the - given type are matched. Using all here makes the pattern match all - types (this is the default). - - Options: - - -a: makes the pattern match even objects whose names start with a - single underscore. These names are normally omitted from the - search. - - -i/-c: make the pattern case insensitive/sensitive. If neither of - these options are given, the default is read from your configuration - file, with the option ``InteractiveShell.wildcards_case_sensitive``. - If this option is not specified in your configuration file, IPython's - internal default is to do a case sensitive search. - - -e/-s NAMESPACE: exclude/search a given namespace. The pattern you - specify can be searched in any of the following namespaces: - 'builtin', 'user', 'user_global','internal', 'alias', where - 'builtin' and 'user' are the search defaults. Note that you should - not use quotes when specifying namespaces. - - 'Builtin' contains the python module builtin, 'user' contains all - user data, 'alias' only contain the shell aliases and no python - objects, 'internal' contains objects used by IPython. The - 'user_global' namespace is only used by embedded IPython instances, - and it contains module-level globals. You can add namespaces to the - search with -s or exclude them with -e (these options can be given - more than once). - - Examples - -------- - :: - - %psearch a* -> objects beginning with an a - %psearch -e builtin a* -> objects NOT in the builtin space starting in a - %psearch a* function -> all functions beginning with an a - %psearch re.e* -> objects beginning with an e in module re - %psearch r*.e* -> objects that start with e in modules starting in r - %psearch r*.* string -> all strings in modules beginning with r - - Case sensitive search:: - - %psearch -c a* list all object beginning with lower case a - - Show objects beginning with a single _:: - - %psearch -a _* list objects beginning with a single underscore""" - try: - parameter_s.encode('ascii') - except UnicodeEncodeError: - print 'Python identifiers can only contain ascii characters.' - return - - # default namespaces to be searched - def_search = ['user_local', 'user_global', 'builtin'] - - # Process options/args - opts,args = self.parse_options(parameter_s,'cias:e:',list_all=True) - opt = opts.get - shell = self.shell - psearch = shell.inspector.psearch - - # select case options - if opts.has_key('i'): - ignore_case = True - elif opts.has_key('c'): - ignore_case = False - else: - ignore_case = not shell.wildcards_case_sensitive - - # Build list of namespaces to search from user options - def_search.extend(opt('s',[])) - ns_exclude = ns_exclude=opt('e',[]) - ns_search = [nm for nm in def_search if nm not in ns_exclude] - - # Call the actual search - try: - psearch(args,shell.ns_table,ns_search, - show_all=opt('a'),ignore_case=ignore_case) - except: - shell.showtraceback() - - @skip_doctest - def magic_who_ls(self, parameter_s=''): - """Return a sorted list of all interactive variables. - - If arguments are given, only variables of types matching these - arguments are returned. - - Examples - -------- - - Define two variables and list them with who_ls:: - - In [1]: alpha = 123 - - In [2]: beta = 'test' - - In [3]: %who_ls - Out[3]: ['alpha', 'beta'] - - In [4]: %who_ls int - Out[4]: ['alpha'] - - In [5]: %who_ls str - Out[5]: ['beta'] - """ - - user_ns = self.shell.user_ns - user_ns_hidden = self.shell.user_ns_hidden - out = [ i for i in user_ns - if not i.startswith('_') \ - and not i in user_ns_hidden ] - - typelist = parameter_s.split() - if typelist: - typeset = set(typelist) - out = [i for i in out if type(user_ns[i]).__name__ in typeset] - - out.sort() - return out - - @skip_doctest - def magic_who(self, parameter_s=''): - """Print all interactive variables, with some minimal formatting. - - If any arguments are given, only variables whose type matches one of - these are printed. For example:: - - %who function str - - will only list functions and strings, excluding all other types of - variables. To find the proper type names, simply use type(var) at a - command line to see how python prints type names. For example: - - :: - - In [1]: type('hello')\\ - Out[1]: - - indicates that the type name for strings is 'str'. - - ``%who`` always excludes executed names loaded through your configuration - file and things which are internal to IPython. - - This is deliberate, as typically you may load many modules and the - purpose of %who is to show you only what you've manually defined. - - Examples - -------- - - Define two variables and list them with who:: - - In [1]: alpha = 123 - - In [2]: beta = 'test' - - In [3]: %who - alpha beta - - In [4]: %who int - alpha - - In [5]: %who str - beta - """ - - varlist = self.magic_who_ls(parameter_s) - if not varlist: - if parameter_s: - print 'No variables match your requested type.' - else: - print 'Interactive namespace is empty.' - return - - # if we have variables, move on... - count = 0 - for i in varlist: - print i+'\t', - count += 1 - if count > 8: - count = 0 - print - print - - @skip_doctest - def magic_whos(self, parameter_s=''): - """Like %who, but gives some extra information about each variable. - - The same type filtering of %who can be applied here. - - For all variables, the type is printed. Additionally it prints: - - - For {},[],(): their length. - - - For numpy arrays, a summary with shape, number of - elements, typecode and size in memory. - - - Everything else: a string representation, snipping their middle if - too long. - - Examples - -------- - - Define two variables and list them with whos:: - - In [1]: alpha = 123 - - In [2]: beta = 'test' - - In [3]: %whos - Variable Type Data/Info - -------------------------------- - alpha int 123 - beta str test - """ - - varnames = self.magic_who_ls(parameter_s) - if not varnames: - if parameter_s: - print 'No variables match your requested type.' - else: - print 'Interactive namespace is empty.' - return - - # if we have variables, move on... - - # for these types, show len() instead of data: - seq_types = ['dict', 'list', 'tuple'] - - # for numpy arrays, display summary info - ndarray_type = None - if 'numpy' in sys.modules: - try: - from numpy import ndarray - except ImportError: - pass - else: - ndarray_type = ndarray.__name__ - - # Find all variable names and types so we can figure out column sizes - def get_vars(i): - return self.shell.user_ns[i] - - # some types are well known and can be shorter - abbrevs = {'IPython.core.macro.Macro' : 'Macro'} - def type_name(v): - tn = type(v).__name__ - return abbrevs.get(tn,tn) - - varlist = map(get_vars,varnames) - - typelist = [] - for vv in varlist: - tt = type_name(vv) - - if tt=='instance': - typelist.append( abbrevs.get(str(vv.__class__), - str(vv.__class__))) - else: - typelist.append(tt) - - # column labels and # of spaces as separator - varlabel = 'Variable' - typelabel = 'Type' - datalabel = 'Data/Info' - colsep = 3 - # variable format strings - vformat = "{0:<{varwidth}}{1:<{typewidth}}" - aformat = "%s: %s elems, type `%s`, %s bytes" - # find the size of the columns to format the output nicely - varwidth = max(max(map(len,varnames)), len(varlabel)) + colsep - typewidth = max(max(map(len,typelist)), len(typelabel)) + colsep - # table header - print varlabel.ljust(varwidth) + typelabel.ljust(typewidth) + \ - ' '+datalabel+'\n' + '-'*(varwidth+typewidth+len(datalabel)+1) - # and the table itself - kb = 1024 - Mb = 1048576 # kb**2 - for vname,var,vtype in zip(varnames,varlist,typelist): - print vformat.format(vname, vtype, varwidth=varwidth, typewidth=typewidth), - if vtype in seq_types: - print "n="+str(len(var)) - elif vtype == ndarray_type: - vshape = str(var.shape).replace(',','').replace(' ','x')[1:-1] - if vtype==ndarray_type: - # numpy - vsize = var.size - vbytes = vsize*var.itemsize - vdtype = var.dtype - - if vbytes < 100000: - print aformat % (vshape,vsize,vdtype,vbytes) - else: - print aformat % (vshape,vsize,vdtype,vbytes), - if vbytes < Mb: - print '(%s kb)' % (vbytes/kb,) - else: - print '(%s Mb)' % (vbytes/Mb,) - else: - try: - vstr = str(var) - except UnicodeEncodeError: - vstr = unicode(var).encode(DEFAULT_ENCODING, - 'backslashreplace') - except: - vstr = "" % id(var) - vstr = vstr.replace('\n','\\n') - if len(vstr) < 50: - print vstr - else: - print vstr[:25] + "<...>" + vstr[-25:] - - def magic_reset(self, parameter_s=''): - """Resets the namespace by removing all names defined by the user, if - called without arguments, or by removing some types of objects, such - as everything currently in IPython's In[] and Out[] containers (see - the parameters for details). - - Parameters - ---------- - -f : force reset without asking for confirmation. - - -s : 'Soft' reset: Only clears your namespace, leaving history intact. - References to objects may be kept. By default (without this option), - we do a 'hard' reset, giving you a new session and removing all - references to objects from the current session. - - in : reset input history - - out : reset output history - - dhist : reset directory history - - array : reset only variables that are NumPy arrays - - See Also - -------- - magic_reset_selective : invoked as ``%reset_selective`` - - Examples - -------- - :: - - In [6]: a = 1 - - In [7]: a - Out[7]: 1 - - In [8]: 'a' in _ip.user_ns - Out[8]: True - - In [9]: %reset -f - - In [1]: 'a' in _ip.user_ns - Out[1]: False - - In [2]: %reset -f in - Flushing input history - - In [3]: %reset -f dhist in - Flushing directory history - Flushing input history - - Notes - ----- - Calling this magic from clients that do not implement standard input, - such as the ipython notebook interface, will reset the namespace - without confirmation. - """ - opts, args = self.parse_options(parameter_s,'sf', mode='list') - if 'f' in opts: - ans = True - else: - try: - ans = self.shell.ask_yes_no( - "Once deleted, variables cannot be recovered. Proceed (y/[n])? ", default='n') - except StdinNotImplementedError: - ans = True - if not ans: - print 'Nothing done.' - return - - if 's' in opts: # Soft reset - user_ns = self.shell.user_ns - for i in self.magic_who_ls(): - del(user_ns[i]) - elif len(args) == 0: # Hard reset - self.shell.reset(new_session = False) - - # reset in/out/dhist/array: previously extensinions/clearcmd.py - ip = self.shell - user_ns = self.shell.user_ns # local lookup, heavily used - - for target in args: - target = target.lower() # make matches case insensitive - if target == 'out': - print "Flushing output cache (%d entries)" % len(user_ns['_oh']) - self.shell.displayhook.flush() - - elif target == 'in': - print "Flushing input history" - pc = self.shell.displayhook.prompt_count + 1 - for n in range(1, pc): - key = '_i'+repr(n) - user_ns.pop(key,None) - user_ns.update(dict(_i=u'',_ii=u'',_iii=u'')) - hm = ip.history_manager - # don't delete these, as %save and %macro depending on the length - # of these lists to be preserved - hm.input_hist_parsed[:] = [''] * pc - hm.input_hist_raw[:] = [''] * pc - # hm has internal machinery for _i,_ii,_iii, clear it out - hm._i = hm._ii = hm._iii = hm._i00 = u'' - - elif target == 'array': - # Support cleaning up numpy arrays - try: - from numpy import ndarray - # This must be done with items and not iteritems because we're - # going to modify the dict in-place. - for x,val in user_ns.items(): - if isinstance(val,ndarray): - del user_ns[x] - except ImportError: - print "reset array only works if Numpy is available." - - elif target == 'dhist': - print "Flushing directory history" - del user_ns['_dh'][:] - - else: - print "Don't know how to reset ", - print target + ", please run `%reset?` for details" - - gc.collect() - - def magic_reset_selective(self, parameter_s=''): - """Resets the namespace by removing names defined by the user. - - Input/Output history are left around in case you need them. - - %reset_selective [-f] regex - - No action is taken if regex is not included - - Options - -f : force reset without asking for confirmation. - - See Also - -------- - magic_reset : invoked as ``%reset`` - - Examples - -------- - - We first fully reset the namespace so your output looks identical to - this example for pedagogical reasons; in practice you do not need a - full reset:: - - In [1]: %reset -f - - Now, with a clean namespace we can make a few variables and use - ``%reset_selective`` to only delete names that match our regexp:: - - In [2]: a=1; b=2; c=3; b1m=4; b2m=5; b3m=6; b4m=7; b2s=8 - - In [3]: who_ls - Out[3]: ['a', 'b', 'b1m', 'b2m', 'b2s', 'b3m', 'b4m', 'c'] - - In [4]: %reset_selective -f b[2-3]m - - In [5]: who_ls - Out[5]: ['a', 'b', 'b1m', 'b2s', 'b4m', 'c'] - - In [6]: %reset_selective -f d - - In [7]: who_ls - Out[7]: ['a', 'b', 'b1m', 'b2s', 'b4m', 'c'] - - In [8]: %reset_selective -f c - - In [9]: who_ls - Out[9]: ['a', 'b', 'b1m', 'b2s', 'b4m'] - - In [10]: %reset_selective -f b - - In [11]: who_ls - Out[11]: ['a'] - - Notes - ----- - Calling this magic from clients that do not implement standard input, - such as the ipython notebook interface, will reset the namespace - without confirmation. - """ - - opts, regex = self.parse_options(parameter_s,'f') - - if opts.has_key('f'): - ans = True - else: - try: - ans = self.shell.ask_yes_no( - "Once deleted, variables cannot be recovered. Proceed (y/[n])? ", - default='n') - except StdinNotImplementedError: - ans = True - if not ans: - print 'Nothing done.' - return - user_ns = self.shell.user_ns - if not regex: - print 'No regex pattern specified. Nothing done.' - return - else: - try: - m = re.compile(regex) - except TypeError: - raise TypeError('regex must be a string or compiled pattern') - for i in self.magic_who_ls(): - if m.search(i): - del(user_ns[i]) - - def magic_xdel(self, parameter_s=''): - """Delete a variable, trying to clear it from anywhere that - IPython's machinery has references to it. By default, this uses - the identity of the named object in the user namespace to remove - references held under other names. The object is also removed - from the output history. - - Options - -n : Delete the specified name from all namespaces, without - checking their identity. - """ - opts, varname = self.parse_options(parameter_s,'n') - try: - self.shell.del_var(varname, ('n' in opts)) - except (NameError, ValueError) as e: - print type(e).__name__ +": "+ str(e) - - -class ExecutionMagics(MagicFunctions): - """Magics related to code execution, debugging, profiling, etc. - - """ - - def __init__(self, shell): - super(ProfileMagics, self).__init__(shell) - if profile is None: - self.magic_prun = self.profile_missing_notice - # Default execution function used to actually run user code. - self.default_runner = None - - def profile_missing_notice(self, *args, **kwargs): - error("""\ -The profile module could not be found. It has been removed from the standard -python packages because of its non-free license. To use profiling, install the -python-profiler package from non-free.""") - - @skip_doctest - def magic_prun(self, parameter_s ='',user_mode=1, - opts=None,arg_lst=None,prog_ns=None): - - """Run a statement through the python code profiler. - - Usage: - %prun [options] statement - - The given statement (which doesn't require quote marks) is run via the - python profiler in a manner similar to the profile.run() function. - Namespaces are internally managed to work correctly; profile.run - cannot be used in IPython because it makes certain assumptions about - namespaces which do not hold under IPython. - - Options: - - -l : you can place restrictions on what or how much of the - profile gets printed. The limit value can be: - - * A string: only information for function names containing this string - is printed. - - * An integer: only these many lines are printed. - - * A float (between 0 and 1): this fraction of the report is printed - (for example, use a limit of 0.4 to see the topmost 40% only). - - You can combine several limits with repeated use of the option. For - example, '-l __init__ -l 5' will print only the topmost 5 lines of - information about class constructors. - - -r: return the pstats.Stats object generated by the profiling. This - object has all the information about the profile in it, and you can - later use it for further analysis or in other functions. - - -s : sort profile by given key. You can provide more than one key - by using the option several times: '-s key1 -s key2 -s key3...'. The - default sorting key is 'time'. - - The following is copied verbatim from the profile documentation - referenced below: - - When more than one key is provided, additional keys are used as - secondary criteria when the there is equality in all keys selected - before them. - - Abbreviations can be used for any key names, as long as the - abbreviation is unambiguous. The following are the keys currently - defined: - - Valid Arg Meaning - "calls" call count - "cumulative" cumulative time - "file" file name - "module" file name - "pcalls" primitive call count - "line" line number - "name" function name - "nfl" name/file/line - "stdname" standard name - "time" internal time - - Note that all sorts on statistics are in descending order (placing - most time consuming items first), where as name, file, and line number - searches are in ascending order (i.e., alphabetical). The subtle - distinction between "nfl" and "stdname" is that the standard name is a - sort of the name as printed, which means that the embedded line - numbers get compared in an odd way. For example, lines 3, 20, and 40 - would (if the file names were the same) appear in the string order - "20" "3" and "40". In contrast, "nfl" does a numeric compare of the - line numbers. In fact, sort_stats("nfl") is the same as - sort_stats("name", "file", "line"). - - -T : save profile results as shown on screen to a text - file. The profile is still shown on screen. - - -D : save (via dump_stats) profile statistics to given - filename. This data is in a format understood by the pstats module, and - is generated by a call to the dump_stats() method of profile - objects. The profile is still shown on screen. - - -q: suppress output to the pager. Best used with -T and/or -D above. - - If you want to run complete programs under the profiler's control, use - '%run -p [prof_opts] filename.py [args to program]' where prof_opts - contains profiler specific options as described here. - - You can read the complete documentation for the profile module with:: - - In [1]: import profile; profile.help() - """ - - opts_def = Struct(D=[''],l=[],s=['time'],T=['']) - - if user_mode: # regular user call - opts,arg_str = self.parse_options(parameter_s,'D:l:rs:T:q', - list_all=1, posix=False) - namespace = self.shell.user_ns - else: # called to run a program by %run -p - try: - filename = get_py_filename(arg_lst[0]) - except IOError as e: - try: - msg = str(e) - except UnicodeError: - msg = e.message - error(msg) - return - - arg_str = 'execfile(filename,prog_ns)' - namespace = { - 'execfile': self.shell.safe_execfile, - 'prog_ns': prog_ns, - 'filename': filename - } - - opts.merge(opts_def) - - prof = profile.Profile() - try: - prof = prof.runctx(arg_str,namespace,namespace) - sys_exit = '' - except SystemExit: - sys_exit = """*** SystemExit exception caught in code being profiled.""" - - stats = pstats.Stats(prof).strip_dirs().sort_stats(*opts.s) - - lims = opts.l - if lims: - lims = [] # rebuild lims with ints/floats/strings - for lim in opts.l: - try: - lims.append(int(lim)) - except ValueError: - try: - lims.append(float(lim)) - except ValueError: - lims.append(lim) - - # Trap output. - stdout_trap = StringIO() - - if hasattr(stats,'stream'): - # In newer versions of python, the stats object has a 'stream' - # attribute to write into. - stats.stream = stdout_trap - stats.print_stats(*lims) - else: - # For older versions, we manually redirect stdout during printing - sys_stdout = sys.stdout - try: - sys.stdout = stdout_trap - stats.print_stats(*lims) - finally: - sys.stdout = sys_stdout - - output = stdout_trap.getvalue() - output = output.rstrip() - - if 'q' not in opts: - page.page(output) - print sys_exit, - - dump_file = opts.D[0] - text_file = opts.T[0] - if dump_file: - dump_file = unquote_filename(dump_file) - prof.dump_stats(dump_file) - print '\n*** Profile stats marshalled to file',\ - `dump_file`+'.',sys_exit - if text_file: - text_file = unquote_filename(text_file) - pfile = open(text_file,'w') - pfile.write(output) - pfile.close() - print '\n*** Profile printout saved to text file',\ - `text_file`+'.',sys_exit - - if opts.has_key('r'): - return stats - else: - return None - - def magic_pdb(self, parameter_s=''): - """Control the automatic calling of the pdb interactive debugger. - - Call as '%pdb on', '%pdb 1', '%pdb off' or '%pdb 0'. If called without - argument it works as a toggle. - - When an exception is triggered, IPython can optionally call the - interactive pdb debugger after the traceback printout. %pdb toggles - this feature on and off. - - The initial state of this feature is set in your configuration - file (the option is ``InteractiveShell.pdb``). - - If you want to just activate the debugger AFTER an exception has fired, - without having to type '%pdb on' and rerunning your code, you can use - the %debug magic.""" - - par = parameter_s.strip().lower() - - if par: - try: - new_pdb = {'off':0,'0':0,'on':1,'1':1}[par] - except KeyError: - print ('Incorrect argument. Use on/1, off/0, ' - 'or nothing for a toggle.') - return - else: - # toggle - new_pdb = not self.shell.call_pdb - - # set on the shell - self.shell.call_pdb = new_pdb - print 'Automatic pdb calling has been turned',on_off(new_pdb) - - def magic_debug(self, parameter_s=''): - """Activate the interactive debugger in post-mortem mode. - - If an exception has just occurred, this lets you inspect its stack - frames interactively. Note that this will always work only on the last - traceback that occurred, so you must call this quickly after an - exception that you wish to inspect has fired, because if another one - occurs, it clobbers the previous one. - - If you want IPython to automatically do this on every exception, see - the %pdb magic for more details. - """ - self.shell.debugger(force=True) - - def magic_tb(self, s): - """Print the last traceback with the currently active exception mode. - - See %xmode for changing exception reporting modes.""" - self.shell.showtraceback() - - @skip_doctest - def magic_run(self, parameter_s ='', runner=None, - file_finder=get_py_filename): - """Run the named file inside IPython as a program. - - Usage:\\ - %run [-n -i -t [-N] -d [-b] -p [profile options]] file [args] - - Parameters after the filename are passed as command-line arguments to - the program (put in sys.argv). Then, control returns to IPython's - prompt. - - This is similar to running at a system prompt:\\ - $ python file args\\ - but with the advantage of giving you IPython's tracebacks, and of - loading all variables into your interactive namespace for further use - (unless -p is used, see below). - - The file is executed in a namespace initially consisting only of - __name__=='__main__' and sys.argv constructed as indicated. It thus - sees its environment as if it were being run as a stand-alone program - (except for sharing global objects such as previously imported - modules). But after execution, the IPython interactive namespace gets - updated with all variables defined in the program (except for __name__ - and sys.argv). This allows for very convenient loading of code for - interactive work, while giving each program a 'clean sheet' to run in. - - Options: - - -n: __name__ is NOT set to '__main__', but to the running file's name - without extension (as python does under import). This allows running - scripts and reloading the definitions in them without calling code - protected by an ' if __name__ == "__main__" ' clause. - - -i: run the file in IPython's namespace instead of an empty one. This - is useful if you are experimenting with code written in a text editor - which depends on variables defined interactively. - - -e: ignore sys.exit() calls or SystemExit exceptions in the script - being run. This is particularly useful if IPython is being used to - run unittests, which always exit with a sys.exit() call. In such - cases you are interested in the output of the test results, not in - seeing a traceback of the unittest module. - - -t: print timing information at the end of the run. IPython will give - you an estimated CPU time consumption for your script, which under - Unix uses the resource module to avoid the wraparound problems of - time.clock(). Under Unix, an estimate of time spent on system tasks - is also given (for Windows platforms this is reported as 0.0). - - If -t is given, an additional -N option can be given, where - must be an integer indicating how many times you want the script to - run. The final timing report will include total and per run results. - - For example (testing the script uniq_stable.py):: - - In [1]: run -t uniq_stable - - IPython CPU timings (estimated):\\ - User : 0.19597 s.\\ - System: 0.0 s.\\ - - In [2]: run -t -N5 uniq_stable - - IPython CPU timings (estimated):\\ - Total runs performed: 5\\ - Times : Total Per run\\ - User : 0.910862 s, 0.1821724 s.\\ - System: 0.0 s, 0.0 s. - - -d: run your program under the control of pdb, the Python debugger. - This allows you to execute your program step by step, watch variables, - etc. Internally, what IPython does is similar to calling: - - pdb.run('execfile("YOURFILENAME")') - - with a breakpoint set on line 1 of your file. You can change the line - number for this automatic breakpoint to be by using the -bN option - (where N must be an integer). For example:: - - %run -d -b40 myscript - - will set the first breakpoint at line 40 in myscript.py. Note that - the first breakpoint must be set on a line which actually does - something (not a comment or docstring) for it to stop execution. - - When the pdb debugger starts, you will see a (Pdb) prompt. You must - first enter 'c' (without quotes) to start execution up to the first - breakpoint. - - Entering 'help' gives information about the use of the debugger. You - can easily see pdb's full documentation with "import pdb;pdb.help()" - at a prompt. - - -p: run program under the control of the Python profiler module (which - prints a detailed report of execution times, function calls, etc). - - You can pass other options after -p which affect the behavior of the - profiler itself. See the docs for %prun for details. - - In this mode, the program's variables do NOT propagate back to the - IPython interactive namespace (because they remain in the namespace - where the profiler executes them). - - Internally this triggers a call to %prun, see its documentation for - details on the options available specifically for profiling. - - There is one special usage for which the text above doesn't apply: - if the filename ends with .ipy, the file is run as ipython script, - just as if the commands were written on IPython prompt. - - -m: specify module name to load instead of script path. Similar to - the -m option for the python interpreter. Use this option last if you - want to combine with other %run options. Unlike the python interpreter - only source modules are allowed no .pyc or .pyo files. - For example:: - - %run -m example - - will run the example module. - - """ - - # get arguments and set sys.argv for program to be run. - opts, arg_lst = self.parse_options(parameter_s, 'nidtN:b:pD:l:rs:T:em:', - mode='list', list_all=1) - if "m" in opts: - modulename = opts["m"][0] - modpath = find_mod(modulename) - if modpath is None: - warn('%r is not a valid modulename on sys.path'%modulename) - return - arg_lst = [modpath] + arg_lst - try: - filename = file_finder(arg_lst[0]) - except IndexError: - warn('you must provide at least a filename.') - print '\n%run:\n', oinspect.getdoc(self.magic_run) - return - except IOError as e: - try: - msg = str(e) - except UnicodeError: - msg = e.message - error(msg) - return - - if filename.lower().endswith('.ipy'): - self.shell.safe_execfile_ipy(filename) - return - - # Control the response to exit() calls made by the script being run - exit_ignore = 'e' in opts - - # Make sure that the running script gets a proper sys.argv as if it - # were run from a system shell. - save_argv = sys.argv # save it for later restoring - - # simulate shell expansion on arguments, at least tilde expansion - args = [ os.path.expanduser(a) for a in arg_lst[1:] ] - - sys.argv = [filename] + args # put in the proper filename - # protect sys.argv from potential unicode strings on Python 2: - if not py3compat.PY3: - sys.argv = [ py3compat.cast_bytes(a) for a in sys.argv ] - - if 'i' in opts: - # Run in user's interactive namespace - prog_ns = self.shell.user_ns - __name__save = self.shell.user_ns['__name__'] - prog_ns['__name__'] = '__main__' - main_mod = self.shell.new_main_mod(prog_ns) - else: - # Run in a fresh, empty namespace - if 'n' in opts: - name = os.path.splitext(os.path.basename(filename))[0] - else: - name = '__main__' - - main_mod = self.shell.new_main_mod() - prog_ns = main_mod.__dict__ - prog_ns['__name__'] = name - - # Since '%run foo' emulates 'python foo.py' at the cmd line, we must - # set the __file__ global in the script's namespace - prog_ns['__file__'] = filename - - # pickle fix. See interactiveshell for an explanation. But we need to make sure - # that, if we overwrite __main__, we replace it at the end - main_mod_name = prog_ns['__name__'] - - if main_mod_name == '__main__': - restore_main = sys.modules['__main__'] - else: - restore_main = False - - # This needs to be undone at the end to prevent holding references to - # every single object ever created. - sys.modules[main_mod_name] = main_mod - - try: - stats = None - with self.shell.readline_no_record: - if 'p' in opts: - stats = self.magic_prun('', 0, opts, arg_lst, prog_ns) - else: - if 'd' in opts: - deb = debugger.Pdb(self.shell.colors) - # reset Breakpoint state, which is moronically kept - # in a class - bdb.Breakpoint.next = 1 - bdb.Breakpoint.bplist = {} - bdb.Breakpoint.bpbynumber = [None] - # Set an initial breakpoint to stop execution - maxtries = 10 - bp = int(opts.get('b', [1])[0]) - checkline = deb.checkline(filename, bp) - if not checkline: - for bp in range(bp + 1, bp + maxtries + 1): - if deb.checkline(filename, bp): - break - else: - msg = ("\nI failed to find a valid line to set " - "a breakpoint\n" - "after trying up to line: %s.\n" - "Please set a valid breakpoint manually " - "with the -b option." % bp) - error(msg) - return - # if we find a good linenumber, set the breakpoint - deb.do_break('%s:%s' % (filename, bp)) - # Start file run - print "NOTE: Enter 'c' at the", - print "%s prompt to start your script." % deb.prompt - ns = {'execfile': py3compat.execfile, 'prog_ns': prog_ns} - try: - deb.run('execfile("%s", prog_ns)' % filename, ns) - - except: - etype, value, tb = sys.exc_info() - # Skip three frames in the traceback: the %run one, - # one inside bdb.py, and the command-line typed by the - # user (run by exec in pdb itself). - self.shell.InteractiveTB(etype, value, tb, tb_offset=3) - else: - if runner is None: - runner = self.default_runner - if runner is None: - runner = self.shell.safe_execfile - if 't' in opts: - # timed execution - try: - nruns = int(opts['N'][0]) - if nruns < 1: - error('Number of runs must be >=1') - return - except (KeyError): - nruns = 1 - twall0 = time.time() - if nruns == 1: - t0 = clock2() - runner(filename, prog_ns, prog_ns, - exit_ignore=exit_ignore) - t1 = clock2() - t_usr = t1[0] - t0[0] - t_sys = t1[1] - t0[1] - print "\nIPython CPU timings (estimated):" - print " User : %10.2f s." % t_usr - print " System : %10.2f s." % t_sys - else: - runs = range(nruns) - t0 = clock2() - for nr in runs: - runner(filename, prog_ns, prog_ns, - exit_ignore=exit_ignore) - t1 = clock2() - t_usr = t1[0] - t0[0] - t_sys = t1[1] - t0[1] - print "\nIPython CPU timings (estimated):" - print "Total runs performed:", nruns - print " Times : %10.2f %10.2f" % ('Total', 'Per run') - print " User : %10.2f s, %10.2f s." % (t_usr, t_usr / nruns) - print " System : %10.2f s, %10.2f s." % (t_sys, t_sys / nruns) - twall1 = time.time() - print "Wall time: %10.2f s." % (twall1 - twall0) - - else: - # regular execution - runner(filename, prog_ns, prog_ns, exit_ignore=exit_ignore) - - if 'i' in opts: - self.shell.user_ns['__name__'] = __name__save - else: - # The shell MUST hold a reference to prog_ns so after %run - # exits, the python deletion mechanism doesn't zero it out - # (leaving dangling references). - self.shell.cache_main_mod(prog_ns, filename) - # update IPython interactive namespace - - # Some forms of read errors on the file may mean the - # __name__ key was never set; using pop we don't have to - # worry about a possible KeyError. - prog_ns.pop('__name__', None) - - self.shell.user_ns.update(prog_ns) - finally: - # It's a bit of a mystery why, but __builtins__ can change from - # being a module to becoming a dict missing some key data after - # %run. As best I can see, this is NOT something IPython is doing - # at all, and similar problems have been reported before: - # http://coding.derkeiler.com/Archive/Python/comp.lang.python/2004-10/0188.html - # Since this seems to be done by the interpreter itself, the best - # we can do is to at least restore __builtins__ for the user on - # exit. - self.shell.user_ns['__builtins__'] = builtin_mod - - # Ensure key global structures are restored - sys.argv = save_argv - if restore_main: - sys.modules['__main__'] = restore_main - else: - # Remove from sys.modules the reference to main_mod we'd - # added. Otherwise it will trap references to objects - # contained therein. - del sys.modules[main_mod_name] - - return stats - - @skip_doctest - def magic_timeit(self, parameter_s =''): - """Time execution of a Python statement or expression - - Usage:\\ - %timeit [-n -r [-t|-c]] statement - - Time execution of a Python statement or expression using the timeit - module. - - Options: - -n: execute the given statement times in a loop. If this value - is not given, a fitting value is chosen. - - -r: repeat the loop iteration times and take the best result. - Default: 3 - - -t: use time.time to measure the time, which is the default on Unix. - This function measures wall time. - - -c: use time.clock to measure the time, which is the default on - Windows and measures wall time. On Unix, resource.getrusage is used - instead and returns the CPU user time. - - -p

: use a precision of

digits to display the timing result. - Default: 3 - - - Examples - -------- - :: - - In [1]: %timeit pass - 10000000 loops, best of 3: 53.3 ns per loop - - In [2]: u = None - - In [3]: %timeit u is None - 10000000 loops, best of 3: 184 ns per loop - - In [4]: %timeit -r 4 u == None - 1000000 loops, best of 4: 242 ns per loop - - In [5]: import time - - In [6]: %timeit -n1 time.sleep(2) - 1 loops, best of 3: 2 s per loop - - - The times reported by %timeit will be slightly higher than those - reported by the timeit.py script when variables are accessed. This is - due to the fact that %timeit executes the statement in the namespace - of the shell, compared with timeit.py, which uses a single setup - statement to import function or create variables. Generally, the bias - does not matter as long as results from timeit.py are not mixed with - those from %timeit.""" - - import timeit - import math - - # XXX: Unfortunately the unicode 'micro' symbol can cause problems in - # certain terminals. Until we figure out a robust way of - # auto-detecting if the terminal can deal with it, use plain 'us' for - # microseconds. I am really NOT happy about disabling the proper - # 'micro' prefix, but crashing is worse... If anyone knows what the - # right solution for this is, I'm all ears... - # - # Note: using - # - # s = u'\xb5' - # s.encode(sys.getdefaultencoding()) - # - # is not sufficient, as I've seen terminals where that fails but - # print s - # - # succeeds - # - # See bug: https://bugs.launchpad.net/ipython/+bug/348466 - - #units = [u"s", u"ms",u'\xb5',"ns"] - units = [u"s", u"ms",u'us',"ns"] - - scaling = [1, 1e3, 1e6, 1e9] - - opts, stmt = self.parse_options(parameter_s,'n:r:tcp:', - posix=False, strict=False) - if stmt == "": - return - timefunc = timeit.default_timer - number = int(getattr(opts, "n", 0)) - repeat = int(getattr(opts, "r", timeit.default_repeat)) - precision = int(getattr(opts, "p", 3)) - if hasattr(opts, "t"): - timefunc = time.time - if hasattr(opts, "c"): - timefunc = clock - - timer = timeit.Timer(timer=timefunc) - # this code has tight coupling to the inner workings of timeit.Timer, - # but is there a better way to achieve that the code stmt has access - # to the shell namespace? - - src = timeit.template % {'stmt': timeit.reindent(stmt, 8), - 'setup': "pass"} - # Track compilation time so it can be reported if too long - # Minimum time above which compilation time will be reported - tc_min = 0.1 - - t0 = clock() - code = compile(src, "", "exec") - tc = clock()-t0 - - ns = {} - exec code in self.shell.user_ns, ns - timer.inner = ns["inner"] - - if number == 0: - # determine number so that 0.2 <= total time < 2.0 - number = 1 - for i in range(1, 10): - if timer.timeit(number) >= 0.2: - break - number *= 10 - - best = min(timer.repeat(repeat, number)) / number - - if best > 0.0 and best < 1000.0: - order = min(-int(math.floor(math.log10(best)) // 3), 3) - elif best >= 1000.0: - order = 0 - else: - order = 3 - print u"%d loops, best of %d: %.*g %s per loop" % (number, repeat, - precision, - best * scaling[order], - units[order]) - if tc > tc_min: - print "Compiler time: %.2f s" % tc - - @skip_doctest - @needs_local_scope - def magic_time(self,parameter_s, user_locals): - """Time execution of a Python statement or expression. - - The CPU and wall clock times are printed, and the value of the - expression (if any) is returned. Note that under Win32, system time - is always reported as 0, since it can not be measured. - - This function provides very basic timing functionality. In Python - 2.3, the timeit module offers more control and sophistication, so this - could be rewritten to use it (patches welcome). - - Examples - -------- - :: - - In [1]: time 2**128 - CPU times: user 0.00 s, sys: 0.00 s, total: 0.00 s - Wall time: 0.00 - Out[1]: 340282366920938463463374607431768211456L - - In [2]: n = 1000000 - - In [3]: time sum(range(n)) - CPU times: user 1.20 s, sys: 0.05 s, total: 1.25 s - Wall time: 1.37 - Out[3]: 499999500000L - - In [4]: time print 'hello world' - hello world - CPU times: user 0.00 s, sys: 0.00 s, total: 0.00 s - Wall time: 0.00 - - Note that the time needed by Python to compile the given expression - will be reported if it is more than 0.1s. In this example, the - actual exponentiation is done by Python at compilation time, so while - the expression can take a noticeable amount of time to compute, that - time is purely due to the compilation: - - In [5]: time 3**9999; - CPU times: user 0.00 s, sys: 0.00 s, total: 0.00 s - Wall time: 0.00 s - - In [6]: time 3**999999; - CPU times: user 0.00 s, sys: 0.00 s, total: 0.00 s - Wall time: 0.00 s - Compiler : 0.78 s - """ - - # fail immediately if the given expression can't be compiled - - expr = self.shell.prefilter(parameter_s,False) - - # Minimum time above which compilation time will be reported - tc_min = 0.1 - - try: - mode = 'eval' - t0 = clock() - code = compile(expr,'',mode) - tc = clock()-t0 - except SyntaxError: - mode = 'exec' - t0 = clock() - code = compile(expr,'',mode) - tc = clock()-t0 - # skew measurement as little as possible - glob = self.shell.user_ns - wtime = time.time - # time execution - wall_st = wtime() - if mode=='eval': - st = clock2() - out = eval(code, glob, user_locals) - end = clock2() - else: - st = clock2() - exec code in glob, user_locals - end = clock2() - out = None - wall_end = wtime() - # Compute actual times and report - wall_time = wall_end-wall_st - cpu_user = end[0]-st[0] - cpu_sys = end[1]-st[1] - cpu_tot = cpu_user+cpu_sys - print "CPU times: user %.2f s, sys: %.2f s, total: %.2f s" % \ - (cpu_user,cpu_sys,cpu_tot) - print "Wall time: %.2f s" % wall_time - if tc > tc_min: - print "Compiler : %.2f s" % tc - return out - - @skip_doctest - def magic_macro(self,parameter_s = ''): - """Define a macro for future re-execution. It accepts ranges of history, - filenames or string objects. - - Usage:\\ - %macro [options] name n1-n2 n3-n4 ... n5 .. n6 ... - - Options: - - -r: use 'raw' input. By default, the 'processed' history is used, - so that magics are loaded in their transformed version to valid - Python. If this option is given, the raw input as typed as the - command line is used instead. - - This will define a global variable called `name` which is a string - made of joining the slices and lines you specify (n1,n2,... numbers - above) from your input history into a single string. This variable - acts like an automatic function which re-executes those lines as if - you had typed them. You just type 'name' at the prompt and the code - executes. - - The syntax for indicating input ranges is described in %history. - - Note: as a 'hidden' feature, you can also use traditional python slice - notation, where N:M means numbers N through M-1. - - For example, if your history contains (%hist prints it):: - - 44: x=1 - 45: y=3 - 46: z=x+y - 47: print x - 48: a=5 - 49: print 'x',x,'y',y - - you can create a macro with lines 44 through 47 (included) and line 49 - called my_macro with:: - - In [55]: %macro my_macro 44-47 49 - - Now, typing `my_macro` (without quotes) will re-execute all this code - in one pass. - - You don't need to give the line-numbers in order, and any given line - number can appear multiple times. You can assemble macros with any - lines from your input history in any order. - - The macro is a simple object which holds its value in an attribute, - but IPython's display system checks for macros and executes them as - code instead of printing them when you type their name. - - You can view a macro's contents by explicitly printing it with:: - - print macro_name - - """ - opts,args = self.parse_options(parameter_s,'r',mode='list') - if not args: # List existing macros - return sorted(k for k,v in self.shell.user_ns.iteritems() if\ - isinstance(v, Macro)) - if len(args) == 1: - raise UsageError( - "%macro insufficient args; usage '%macro name n1-n2 n3-4...") - name, codefrom = args[0], " ".join(args[1:]) - - #print 'rng',ranges # dbg - try: - lines = self.shell.find_user_code(codefrom, 'r' in opts) - except (ValueError, TypeError) as e: - print e.args[0] - return - macro = Macro(lines) - self.shell.define_macro(name, macro) - print 'Macro `%s` created. To execute, type its name (without quotes).' % name - print '=== Macro contents: ===' - print macro, - - -class AutoMagics(MagicFunctions): - """Magics that control various autoX behaviors.""" - - def __init__(self, shell): - super(ProfileMagics, self).__init__(shell) - # namespace for holding state we may need - self._magic_state = Bunch() - - def magic_automagic(self, parameter_s = ''): - """Make magic functions callable without having to type the initial %. - - Without argumentsl toggles on/off (when off, you must call it as - %automagic, of course). With arguments it sets the value, and you can - use any of (case insensitive): - - - on,1,True: to activate - - - off,0,False: to deactivate. - - Note that magic functions have lowest priority, so if there's a - variable whose name collides with that of a magic fn, automagic won't - work for that function (you get the variable instead). However, if you - delete the variable (del var), the previously shadowed magic function - becomes visible to automagic again.""" - - arg = parameter_s.lower() - if parameter_s in ('on','1','true'): - self.shell.automagic = True - elif parameter_s in ('off','0','false'): - self.shell.automagic = False - else: - self.shell.automagic = not self.shell.automagic - print '\n' + Magic.auto_status[self.shell.automagic] - - @skip_doctest - def magic_autocall(self, parameter_s = ''): - """Make functions callable without having to type parentheses. - - Usage: - - %autocall [mode] - - The mode can be one of: 0->Off, 1->Smart, 2->Full. If not given, the - value is toggled on and off (remembering the previous state). - - In more detail, these values mean: - - 0 -> fully disabled - - 1 -> active, but do not apply if there are no arguments on the line. - - In this mode, you get:: - - In [1]: callable - Out[1]: - - In [2]: callable 'hello' - ------> callable('hello') - Out[2]: False - - 2 -> Active always. Even if no arguments are present, the callable - object is called:: - - In [2]: float - ------> float() - Out[2]: 0.0 - - Note that even with autocall off, you can still use '/' at the start of - a line to treat the first argument on the command line as a function - and add parentheses to it:: - - In [8]: /str 43 - ------> str(43) - Out[8]: '43' - - # all-random (note for auto-testing) - """ - - if parameter_s: - arg = int(parameter_s) - else: - arg = 'toggle' - - if not arg in (0,1,2,'toggle'): - error('Valid modes: (0->Off, 1->Smart, 2->Full') - return - - if arg in (0,1,2): - self.shell.autocall = arg - else: # toggle - if self.shell.autocall: - self._magic_state.autocall_save = self.shell.autocall - self.shell.autocall = 0 - else: - try: - self.shell.autocall = self._magic_state.autocall_save - except AttributeError: - self.shell.autocall = self._magic_state.autocall_save = 1 - - print "Automatic calling is:",['OFF','Smart','Full'][self.shell.autocall] - - -class OSMagics(MagicFunctions): - """Magics to interact with the underlying OS (shell-type functionality). - """ - - @skip_doctest - def magic_alias(self, parameter_s = ''): - """Define an alias for a system command. - - '%alias alias_name cmd' defines 'alias_name' as an alias for 'cmd' - - Then, typing 'alias_name params' will execute the system command 'cmd - params' (from your underlying operating system). - - Aliases have lower precedence than magic functions and Python normal - variables, so if 'foo' is both a Python variable and an alias, the - alias can not be executed until 'del foo' removes the Python variable. - - You can use the %l specifier in an alias definition to represent the - whole line when the alias is called. For example:: - - In [2]: alias bracket echo "Input in brackets: <%l>" - In [3]: bracket hello world - Input in brackets: - - You can also define aliases with parameters using %s specifiers (one - per parameter):: - - In [1]: alias parts echo first %s second %s - In [2]: %parts A B - first A second B - In [3]: %parts A - Incorrect number of arguments: 2 expected. - parts is an alias to: 'echo first %s second %s' - - Note that %l and %s are mutually exclusive. You can only use one or - the other in your aliases. - - Aliases expand Python variables just like system calls using ! or !! - do: all expressions prefixed with '$' get expanded. For details of - the semantic rules, see PEP-215: - http://www.python.org/peps/pep-0215.html. This is the library used by - IPython for variable expansion. If you want to access a true shell - variable, an extra $ is necessary to prevent its expansion by - IPython:: - - In [6]: alias show echo - In [7]: PATH='A Python string' - In [8]: show $PATH - A Python string - In [9]: show $$PATH - /usr/local/lf9560/bin:/usr/local/intel/compiler70/ia32/bin:... - - You can use the alias facility to acess all of $PATH. See the %rehash - and %rehashx functions, which automatically create aliases for the - contents of your $PATH. - - If called with no parameters, %alias prints the current alias table.""" - - par = parameter_s.strip() - if not par: - stored = self.shell.db.get('stored_aliases', {} ) - aliases = sorted(self.shell.alias_manager.aliases) - # for k, v in stored: - # atab.append(k, v[0]) - - print "Total number of aliases:", len(aliases) - sys.stdout.flush() - return aliases - - # Now try to define a new one - try: - alias,cmd = par.split(None, 1) - except: - print oinspect.getdoc(self.magic_alias) - else: - self.shell.alias_manager.soft_define_alias(alias, cmd) - # end magic_alias - - def magic_unalias(self, parameter_s = ''): - """Remove an alias""" - - aname = parameter_s.strip() - self.shell.alias_manager.undefine_alias(aname) - stored = self.shell.db.get('stored_aliases', {} ) - if aname in stored: - print "Removing %stored alias",aname - del stored[aname] - self.shell.db['stored_aliases'] = stored - - def magic_rehashx(self, parameter_s = ''): - """Update the alias table with all executable files in $PATH. - - This version explicitly checks that every entry in $PATH is a file - with execute access (os.X_OK), so it is much slower than %rehash. - - Under Windows, it checks executability as a match against a - '|'-separated string of extensions, stored in the IPython config - variable win_exec_ext. This defaults to 'exe|com|bat'. - - This function also resets the root module cache of module completer, - used on slow filesystems. - """ - from IPython.core.alias import InvalidAliasError - - # for the benefit of module completer in ipy_completers.py - del self.shell.db['rootmodules'] - - path = [os.path.abspath(os.path.expanduser(p)) for p in - os.environ.get('PATH','').split(os.pathsep)] - path = filter(os.path.isdir,path) - - syscmdlist = [] - # Now define isexec in a cross platform manner. - if os.name == 'posix': - isexec = lambda fname:os.path.isfile(fname) and \ - os.access(fname,os.X_OK) - else: - try: - winext = os.environ['pathext'].replace(';','|').replace('.','') - except KeyError: - winext = 'exe|com|bat|py' - if 'py' not in winext: - winext += '|py' - execre = re.compile(r'(.*)\.(%s)$' % winext,re.IGNORECASE) - isexec = lambda fname:os.path.isfile(fname) and execre.match(fname) - savedir = os.getcwdu() - - # Now walk the paths looking for executables to alias. - try: - # write the whole loop for posix/Windows so we don't have an if in - # the innermost part - if os.name == 'posix': - for pdir in path: - os.chdir(pdir) - for ff in os.listdir(pdir): - if isexec(ff): - try: - # Removes dots from the name since ipython - # will assume names with dots to be python. - self.shell.alias_manager.define_alias( - ff.replace('.',''), ff) - except InvalidAliasError: - pass - else: - syscmdlist.append(ff) - else: - no_alias = self.shell.alias_manager.no_alias - for pdir in path: - os.chdir(pdir) - for ff in os.listdir(pdir): - base, ext = os.path.splitext(ff) - if isexec(ff) and base.lower() not in no_alias: - if ext.lower() == '.exe': - ff = base - try: - # Removes dots from the name since ipython - # will assume names with dots to be python. - self.shell.alias_manager.define_alias( - base.lower().replace('.',''), ff) - except InvalidAliasError: - pass - syscmdlist.append(ff) - self.shell.db['syscmdlist'] = syscmdlist - finally: - os.chdir(savedir) - - @skip_doctest - def magic_pwd(self, parameter_s = ''): - """Return the current working directory path. - - Examples - -------- - :: - - In [9]: pwd - Out[9]: '/home/tsuser/sprint/ipython' - """ - return os.getcwdu() - - @skip_doctest - def magic_cd(self, parameter_s=''): - """Change the current working directory. - - This command automatically maintains an internal list of directories - you visit during your IPython session, in the variable _dh. The - command %dhist shows this history nicely formatted. You can also - do 'cd -' to see directory history conveniently. - - Usage: - - cd 'dir': changes to directory 'dir'. - - cd -: changes to the last visited directory. - - cd -: changes to the n-th directory in the directory history. - - cd --foo: change to directory that matches 'foo' in history - - cd -b : jump to a bookmark set by %bookmark - (note: cd is enough if there is no - directory , but a bookmark with the name exists.) - 'cd -b ' allows you to tab-complete bookmark names. - - Options: - - -q: quiet. Do not print the working directory after the cd command is - executed. By default IPython's cd command does print this directory, - since the default prompts do not display path information. - - Note that !cd doesn't work for this purpose because the shell where - !command runs is immediately discarded after executing 'command'. - - Examples - -------- - :: - - In [10]: cd parent/child - /home/tsuser/parent/child - """ - - parameter_s = parameter_s.strip() - #bkms = self.shell.persist.get("bookmarks",{}) - - oldcwd = os.getcwdu() - numcd = re.match(r'(-)(\d+)$',parameter_s) - # jump in directory history by number - if numcd: - nn = int(numcd.group(2)) - try: - ps = self.shell.user_ns['_dh'][nn] - except IndexError: - print 'The requested directory does not exist in history.' - return - else: - opts = {} - elif parameter_s.startswith('--'): - ps = None - fallback = None - pat = parameter_s[2:] - dh = self.shell.user_ns['_dh'] - # first search only by basename (last component) - for ent in reversed(dh): - if pat in os.path.basename(ent) and os.path.isdir(ent): - ps = ent - break - - if fallback is None and pat in ent and os.path.isdir(ent): - fallback = ent - - # if we have no last part match, pick the first full path match - if ps is None: - ps = fallback - - if ps is None: - print "No matching entry in directory history" - return - else: - opts = {} - - - else: - #turn all non-space-escaping backslashes to slashes, - # for c:\windows\directory\names\ - parameter_s = re.sub(r'\\(?! )','/', parameter_s) - opts,ps = self.parse_options(parameter_s,'qb',mode='string') - # jump to previous - if ps == '-': - try: - ps = self.shell.user_ns['_dh'][-2] - except IndexError: - raise UsageError('%cd -: No previous directory to change to.') - # jump to bookmark if needed - else: - if not os.path.isdir(ps) or opts.has_key('b'): - bkms = self.shell.db.get('bookmarks', {}) - - if bkms.has_key(ps): - target = bkms[ps] - print '(bookmark:%s) -> %s' % (ps,target) - ps = target - else: - if opts.has_key('b'): - raise UsageError("Bookmark '%s' not found. " - "Use '%%bookmark -l' to see your bookmarks." % ps) - - # strip extra quotes on Windows, because os.chdir doesn't like them - ps = unquote_filename(ps) - # at this point ps should point to the target dir - if ps: - try: - os.chdir(os.path.expanduser(ps)) - if hasattr(self.shell, 'term_title') and self.shell.term_title: - set_term_title('IPython: ' + abbrev_cwd()) - except OSError: - print sys.exc_info()[1] - else: - cwd = os.getcwdu() - dhist = self.shell.user_ns['_dh'] - if oldcwd != cwd: - dhist.append(cwd) - self.shell.db['dhist'] = compress_dhist(dhist)[-100:] - - else: - os.chdir(self.shell.home_dir) - if hasattr(self.shell, 'term_title') and self.shell.term_title: - set_term_title('IPython: ' + '~') - cwd = os.getcwdu() - dhist = self.shell.user_ns['_dh'] - - if oldcwd != cwd: - dhist.append(cwd) - self.shell.db['dhist'] = compress_dhist(dhist)[-100:] - if not 'q' in opts and self.shell.user_ns['_dh']: - print self.shell.user_ns['_dh'][-1] - - - def magic_env(self, parameter_s=''): - """List environment variables.""" - - return dict(os.environ) - - def magic_pushd(self, parameter_s=''): - """Place the current dir on stack and change directory. - - Usage:\\ - %pushd ['dirname'] - """ - - dir_s = self.shell.dir_stack - tgt = os.path.expanduser(unquote_filename(parameter_s)) - cwd = os.getcwdu().replace(self.shell.home_dir,'~') - if tgt: - self.magic_cd(parameter_s) - dir_s.insert(0,cwd) - return self.shell.magic('dirs') - - def magic_popd(self, parameter_s=''): - """Change to directory popped off the top of the stack. - """ - if not self.shell.dir_stack: - raise UsageError("%popd on empty stack") - top = self.shell.dir_stack.pop(0) - self.magic_cd(top) - print "popd ->",top - - def magic_dirs(self, parameter_s=''): - """Return the current directory stack.""" - - return self.shell.dir_stack - - def magic_dhist(self, parameter_s=''): - """Print your history of visited directories. - - %dhist -> print full history\\ - %dhist n -> print last n entries only\\ - %dhist n1 n2 -> print entries between n1 and n2 (n1 not included)\\ - - This history is automatically maintained by the %cd command, and - always available as the global list variable _dh. You can use %cd - - to go to directory number . - - Note that most of time, you should view directory history by entering - cd -. - - """ - - dh = self.shell.user_ns['_dh'] - if parameter_s: - try: - args = map(int,parameter_s.split()) - except: - self.arg_err(Magic.magic_dhist) - return - if len(args) == 1: - ini,fin = max(len(dh)-(args[0]),0),len(dh) - elif len(args) == 2: - ini,fin = args - else: - self.arg_err(Magic.magic_dhist) - return - else: - ini,fin = 0,len(dh) - nlprint(dh, - header = 'Directory history (kept in _dh)', - start=ini,stop=fin) - - @skip_doctest - def magic_sc(self, parameter_s=''): - """Shell capture - execute a shell command and capture its output. - - DEPRECATED. Suboptimal, retained for backwards compatibility. - - You should use the form 'var = !command' instead. Example: - - "%sc -l myfiles = ls ~" should now be written as - - "myfiles = !ls ~" - - myfiles.s, myfiles.l and myfiles.n still apply as documented - below. - - -- - %sc [options] varname=command - - IPython will run the given command using commands.getoutput(), and - will then update the user's interactive namespace with a variable - called varname, containing the value of the call. Your command can - contain shell wildcards, pipes, etc. - - The '=' sign in the syntax is mandatory, and the variable name you - supply must follow Python's standard conventions for valid names. - - (A special format without variable name exists for internal use) - - Options: - - -l: list output. Split the output on newlines into a list before - assigning it to the given variable. By default the output is stored - as a single string. - - -v: verbose. Print the contents of the variable. - - In most cases you should not need to split as a list, because the - returned value is a special type of string which can automatically - provide its contents either as a list (split on newlines) or as a - space-separated string. These are convenient, respectively, either - for sequential processing or to be passed to a shell command. - - For example:: - - # Capture into variable a - In [1]: sc a=ls *py - - # a is a string with embedded newlines - In [2]: a - Out[2]: 'setup.py\\nwin32_manual_post_install.py' - - # which can be seen as a list: - In [3]: a.l - Out[3]: ['setup.py', 'win32_manual_post_install.py'] - - # or as a whitespace-separated string: - In [4]: a.s - Out[4]: 'setup.py win32_manual_post_install.py' - - # a.s is useful to pass as a single command line: - In [5]: !wc -l $a.s - 146 setup.py - 130 win32_manual_post_install.py - 276 total - - # while the list form is useful to loop over: - In [6]: for f in a.l: - ...: !wc -l $f - ...: - 146 setup.py - 130 win32_manual_post_install.py - - Similarly, the lists returned by the -l option are also special, in - the sense that you can equally invoke the .s attribute on them to - automatically get a whitespace-separated string from their contents:: - - In [7]: sc -l b=ls *py - - In [8]: b - Out[8]: ['setup.py', 'win32_manual_post_install.py'] - - In [9]: b.s - Out[9]: 'setup.py win32_manual_post_install.py' - - In summary, both the lists and strings used for output capture have - the following special attributes:: - - .l (or .list) : value as list. - .n (or .nlstr): value as newline-separated string. - .s (or .spstr): value as space-separated string. - """ - - opts,args = self.parse_options(parameter_s,'lv') - # Try to get a variable name and command to run - try: - # the variable name must be obtained from the parse_options - # output, which uses shlex.split to strip options out. - var,_ = args.split('=',1) - var = var.strip() - # But the command has to be extracted from the original input - # parameter_s, not on what parse_options returns, to avoid the - # quote stripping which shlex.split performs on it. - _,cmd = parameter_s.split('=',1) - except ValueError: - var,cmd = '','' - # If all looks ok, proceed - split = 'l' in opts - out = self.shell.getoutput(cmd, split=split) - if opts.has_key('v'): - print '%s ==\n%s' % (var,pformat(out)) - if var: - self.shell.user_ns.update({var:out}) - else: - return out - - def magic_sx(self, parameter_s=''): - """Shell execute - run a shell command and capture its output. - - %sx command - - IPython will run the given command using commands.getoutput(), and - return the result formatted as a list (split on '\\n'). Since the - output is _returned_, it will be stored in ipython's regular output - cache Out[N] and in the '_N' automatic variables. - - Notes: - - 1) If an input line begins with '!!', then %sx is automatically - invoked. That is, while:: - - !ls - - causes ipython to simply issue system('ls'), typing:: - - !!ls - - is a shorthand equivalent to:: - - %sx ls - - 2) %sx differs from %sc in that %sx automatically splits into a list, - like '%sc -l'. The reason for this is to make it as easy as possible - to process line-oriented shell output via further python commands. - %sc is meant to provide much finer control, but requires more - typing. - - 3) Just like %sc -l, this is a list with special attributes: - :: - - .l (or .list) : value as list. - .n (or .nlstr): value as newline-separated string. - .s (or .spstr): value as whitespace-separated string. - - This is very useful when trying to use such lists as arguments to - system commands.""" - - if parameter_s: - return self.shell.getoutput(parameter_s) - - - def magic_bookmark(self, parameter_s=''): - """Manage IPython's bookmark system. - - %bookmark - set bookmark to current dir - %bookmark

- set bookmark to - %bookmark -l - list all bookmarks - %bookmark -d - remove bookmark - %bookmark -r - remove all bookmarks - - You can later on access a bookmarked folder with:: - - %cd -b - - or simply '%cd ' if there is no directory called AND - there is such a bookmark defined. - - Your bookmarks persist through IPython sessions, but they are - associated with each profile.""" - - opts,args = self.parse_options(parameter_s,'drl',mode='list') - if len(args) > 2: - raise UsageError("%bookmark: too many arguments") - - bkms = self.shell.db.get('bookmarks',{}) - - if opts.has_key('d'): - try: - todel = args[0] - except IndexError: - raise UsageError( - "%bookmark -d: must provide a bookmark to delete") - else: - try: - del bkms[todel] - except KeyError: - raise UsageError( - "%%bookmark -d: Can't delete bookmark '%s'" % todel) - - elif opts.has_key('r'): - bkms = {} - elif opts.has_key('l'): - bks = bkms.keys() - bks.sort() - if bks: - size = max(map(len,bks)) - else: - size = 0 - fmt = '%-'+str(size)+'s -> %s' - print 'Current bookmarks:' - for bk in bks: - print fmt % (bk,bkms[bk]) - else: - if not args: - raise UsageError("%bookmark: You must specify the bookmark name") - elif len(args)==1: - bkms[args[0]] = os.getcwdu() - elif len(args)==2: - bkms[args[0]] = args[1] - self.shell.db['bookmarks'] = bkms - - - def magic_pycat(self, parameter_s=''): - """Show a syntax-highlighted file through a pager. - - This magic is similar to the cat utility, but it will assume the file - to be Python source and will show it with syntax highlighting. - - This magic command can either take a local filename, an url, - an history range (see %history) or a macro as argument :: - - %pycat myscript.py - %pycat 7-27 - %pycat myMacro - %pycat http://www.example.com/myscript.py - """ - - try : - cont = self.shell.find_user_code(parameter_s) - except ValueError, IOError: - print "Error: no such file, variable, URL, history range or macro" - return - - page.page(self.shell.pycolorize(cont)) - - -class LoggingMagics(MagicFunctions): - """Magics related to all logging machinery.""" - def magic_logstart(self,parameter_s=''): - """Start logging anywhere in a session. - - %logstart [-o|-r|-t] [log_name [log_mode]] - - If no name is given, it defaults to a file named 'ipython_log.py' in your - current directory, in 'rotate' mode (see below). - - '%logstart name' saves to file 'name' in 'backup' mode. It saves your - history up to that point and then continues logging. - - %logstart takes a second optional parameter: logging mode. This can be one - of (note that the modes are given unquoted):\\ - append: well, that says it.\\ - backup: rename (if exists) to name~ and start name.\\ - global: single logfile in your home dir, appended to.\\ - over : overwrite existing log.\\ - rotate: create rotating logs name.1~, name.2~, etc. - - Options: - - -o: log also IPython's output. In this mode, all commands which - generate an Out[NN] prompt are recorded to the logfile, right after - their corresponding input line. The output lines are always - prepended with a '#[Out]# ' marker, so that the log remains valid - Python code. - - Since this marker is always the same, filtering only the output from - a log is very easy, using for example a simple awk call:: - - awk -F'#\\[Out\\]# ' '{if($2) {print $2}}' ipython_log.py - - -r: log 'raw' input. Normally, IPython's logs contain the processed - input, so that user lines are logged in their final form, converted - into valid Python. For example, %Exit is logged as - _ip.magic("Exit"). If the -r flag is given, all input is logged - exactly as typed, with no transformations applied. - - -t: put timestamps before each input line logged (these are put in - comments).""" - - opts,par = self.parse_options(parameter_s,'ort') - log_output = 'o' in opts - log_raw_input = 'r' in opts - timestamp = 't' in opts - - logger = self.shell.logger - - # if no args are given, the defaults set in the logger constructor by - # ipython remain valid - if par: - try: - logfname,logmode = par.split() - except: - logfname = par - logmode = 'backup' - else: - logfname = logger.logfname - logmode = logger.logmode - # put logfname into rc struct as if it had been called on the command - # line, so it ends up saved in the log header Save it in case we need - # to restore it... - old_logfile = self.shell.logfile - if logfname: - logfname = os.path.expanduser(logfname) - self.shell.logfile = logfname - - loghead = '# IPython log file\n\n' - try: - started = logger.logstart(logfname,loghead,logmode, - log_output,timestamp,log_raw_input) - except: - self.shell.logfile = old_logfile - warn("Couldn't start log: %s" % sys.exc_info()[1]) - else: - # log input history up to this point, optionally interleaving - # output if requested - - if timestamp: - # disable timestamping for the previous history, since we've - # lost those already (no time machine here). - logger.timestamp = False - - if log_raw_input: - input_hist = self.shell.history_manager.input_hist_raw - else: - input_hist = self.shell.history_manager.input_hist_parsed - - if log_output: - log_write = logger.log_write - output_hist = self.shell.history_manager.output_hist - for n in range(1,len(input_hist)-1): - log_write(input_hist[n].rstrip() + '\n') - if n in output_hist: - log_write(repr(output_hist[n]),'output') - else: - logger.log_write('\n'.join(input_hist[1:])) - logger.log_write('\n') - if timestamp: - # re-enable timestamping - logger.timestamp = True - - print ('Activating auto-logging. ' - 'Current session state plus future input saved.') - logger.logstate() - - def magic_logstop(self,parameter_s=''): - """Fully stop logging and close log file. - - In order to start logging again, a new %logstart call needs to be made, - possibly (though not necessarily) with a new filename, mode and other - options.""" - self.logger.logstop() - - def magic_logoff(self,parameter_s=''): - """Temporarily stop logging. - - You must have previously started logging.""" - self.shell.logger.switch_log(0) - - def magic_logon(self,parameter_s=''): - """Restart logging. - - This function is for restarting logging which you've temporarily - stopped with %logoff. For starting logging for the first time, you - must use the %logstart function, which allows you to specify an - optional log filename.""" - - self.shell.logger.switch_log(1) - - def magic_logstate(self,parameter_s=''): - """Print the status of the logging system.""" - - self.shell.logger.logstate() - -class ExtensionsMagics(MagicFunctions): - """Magics to manage the IPython extensions system.""" - def magic_install_ext(self, parameter_s): - """Download and install an extension from a URL, e.g.:: - - %install_ext https://bitbucket.org/birkenfeld/ipython-physics/raw/d1310a2ab15d/physics.py - - The URL should point to an importable Python module - either a .py file - or a .zip file. - - Parameters: - - -n filename : Specify a name for the file, rather than taking it from - the URL. - """ - opts, args = self.parse_options(parameter_s, 'n:') - try: - filename = self.shell.extension_manager.install_extension(args, - opts.get('n')) - except ValueError as e: - print e - return - - filename = os.path.basename(filename) - print "Installed %s. To use it, type:" % filename - print " %%load_ext %s" % os.path.splitext(filename)[0] - - - def magic_load_ext(self, module_str): - """Load an IPython extension by its module name.""" - return self.shell.extension_manager.load_extension(module_str) - - def magic_unload_ext(self, module_str): - """Unload an IPython extension by its module name.""" - self.shell.extension_manager.unload_extension(module_str) - - def magic_reload_ext(self, module_str): - """Reload an IPython extension by its module name.""" - self.shell.extension_manager.reload_extension(module_str) - - -class DeprecatedMagics(MagicFunctions): - """Magics slated for later removal.""" - def magic_install_profiles(self, s): - """%install_profiles has been deprecated.""" - print '\n'.join([ - "%install_profiles has been deprecated.", - "Use `ipython profile list` to view available profiles.", - "Requesting a profile with `ipython profile create `", - "or `ipython --profile=` will start with the bundled", - "profile of that name if it exists." - ]) - - def magic_install_default_config(self, s): - """%install_default_config has been deprecated.""" - print '\n'.join([ - "%install_default_config has been deprecated.", - "Use `ipython profile create ` to initialize a profile", - "with the default config files.", - "Add `--reset` to overwrite already existing config files with defaults." - ]) - - -class PylabMagics(MagicFunctions): - """Magics related to matplotlib's pylab support""" - - @skip_doctest - def magic_pylab(self, s): - """Load numpy and matplotlib to work interactively. - - %pylab [GUINAME] - - This function lets you activate pylab (matplotlib, numpy and - interactive support) at any point during an IPython session. - - It will import at the top level numpy as np, pyplot as plt, matplotlib, - pylab and mlab, as well as all names from numpy and pylab. - - If you are using the inline matplotlib backend for embedded figures, - you can adjust its behavior via the %config magic:: - - # enable SVG figures, necessary for SVG+XHTML export in the qtconsole - In [1]: %config InlineBackend.figure_format = 'svg' - - # change the behavior of closing all figures at the end of each - # execution (cell), or allowing reuse of active figures across - # cells: - In [2]: %config InlineBackend.close_figures = False - - Parameters - ---------- - guiname : optional - One of the valid arguments to the %gui magic ('qt', 'wx', 'gtk', - 'osx' or 'tk'). If given, the corresponding Matplotlib backend is - used, otherwise matplotlib's default (which you can override in your - matplotlib config file) is used. - - Examples - -------- - In this case, where the MPL default is TkAgg:: - - In [2]: %pylab - - Welcome to pylab, a matplotlib-based Python environment. - Backend in use: TkAgg - For more information, type 'help(pylab)'. - - But you can explicitly request a different backend:: - - In [3]: %pylab qt - - Welcome to pylab, a matplotlib-based Python environment. - Backend in use: Qt4Agg - For more information, type 'help(pylab)'. - """ - - if Application.initialized(): - app = Application.instance() - try: - import_all_status = app.pylab_import_all - except AttributeError: - import_all_status = True - else: - import_all_status = True - - self.shell.enable_pylab(s, import_all=import_all_status) diff --git a/IPython/core/magic_functions.py b/IPython/core/magic_functions.py new file mode 100644 index 00000000000..5f9da887de3 --- /dev/null +++ b/IPython/core/magic_functions.py @@ -0,0 +1,3639 @@ +"""Magic functions for InteractiveShell. +""" + +#----------------------------------------------------------------------------- +# Copyright (C) 2001 Janko Hauser and +# Copyright (C) 2001 Fernando Perez +# Copyright (C) 2008 The IPython Development Team + +# Distributed under the terms of the BSD License. The full license is in +# the file COPYING, distributed as part of this software. +#----------------------------------------------------------------------------- +#----------------------------------------------------------------------------- +# Imports +#----------------------------------------------------------------------------- + +import __builtin__ as builtin_mod +import bdb +import gc +import inspect +import io +import json +import os +import re +import sys +import time +from StringIO import StringIO +from pprint import pformat +from urllib2 import urlopen + +# cProfile was added in Python2.5 +try: + import cProfile as profile + import pstats +except ImportError: + # profile isn't bundled by default in Debian for license reasons + try: + import profile, pstats + except ImportError: + profile = pstats = None + +from IPython.config.application import Application +from IPython.core import debugger, oinspect +from IPython.core import magic_arguments, page +from IPython.core.error import UsageError, StdinNotImplementedError, TryNext +from IPython.core.macro import Macro +from IPython.core.magic import (Bunch, Magics, MacroToEdit, compress_dhist, + on_off, needs_local_scope, + register_magics, line_magic, cell_magic) +from IPython.core.prefilter import ESC_MAGIC +from IPython.testing.skipdoctest import skip_doctest +from IPython.utils import openpy +from IPython.utils import py3compat +from IPython.utils.encoding import DEFAULT_ENCODING +from IPython.utils.io import file_read, nlprint +from IPython.utils.ipstruct import Struct +from IPython.utils.module_paths import find_mod +from IPython.utils.path import get_py_filename, unquote_filename +from IPython.utils.process import abbrev_cwd +from IPython.utils.terminal import set_term_title +from IPython.utils.text import format_screen +from IPython.utils.timing import clock, clock2 +from IPython.utils.warn import warn, error + + +#----------------------------------------------------------------------------- +# Magic implementation classes +#----------------------------------------------------------------------------- +class UserMagics(Magics): + """Placeholder for user-defined magics to be added at runtime. + + All magics are eventually merged into a single namespace at runtime, but we + use this class to isolate the magics defined dynamically by the user into + their own class. + """ + + +class BasicMagics(Magics): + """Magics that provide central IPython functionality. + + These are various magics that don't fit into specific categories but that + are all part of the base 'IPython experience'.""" + + def magic_lsmagic(self, parameter_s = ''): + """List currently available magic functions.""" + mesc = ESC_MAGIC + print 'Available magic functions:\n'+mesc+\ + (' '+mesc).join(self.lsmagic()) + print '\n' + Magic.auto_status[self.shell.automagic] + return None + + def magic_magic(self, parameter_s = ''): + """Print information about the magic function system. + + Supported formats: -latex, -brief, -rest + """ + + mode = '' + try: + if parameter_s.split()[0] == '-latex': + mode = 'latex' + if parameter_s.split()[0] == '-brief': + mode = 'brief' + if parameter_s.split()[0] == '-rest': + mode = 'rest' + rest_docs = [] + except: + pass + + magic_docs = [] + for fname in self.lsmagic(): + mname = 'magic_' + fname + for space in (Magic, self, self.__class__): + try: + fn = space.__dict__[mname] + except KeyError: + pass + else: + break + if mode == 'brief': + # only first line + if fn.__doc__: + fndoc = fn.__doc__.split('\n',1)[0] + else: + fndoc = 'No documentation' + else: + if fn.__doc__: + fndoc = fn.__doc__.rstrip() + else: + fndoc = 'No documentation' + + + if mode == 'rest': + rest_docs.append('**%s%s**::\n\n\t%s\n\n' %(ESC_MAGIC, + fname,fndoc)) + + else: + magic_docs.append('%s%s:\n\t%s\n' %(ESC_MAGIC, + fname,fndoc)) + + magic_docs = ''.join(magic_docs) + + if mode == 'rest': + return "".join(rest_docs) + + if mode == 'latex': + print self.format_latex(magic_docs) + return + else: + magic_docs = format_screen(magic_docs) + if mode == 'brief': + return magic_docs + + outmsg = """ +IPython's 'magic' functions +=========================== + +The magic function system provides a series of functions which allow you to +control the behavior of IPython itself, plus a lot of system-type +features. All these functions are prefixed with a % character, but parameters +are given without parentheses or quotes. + +NOTE: If you have 'automagic' enabled (via the command line option or with the +%automagic function), you don't need to type in the % explicitly. By default, +IPython ships with automagic on, so you should only rarely need the % escape. + +Example: typing '%cd mydir' (without the quotes) changes you working directory +to 'mydir', if it exists. + +For a list of the available magic functions, use %lsmagic. For a description +of any of them, type %magic_name?, e.g. '%cd?'. + +Currently the magic system has the following functions:\n""" + + mesc = ESC_MAGIC + outmsg = ("%s\n%s\n\nSummary of magic functions (from %slsmagic):" + "\n\n%s%s\n\n%s" % (outmsg, + magic_docs,mesc,mesc, + (' '+mesc).join(self.lsmagic()), + Magic.auto_status[self.shell.automagic] ) ) + page.page(outmsg) + + + def magic_page(self, parameter_s=''): + """Pretty print the object and display it through a pager. + + %page [options] OBJECT + + If no object is given, use _ (last output). + + Options: + + -r: page str(object), don't pretty-print it.""" + + # After a function contributed by Olivier Aubert, slightly modified. + + # Process options/args + opts,args = self.parse_options(parameter_s,'r') + raw = 'r' in opts + + oname = args and args or '_' + info = self._ofind(oname) + if info['found']: + txt = (raw and str or pformat)( info['obj'] ) + page.page(txt) + else: + print 'Object `%s` not found' % oname + + def magic_profile(self, parameter_s=''): + """Print your currently active IPython profile.""" + from IPython.core.application import BaseIPythonApplication + if BaseIPythonApplication.initialized(): + print BaseIPythonApplication.instance().profile + else: + error("profile is an application-level value, but you don't appear to be in an IPython application") + + def magic_pprint(self, parameter_s=''): + """Toggle pretty printing on/off.""" + ptformatter = self.shell.display_formatter.formatters['text/plain'] + ptformatter.pprint = bool(1 - ptformatter.pprint) + print 'Pretty printing has been turned', \ + ['OFF','ON'][ptformatter.pprint] + + def magic_colors(self,parameter_s = ''): + """Switch color scheme for prompts, info system and exception handlers. + + Currently implemented schemes: NoColor, Linux, LightBG. + + Color scheme names are not case-sensitive. + + Examples + -------- + To get a plain black and white terminal:: + + %colors nocolor + """ + + def color_switch_err(name): + warn('Error changing %s color schemes.\n%s' % + (name,sys.exc_info()[1])) + + + new_scheme = parameter_s.strip() + if not new_scheme: + raise UsageError( + "%colors: you must specify a color scheme. See '%colors?'") + return + # local shortcut + shell = self.shell + + import IPython.utils.rlineimpl as readline + + if not shell.colors_force and \ + not readline.have_readline and sys.platform == "win32": + msg = """\ +Proper color support under MS Windows requires the pyreadline library. +You can find it at: +http://ipython.org/pyreadline.html +Gary's readline needs the ctypes module, from: +http://starship.python.net/crew/theller/ctypes +(Note that ctypes is already part of Python versions 2.5 and newer). + +Defaulting color scheme to 'NoColor'""" + new_scheme = 'NoColor' + warn(msg) + + # readline option is 0 + if not shell.colors_force and not shell.has_readline: + new_scheme = 'NoColor' + + # Set prompt colors + try: + shell.prompt_manager.color_scheme = new_scheme + except: + color_switch_err('prompt') + else: + shell.colors = \ + shell.prompt_manager.color_scheme_table.active_scheme_name + # Set exception colors + try: + shell.InteractiveTB.set_colors(scheme = new_scheme) + shell.SyntaxTB.set_colors(scheme = new_scheme) + except: + color_switch_err('exception') + + # Set info (for 'object?') colors + if shell.color_info: + try: + shell.inspector.set_active_scheme(new_scheme) + except: + color_switch_err('object inspector') + else: + shell.inspector.set_active_scheme('NoColor') + + def magic_xmode(self,parameter_s = ''): + """Switch modes for the exception handlers. + + Valid modes: Plain, Context and Verbose. + + If called without arguments, acts as a toggle.""" + + def xmode_switch_err(name): + warn('Error changing %s exception modes.\n%s' % + (name,sys.exc_info()[1])) + + shell = self.shell + new_mode = parameter_s.strip().capitalize() + try: + shell.InteractiveTB.set_mode(mode=new_mode) + print 'Exception reporting mode:',shell.InteractiveTB.mode + except: + xmode_switch_err('user') + + def magic_quickref(self,arg): + """ Show a quick reference sheet """ + import IPython.core.usage + qr = IPython.core.usage.quick_reference + self.magic_magic('-brief') + page.page(qr) + + def magic_doctest_mode(self,parameter_s=''): + """Toggle doctest mode on and off. + + This mode is intended to make IPython behave as much as possible like a + plain Python shell, from the perspective of how its prompts, exceptions + and output look. This makes it easy to copy and paste parts of a + session into doctests. It does so by: + + - Changing the prompts to the classic ``>>>`` ones. + - Changing the exception reporting mode to 'Plain'. + - Disabling pretty-printing of output. + + Note that IPython also supports the pasting of code snippets that have + leading '>>>' and '...' prompts in them. This means that you can paste + doctests from files or docstrings (even if they have leading + whitespace), and the code will execute correctly. You can then use + '%history -t' to see the translated history; this will give you the + input after removal of all the leading prompts and whitespace, which + can be pasted back into an editor. + + With these features, you can switch into this mode easily whenever you + need to do testing and changes to doctests, without having to leave + your existing IPython session. + """ + + # Shorthands + shell = self.shell + pm = shell.prompt_manager + meta = shell.meta + disp_formatter = self.shell.display_formatter + ptformatter = disp_formatter.formatters['text/plain'] + # dstore is a data store kept in the instance metadata bag to track any + # changes we make, so we can undo them later. + dstore = meta.setdefault('doctest_mode',Struct()) + save_dstore = dstore.setdefault + + # save a few values we'll need to recover later + mode = save_dstore('mode',False) + save_dstore('rc_pprint',ptformatter.pprint) + save_dstore('xmode',shell.InteractiveTB.mode) + save_dstore('rc_separate_out',shell.separate_out) + save_dstore('rc_separate_out2',shell.separate_out2) + save_dstore('rc_prompts_pad_left',pm.justify) + save_dstore('rc_separate_in',shell.separate_in) + save_dstore('rc_plain_text_only',disp_formatter.plain_text_only) + save_dstore('prompt_templates',(pm.in_template, pm.in2_template, pm.out_template)) + + if mode == False: + # turn on + pm.in_template = '>>> ' + pm.in2_template = '... ' + pm.out_template = '' + + # Prompt separators like plain python + shell.separate_in = '' + shell.separate_out = '' + shell.separate_out2 = '' + + pm.justify = False + + ptformatter.pprint = False + disp_formatter.plain_text_only = True + + shell.magic('xmode Plain') + else: + # turn off + pm.in_template, pm.in2_template, pm.out_template = dstore.prompt_templates + + shell.separate_in = dstore.rc_separate_in + + shell.separate_out = dstore.rc_separate_out + shell.separate_out2 = dstore.rc_separate_out2 + + pm.justify = dstore.rc_prompts_pad_left + + ptformatter.pprint = dstore.rc_pprint + disp_formatter.plain_text_only = dstore.rc_plain_text_only + + shell.magic('xmode ' + dstore.xmode) + + # Store new mode and inform + dstore.mode = bool(1-int(mode)) + mode_label = ['OFF','ON'][dstore.mode] + print 'Doctest mode is:', mode_label + + def magic_gui(self, parameter_s=''): + """Enable or disable IPython GUI event loop integration. + + %gui [GUINAME] + + This magic replaces IPython's threaded shells that were activated + using the (pylab/wthread/etc.) command line flags. GUI toolkits + can now be enabled at runtime and keyboard + interrupts should work without any problems. The following toolkits + are supported: wxPython, PyQt4, PyGTK, Tk and Cocoa (OSX):: + + %gui wx # enable wxPython event loop integration + %gui qt4|qt # enable PyQt4 event loop integration + %gui gtk # enable PyGTK event loop integration + %gui gtk3 # enable Gtk3 event loop integration + %gui tk # enable Tk event loop integration + %gui OSX # enable Cocoa event loop integration + # (requires %matplotlib 1.1) + %gui # disable all event loop integration + + WARNING: after any of these has been called you can simply create + an application object, but DO NOT start the event loop yourself, as + we have already handled that. + """ + opts, arg = self.parse_options(parameter_s, '') + if arg=='': arg = None + try: + return self.enable_gui(arg) + except Exception as e: + # print simple error message, rather than traceback if we can't + # hook up the GUI + error(str(e)) + + @skip_doctest + def magic_precision(self, s=''): + """Set floating point precision for pretty printing. + + Can set either integer precision or a format string. + + If numpy has been imported and precision is an int, + numpy display precision will also be set, via ``numpy.set_printoptions``. + + If no argument is given, defaults will be restored. + + Examples + -------- + :: + + In [1]: from math import pi + + In [2]: %precision 3 + Out[2]: u'%.3f' + + In [3]: pi + Out[3]: 3.142 + + In [4]: %precision %i + Out[4]: u'%i' + + In [5]: pi + Out[5]: 3 + + In [6]: %precision %e + Out[6]: u'%e' + + In [7]: pi**10 + Out[7]: 9.364805e+04 + + In [8]: %precision + Out[8]: u'%r' + + In [9]: pi**10 + Out[9]: 93648.047476082982 + """ + ptformatter = self.shell.display_formatter.formatters['text/plain'] + ptformatter.float_precision = s + return ptformatter.float_format + + @magic_arguments.magic_arguments() + @magic_arguments.argument( + '-e', '--export', action='store_true', default=False, + help='Export IPython history as a notebook. The filename argument ' + 'is used to specify the notebook name and format. For example ' + 'a filename of notebook.ipynb will result in a notebook name ' + 'of "notebook" and a format of "xml". Likewise using a ".json" ' + 'or ".py" file extension will write the notebook in the json ' + 'or py formats.' + ) + @magic_arguments.argument( + '-f', '--format', + help='Convert an existing IPython notebook to a new format. This option ' + 'specifies the new format and can have the values: xml, json, py. ' + 'The target filename is chosen automatically based on the new ' + 'format. The filename argument gives the name of the source file.' + ) + @magic_arguments.argument( + 'filename', type=unicode, + help='Notebook name or filename' + ) + def magic_notebook(self, s): + """Export and convert IPython notebooks. + + This function can export the current IPython history to a notebook file + or can convert an existing notebook file into a different format. For + example, to export the history to "foo.ipynb" do "%notebook -e foo.ipynb". + To export the history to "foo.py" do "%notebook -e foo.py". To convert + "foo.ipynb" to "foo.json" do "%notebook -f json foo.ipynb". Possible + formats include (json/ipynb, py). + """ + args = magic_arguments.parse_argstring(self.magic_notebook, s) + + from IPython.nbformat import current + args.filename = unquote_filename(args.filename) + if args.export: + fname, name, format = current.parse_filename(args.filename) + cells = [] + hist = list(self.shell.history_manager.get_range()) + for session, prompt_number, input in hist[:-1]: + cells.append(current.new_code_cell(prompt_number=prompt_number, + input=input)) + worksheet = current.new_worksheet(cells=cells) + nb = current.new_notebook(name=name,worksheets=[worksheet]) + with io.open(fname, 'w', encoding='utf-8') as f: + current.write(nb, f, format); + elif args.format is not None: + old_fname, old_name, old_format = current.parse_filename(args.filename) + new_format = args.format + if new_format == u'xml': + raise ValueError('Notebooks cannot be written as xml.') + elif new_format == u'ipynb' or new_format == u'json': + new_fname = old_name + u'.ipynb' + new_format = u'json' + elif new_format == u'py': + new_fname = old_name + u'.py' + else: + raise ValueError('Invalid notebook format: %s' % new_format) + with io.open(old_fname, 'r', encoding='utf-8') as f: + nb = current.read(f, old_format) + with io.open(new_fname, 'w', encoding='utf-8') as f: + current.write(nb, f, new_format) + + +class CodeMagics(Magics): + """Magics related to code management (loading, saving, editing, ...).""" + + def magic_save(self,parameter_s = ''): + """Save a set of lines or a macro to a given filename. + + Usage:\\ + %save [options] filename n1-n2 n3-n4 ... n5 .. n6 ... + + Options: + + -r: use 'raw' input. By default, the 'processed' history is used, + so that magics are loaded in their transformed version to valid + Python. If this option is given, the raw input as typed as the + command line is used instead. + + This function uses the same syntax as %history for input ranges, + then saves the lines to the filename you specify. + + It adds a '.py' extension to the file if you don't do so yourself, and + it asks for confirmation before overwriting existing files.""" + + opts,args = self.parse_options(parameter_s,'r',mode='list') + fname, codefrom = unquote_filename(args[0]), " ".join(args[1:]) + if not fname.endswith('.py'): + fname += '.py' + if os.path.isfile(fname): + ans = raw_input('File `%s` exists. Overwrite (y/[N])? ' % fname) + if ans.lower() not in ['y','yes']: + print 'Operation cancelled.' + return + try: + cmds = self.shell.find_user_code(codefrom, 'r' in opts) + except (TypeError, ValueError) as e: + print e.args[0] + return + with io.open(fname,'w', encoding="utf-8") as f: + f.write(u"# coding: utf-8\n") + f.write(py3compat.cast_unicode(cmds)) + print 'The following commands were written to file `%s`:' % fname + print cmds + + def magic_pastebin(self, parameter_s = ''): + """Upload code to Github's Gist paste bin, returning the URL. + + Usage:\\ + %pastebin [-d "Custom description"] 1-7 + + The argument can be an input history range, a filename, or the name of a + string or macro. + + Options: + + -d: Pass a custom description for the gist. The default will say + "Pasted from IPython". + """ + opts, args = self.parse_options(parameter_s, 'd:') + + try: + code = self.shell.find_user_code(args) + except (ValueError, TypeError) as e: + print e.args[0] + return + + post_data = json.dumps({ + "description": opts.get('d', "Pasted from IPython"), + "public": True, + "files": { + "file1.py": { + "content": code + } + } + }).encode('utf-8') + + response = urlopen("https://api.github.com/gists", post_data) + response_data = json.loads(response.read().decode('utf-8')) + return response_data['html_url'] + + def magic_loadpy(self, arg_s): + """Load a .py python script into the GUI console. + + This magic command can either take a local filename or a url:: + + %loadpy myscript.py + %loadpy http://www.example.com/myscript.py + """ + arg_s = unquote_filename(arg_s) + remote_url = arg_s.startswith(('http://', 'https://')) + local_url = not remote_url + if local_url and not arg_s.endswith('.py'): + # Local files must be .py; for remote URLs it's possible that the + # fetch URL doesn't have a .py in it (many servers have an opaque + # URL, such as scipy-central.org). + raise ValueError('%%loadpy only works with .py files: %s' % arg_s) + + # openpy takes care of finding the source encoding (per PEP 263) + if remote_url: + contents = openpy.read_py_url(arg_s, skip_encoding_cookie=True) + else: + contents = openpy.read_py_file(arg_s, skip_encoding_cookie=True) + + self.set_next_input(contents) + + def _find_edit_target(self, args, opts, last_call): + """Utility method used by magic_edit to find what to edit.""" + + def make_filename(arg): + "Make a filename from the given args" + arg = unquote_filename(arg) + try: + filename = get_py_filename(arg) + except IOError: + # If it ends with .py but doesn't already exist, assume we want + # a new file. + if arg.endswith('.py'): + filename = arg + else: + filename = None + return filename + + # Set a few locals from the options for convenience: + opts_prev = 'p' in opts + opts_raw = 'r' in opts + + # custom exceptions + class DataIsObject(Exception): pass + + # Default line number value + lineno = opts.get('n',None) + + if opts_prev: + args = '_%s' % last_call[0] + if not self.shell.user_ns.has_key(args): + args = last_call[1] + + # use last_call to remember the state of the previous call, but don't + # let it be clobbered by successive '-p' calls. + try: + last_call[0] = self.shell.displayhook.prompt_count + if not opts_prev: + last_call[1] = args + except: + pass + + # by default this is done with temp files, except when the given + # arg is a filename + use_temp = True + + data = '' + + # First, see if the arguments should be a filename. + filename = make_filename(args) + if filename: + use_temp = False + elif args: + # Mode where user specifies ranges of lines, like in %macro. + data = self.shell.extract_input_lines(args, opts_raw) + if not data: + try: + # Load the parameter given as a variable. If not a string, + # process it as an object instead (below) + + #print '*** args',args,'type',type(args) # dbg + data = eval(args, self.shell.user_ns) + if not isinstance(data, basestring): + raise DataIsObject + + except (NameError,SyntaxError): + # given argument is not a variable, try as a filename + filename = make_filename(args) + if filename is None: + warn("Argument given (%s) can't be found as a variable " + "or as a filename." % args) + return + use_temp = False + + except DataIsObject: + # macros have a special edit function + if isinstance(data, Macro): + raise MacroToEdit(data) + + # For objects, try to edit the file where they are defined + try: + filename = inspect.getabsfile(data) + if 'fakemodule' in filename.lower() and inspect.isclass(data): + # class created by %edit? Try to find source + # by looking for method definitions instead, the + # __module__ in those classes is FakeModule. + attrs = [getattr(data, aname) for aname in dir(data)] + for attr in attrs: + if not inspect.ismethod(attr): + continue + filename = inspect.getabsfile(attr) + if filename and 'fakemodule' not in filename.lower(): + # change the attribute to be the edit target instead + data = attr + break + + datafile = 1 + except TypeError: + filename = make_filename(args) + datafile = 1 + warn('Could not find file where `%s` is defined.\n' + 'Opening a file named `%s`' % (args,filename)) + # Now, make sure we can actually read the source (if it was in + # a temp file it's gone by now). + if datafile: + try: + if lineno is None: + lineno = inspect.getsourcelines(data)[1] + except IOError: + filename = make_filename(args) + if filename is None: + warn('The file `%s` where `%s` was defined cannot ' + 'be read.' % (filename,data)) + return + use_temp = False + + if use_temp: + filename = self.shell.mktempfile(data) + print 'IPython will make a temporary file named:',filename + + return filename, lineno, use_temp + + def _edit_macro(self,mname,macro): + """open an editor with the macro data in a file""" + filename = self.shell.mktempfile(macro.value) + self.shell.hooks.editor(filename) + + # and make a new macro object, to replace the old one + mfile = open(filename) + mvalue = mfile.read() + mfile.close() + self.shell.user_ns[mname] = Macro(mvalue) + + def magic_ed(self,parameter_s=''): + """Alias to %edit.""" + return self.magic_edit(parameter_s) + + @skip_doctest + def magic_edit(self,parameter_s='',last_call=['','']): + """Bring up an editor and execute the resulting code. + + Usage: + %edit [options] [args] + + %edit runs IPython's editor hook. The default version of this hook is + set to call the editor specified by your $EDITOR environment variable. + If this isn't found, it will default to vi under Linux/Unix and to + notepad under Windows. See the end of this docstring for how to change + the editor hook. + + You can also set the value of this editor via the + ``TerminalInteractiveShell.editor`` option in your configuration file. + This is useful if you wish to use a different editor from your typical + default with IPython (and for Windows users who typically don't set + environment variables). + + This command allows you to conveniently edit multi-line code right in + your IPython session. + + If called without arguments, %edit opens up an empty editor with a + temporary file and will execute the contents of this file when you + close it (don't forget to save it!). + + + Options: + + -n : open the editor at a specified line number. By default, + the IPython editor hook uses the unix syntax 'editor +N filename', but + you can configure this by providing your own modified hook if your + favorite editor supports line-number specifications with a different + syntax. + + -p: this will call the editor with the same data as the previous time + it was used, regardless of how long ago (in your current session) it + was. + + -r: use 'raw' input. This option only applies to input taken from the + user's history. By default, the 'processed' history is used, so that + magics are loaded in their transformed version to valid Python. If + this option is given, the raw input as typed as the command line is + used instead. When you exit the editor, it will be executed by + IPython's own processor. + + -x: do not execute the edited code immediately upon exit. This is + mainly useful if you are editing programs which need to be called with + command line arguments, which you can then do using %run. + + + Arguments: + + If arguments are given, the following possibilities exist: + + - If the argument is a filename, IPython will load that into the + editor. It will execute its contents with execfile() when you exit, + loading any code in the file into your interactive namespace. + + - The arguments are ranges of input history, e.g. "7 ~1/4-6". + The syntax is the same as in the %history magic. + + - If the argument is a string variable, its contents are loaded + into the editor. You can thus edit any string which contains + python code (including the result of previous edits). + + - If the argument is the name of an object (other than a string), + IPython will try to locate the file where it was defined and open the + editor at the point where it is defined. You can use `%edit function` + to load an editor exactly at the point where 'function' is defined, + edit it and have the file be executed automatically. + + - If the object is a macro (see %macro for details), this opens up your + specified editor with a temporary file containing the macro's data. + Upon exit, the macro is reloaded with the contents of the file. + + Note: opening at an exact line is only supported under Unix, and some + editors (like kedit and gedit up to Gnome 2.8) do not understand the + '+NUMBER' parameter necessary for this feature. Good editors like + (X)Emacs, vi, jed, pico and joe all do. + + After executing your code, %edit will return as output the code you + typed in the editor (except when it was an existing file). This way + you can reload the code in further invocations of %edit as a variable, + via _ or Out[], where is the prompt number of + the output. + + Note that %edit is also available through the alias %ed. + + This is an example of creating a simple function inside the editor and + then modifying it. First, start up the editor:: + + In [1]: ed + Editing... done. Executing edited code... + Out[1]: 'def foo():\\n print "foo() was defined in an editing + session"\\n' + + We can then call the function foo():: + + In [2]: foo() + foo() was defined in an editing session + + Now we edit foo. IPython automatically loads the editor with the + (temporary) file where foo() was previously defined:: + + In [3]: ed foo + Editing... done. Executing edited code... + + And if we call foo() again we get the modified version:: + + In [4]: foo() + foo() has now been changed! + + Here is an example of how to edit a code snippet successive + times. First we call the editor:: + + In [5]: ed + Editing... done. Executing edited code... + hello + Out[5]: "print 'hello'\\n" + + Now we call it again with the previous output (stored in _):: + + In [6]: ed _ + Editing... done. Executing edited code... + hello world + Out[6]: "print 'hello world'\\n" + + Now we call it with the output #8 (stored in _8, also as Out[8]):: + + In [7]: ed _8 + Editing... done. Executing edited code... + hello again + Out[7]: "print 'hello again'\\n" + + + Changing the default editor hook: + + If you wish to write your own editor hook, you can put it in a + configuration file which you load at startup time. The default hook + is defined in the IPython.core.hooks module, and you can use that as a + starting example for further modifications. That file also has + general instructions on how to set a new hook for use once you've + defined it.""" + opts,args = self.parse_options(parameter_s,'prxn:') + + try: + filename, lineno, is_temp = self._find_edit_target(args, opts, last_call) + except MacroToEdit as e: + self._edit_macro(args, e.args[0]) + return + + # do actual editing here + print 'Editing...', + sys.stdout.flush() + try: + # Quote filenames that may have spaces in them + if ' ' in filename: + filename = "'%s'" % filename + self.shell.hooks.editor(filename,lineno) + except TryNext: + warn('Could not open editor') + return + + # XXX TODO: should this be generalized for all string vars? + # For now, this is special-cased to blocks created by cpaste + if args.strip() == 'pasted_block': + self.shell.user_ns['pasted_block'] = file_read(filename) + + if 'x' in opts: # -x prevents actual execution + print + else: + print 'done. Executing edited code...' + if 'r' in opts: # Untranslated IPython code + self.shell.run_cell(file_read(filename), + store_history=False) + else: + self.shell.safe_execfile(filename, self.shell.user_ns, + self.shell.user_ns) + + if is_temp: + try: + return open(filename).read() + except IOError,msg: + if msg.filename == filename: + warn('File not found. Did you forget to save?') + return + else: + self.shell.showtraceback() + + +class ConfigMagics(Magics): + + def __init__(self, shell): + super(ConfigMagics, self).__init__(shell) + self.configurables = [] + + def magic_config(self, s): + """configure IPython + + %config Class[.trait=value] + + This magic exposes most of the IPython config system. Any + Configurable class should be able to be configured with the simple + line:: + + %config Class.trait=value + + Where `value` will be resolved in the user's namespace, if it is an + expression or variable name. + + Examples + -------- + + To see what classes are available for config, pass no arguments:: + + In [1]: %config + Available objects for config: + TerminalInteractiveShell + HistoryManager + PrefilterManager + AliasManager + IPCompleter + PromptManager + DisplayFormatter + + To view what is configurable on a given class, just pass the class + name:: + + In [2]: %config IPCompleter + IPCompleter options + ----------------- + IPCompleter.omit__names= + Current: 2 + Choices: (0, 1, 2) + Instruct the completer to omit private method names + Specifically, when completing on ``object.``. + When 2 [default]: all names that start with '_' will be excluded. + When 1: all 'magic' names (``__foo__``) will be excluded. + When 0: nothing will be excluded. + IPCompleter.merge_completions= + Current: True + Whether to merge completion results into a single list + If False, only the completion results from the first non-empty completer + will be returned. + IPCompleter.limit_to__all__= + Current: False + Instruct the completer to use __all__ for the completion + Specifically, when completing on ``object.``. + When True: only those names in obj.__all__ will be included. + When False [default]: the __all__ attribute is ignored + IPCompleter.greedy= + Current: False + Activate greedy completion + This will enable completion on elements of lists, results of function calls, + etc., but can be unsafe because the code is actually evaluated on TAB. + + but the real use is in setting values:: + + In [3]: %config IPCompleter.greedy = True + + and these values are read from the user_ns if they are variables:: + + In [4]: feeling_greedy=False + + In [5]: %config IPCompleter.greedy = feeling_greedy + + """ + from IPython.config.loader import Config + # some IPython objects are Configurable, but do not yet have + # any configurable traits. Exclude them from the effects of + # this magic, as their presence is just noise: + configurables = [ c for c in self.shell.configurables + if c.__class__.class_traits(config=True) ] + classnames = [ c.__class__.__name__ for c in configurables ] + + line = s.strip() + if not line: + # print available configurable names + print "Available objects for config:" + for name in classnames: + print " ", name + return + elif line in classnames: + # `%config TerminalInteractiveShell` will print trait info for + # TerminalInteractiveShell + c = configurables[classnames.index(line)] + cls = c.__class__ + help = cls.class_get_help(c) + # strip leading '--' from cl-args: + help = re.sub(re.compile(r'^--', re.MULTILINE), '', help) + print help + return + elif '=' not in line: + raise UsageError("Invalid config statement: %r, should be Class.trait = value" % line) + + + # otherwise, assume we are setting configurables. + # leave quotes on args when splitting, because we want + # unquoted args to eval in user_ns + cfg = Config() + exec "cfg."+line in locals(), self.shell.user_ns + + for configurable in configurables: + try: + configurable.update_config(cfg) + except Exception as e: + error(e) + + +class NamespaceMagics(Magics): + """Magics to manage various aspects of the user's namespace. + + These include listing variables, introspecting into them, etc. + """ + + def magic_pinfo(self, parameter_s='', namespaces=None): + """Provide detailed information about an object. + + '%pinfo object' is just a synonym for object? or ?object.""" + + #print 'pinfo par: <%s>' % parameter_s # dbg + + + # detail_level: 0 -> obj? , 1 -> obj?? + detail_level = 0 + # We need to detect if we got called as 'pinfo pinfo foo', which can + # happen if the user types 'pinfo foo?' at the cmd line. + pinfo,qmark1,oname,qmark2 = \ + re.match('(pinfo )?(\?*)(.*?)(\??$)',parameter_s).groups() + if pinfo or qmark1 or qmark2: + detail_level = 1 + if "*" in oname: + self.magic_psearch(oname) + else: + self.shell._inspect('pinfo', oname, detail_level=detail_level, + namespaces=namespaces) + + def magic_pinfo2(self, parameter_s='', namespaces=None): + """Provide extra detailed information about an object. + + '%pinfo2 object' is just a synonym for object?? or ??object.""" + self.shell._inspect('pinfo', parameter_s, detail_level=1, + namespaces=namespaces) + + @skip_doctest + def magic_pdef(self, parameter_s='', namespaces=None): + """Print the definition header for any callable object. + + If the object is a class, print the constructor information. + + Examples + -------- + :: + + In [3]: %pdef urllib.urlopen + urllib.urlopen(url, data=None, proxies=None) + """ + self._inspect('pdef',parameter_s, namespaces) + + def magic_pdoc(self, parameter_s='', namespaces=None): + """Print the docstring for an object. + + If the given object is a class, it will print both the class and the + constructor docstrings.""" + self._inspect('pdoc',parameter_s, namespaces) + + def magic_psource(self, parameter_s='', namespaces=None): + """Print (or run through pager) the source code for an object.""" + self._inspect('psource',parameter_s, namespaces) + + def magic_pfile(self, parameter_s=''): + """Print (or run through pager) the file where an object is defined. + + The file opens at the line where the object definition begins. IPython + will honor the environment variable PAGER if set, and otherwise will + do its best to print the file in a convenient form. + + If the given argument is not an object currently defined, IPython will + try to interpret it as a filename (automatically adding a .py extension + if needed). You can thus use %pfile as a syntax highlighting code + viewer.""" + + # first interpret argument as an object name + out = self._inspect('pfile',parameter_s) + # if not, try the input as a filename + if out == 'not found': + try: + filename = get_py_filename(parameter_s) + except IOError,msg: + print msg + return + page.page(self.shell.inspector.format(open(filename).read())) + + def magic_psearch(self, parameter_s=''): + """Search for object in namespaces by wildcard. + + %psearch [options] PATTERN [OBJECT TYPE] + + Note: ? can be used as a synonym for %psearch, at the beginning or at + the end: both a*? and ?a* are equivalent to '%psearch a*'. Still, the + rest of the command line must be unchanged (options come first), so + for example the following forms are equivalent + + %psearch -i a* function + -i a* function? + ?-i a* function + + Arguments: + + PATTERN + + where PATTERN is a string containing * as a wildcard similar to its + use in a shell. The pattern is matched in all namespaces on the + search path. By default objects starting with a single _ are not + matched, many IPython generated objects have a single + underscore. The default is case insensitive matching. Matching is + also done on the attributes of objects and not only on the objects + in a module. + + [OBJECT TYPE] + + Is the name of a python type from the types module. The name is + given in lowercase without the ending type, ex. StringType is + written string. By adding a type here only objects matching the + given type are matched. Using all here makes the pattern match all + types (this is the default). + + Options: + + -a: makes the pattern match even objects whose names start with a + single underscore. These names are normally omitted from the + search. + + -i/-c: make the pattern case insensitive/sensitive. If neither of + these options are given, the default is read from your configuration + file, with the option ``InteractiveShell.wildcards_case_sensitive``. + If this option is not specified in your configuration file, IPython's + internal default is to do a case sensitive search. + + -e/-s NAMESPACE: exclude/search a given namespace. The pattern you + specify can be searched in any of the following namespaces: + 'builtin', 'user', 'user_global','internal', 'alias', where + 'builtin' and 'user' are the search defaults. Note that you should + not use quotes when specifying namespaces. + + 'Builtin' contains the python module builtin, 'user' contains all + user data, 'alias' only contain the shell aliases and no python + objects, 'internal' contains objects used by IPython. The + 'user_global' namespace is only used by embedded IPython instances, + and it contains module-level globals. You can add namespaces to the + search with -s or exclude them with -e (these options can be given + more than once). + + Examples + -------- + :: + + %psearch a* -> objects beginning with an a + %psearch -e builtin a* -> objects NOT in the builtin space starting in a + %psearch a* function -> all functions beginning with an a + %psearch re.e* -> objects beginning with an e in module re + %psearch r*.e* -> objects that start with e in modules starting in r + %psearch r*.* string -> all strings in modules beginning with r + + Case sensitive search:: + + %psearch -c a* list all object beginning with lower case a + + Show objects beginning with a single _:: + + %psearch -a _* list objects beginning with a single underscore""" + try: + parameter_s.encode('ascii') + except UnicodeEncodeError: + print 'Python identifiers can only contain ascii characters.' + return + + # default namespaces to be searched + def_search = ['user_local', 'user_global', 'builtin'] + + # Process options/args + opts,args = self.parse_options(parameter_s,'cias:e:',list_all=True) + opt = opts.get + shell = self.shell + psearch = shell.inspector.psearch + + # select case options + if opts.has_key('i'): + ignore_case = True + elif opts.has_key('c'): + ignore_case = False + else: + ignore_case = not shell.wildcards_case_sensitive + + # Build list of namespaces to search from user options + def_search.extend(opt('s',[])) + ns_exclude = ns_exclude=opt('e',[]) + ns_search = [nm for nm in def_search if nm not in ns_exclude] + + # Call the actual search + try: + psearch(args,shell.ns_table,ns_search, + show_all=opt('a'),ignore_case=ignore_case) + except: + shell.showtraceback() + + @skip_doctest + def magic_who_ls(self, parameter_s=''): + """Return a sorted list of all interactive variables. + + If arguments are given, only variables of types matching these + arguments are returned. + + Examples + -------- + + Define two variables and list them with who_ls:: + + In [1]: alpha = 123 + + In [2]: beta = 'test' + + In [3]: %who_ls + Out[3]: ['alpha', 'beta'] + + In [4]: %who_ls int + Out[4]: ['alpha'] + + In [5]: %who_ls str + Out[5]: ['beta'] + """ + + user_ns = self.shell.user_ns + user_ns_hidden = self.shell.user_ns_hidden + out = [ i for i in user_ns + if not i.startswith('_') \ + and not i in user_ns_hidden ] + + typelist = parameter_s.split() + if typelist: + typeset = set(typelist) + out = [i for i in out if type(user_ns[i]).__name__ in typeset] + + out.sort() + return out + + @skip_doctest + def magic_who(self, parameter_s=''): + """Print all interactive variables, with some minimal formatting. + + If any arguments are given, only variables whose type matches one of + these are printed. For example:: + + %who function str + + will only list functions and strings, excluding all other types of + variables. To find the proper type names, simply use type(var) at a + command line to see how python prints type names. For example: + + :: + + In [1]: type('hello')\\ + Out[1]: + + indicates that the type name for strings is 'str'. + + ``%who`` always excludes executed names loaded through your configuration + file and things which are internal to IPython. + + This is deliberate, as typically you may load many modules and the + purpose of %who is to show you only what you've manually defined. + + Examples + -------- + + Define two variables and list them with who:: + + In [1]: alpha = 123 + + In [2]: beta = 'test' + + In [3]: %who + alpha beta + + In [4]: %who int + alpha + + In [5]: %who str + beta + """ + + varlist = self.magic_who_ls(parameter_s) + if not varlist: + if parameter_s: + print 'No variables match your requested type.' + else: + print 'Interactive namespace is empty.' + return + + # if we have variables, move on... + count = 0 + for i in varlist: + print i+'\t', + count += 1 + if count > 8: + count = 0 + print + print + + @skip_doctest + def magic_whos(self, parameter_s=''): + """Like %who, but gives some extra information about each variable. + + The same type filtering of %who can be applied here. + + For all variables, the type is printed. Additionally it prints: + + - For {},[],(): their length. + + - For numpy arrays, a summary with shape, number of + elements, typecode and size in memory. + + - Everything else: a string representation, snipping their middle if + too long. + + Examples + -------- + + Define two variables and list them with whos:: + + In [1]: alpha = 123 + + In [2]: beta = 'test' + + In [3]: %whos + Variable Type Data/Info + -------------------------------- + alpha int 123 + beta str test + """ + + varnames = self.magic_who_ls(parameter_s) + if not varnames: + if parameter_s: + print 'No variables match your requested type.' + else: + print 'Interactive namespace is empty.' + return + + # if we have variables, move on... + + # for these types, show len() instead of data: + seq_types = ['dict', 'list', 'tuple'] + + # for numpy arrays, display summary info + ndarray_type = None + if 'numpy' in sys.modules: + try: + from numpy import ndarray + except ImportError: + pass + else: + ndarray_type = ndarray.__name__ + + # Find all variable names and types so we can figure out column sizes + def get_vars(i): + return self.shell.user_ns[i] + + # some types are well known and can be shorter + abbrevs = {'IPython.core.macro.Macro' : 'Macro'} + def type_name(v): + tn = type(v).__name__ + return abbrevs.get(tn,tn) + + varlist = map(get_vars,varnames) + + typelist = [] + for vv in varlist: + tt = type_name(vv) + + if tt=='instance': + typelist.append( abbrevs.get(str(vv.__class__), + str(vv.__class__))) + else: + typelist.append(tt) + + # column labels and # of spaces as separator + varlabel = 'Variable' + typelabel = 'Type' + datalabel = 'Data/Info' + colsep = 3 + # variable format strings + vformat = "{0:<{varwidth}}{1:<{typewidth}}" + aformat = "%s: %s elems, type `%s`, %s bytes" + # find the size of the columns to format the output nicely + varwidth = max(max(map(len,varnames)), len(varlabel)) + colsep + typewidth = max(max(map(len,typelist)), len(typelabel)) + colsep + # table header + print varlabel.ljust(varwidth) + typelabel.ljust(typewidth) + \ + ' '+datalabel+'\n' + '-'*(varwidth+typewidth+len(datalabel)+1) + # and the table itself + kb = 1024 + Mb = 1048576 # kb**2 + for vname,var,vtype in zip(varnames,varlist,typelist): + print vformat.format(vname, vtype, varwidth=varwidth, typewidth=typewidth), + if vtype in seq_types: + print "n="+str(len(var)) + elif vtype == ndarray_type: + vshape = str(var.shape).replace(',','').replace(' ','x')[1:-1] + if vtype==ndarray_type: + # numpy + vsize = var.size + vbytes = vsize*var.itemsize + vdtype = var.dtype + + if vbytes < 100000: + print aformat % (vshape,vsize,vdtype,vbytes) + else: + print aformat % (vshape,vsize,vdtype,vbytes), + if vbytes < Mb: + print '(%s kb)' % (vbytes/kb,) + else: + print '(%s Mb)' % (vbytes/Mb,) + else: + try: + vstr = str(var) + except UnicodeEncodeError: + vstr = unicode(var).encode(DEFAULT_ENCODING, + 'backslashreplace') + except: + vstr = "" % id(var) + vstr = vstr.replace('\n','\\n') + if len(vstr) < 50: + print vstr + else: + print vstr[:25] + "<...>" + vstr[-25:] + + def magic_reset(self, parameter_s=''): + """Resets the namespace by removing all names defined by the user, if + called without arguments, or by removing some types of objects, such + as everything currently in IPython's In[] and Out[] containers (see + the parameters for details). + + Parameters + ---------- + -f : force reset without asking for confirmation. + + -s : 'Soft' reset: Only clears your namespace, leaving history intact. + References to objects may be kept. By default (without this option), + we do a 'hard' reset, giving you a new session and removing all + references to objects from the current session. + + in : reset input history + + out : reset output history + + dhist : reset directory history + + array : reset only variables that are NumPy arrays + + See Also + -------- + magic_reset_selective : invoked as ``%reset_selective`` + + Examples + -------- + :: + + In [6]: a = 1 + + In [7]: a + Out[7]: 1 + + In [8]: 'a' in _ip.user_ns + Out[8]: True + + In [9]: %reset -f + + In [1]: 'a' in _ip.user_ns + Out[1]: False + + In [2]: %reset -f in + Flushing input history + + In [3]: %reset -f dhist in + Flushing directory history + Flushing input history + + Notes + ----- + Calling this magic from clients that do not implement standard input, + such as the ipython notebook interface, will reset the namespace + without confirmation. + """ + opts, args = self.parse_options(parameter_s,'sf', mode='list') + if 'f' in opts: + ans = True + else: + try: + ans = self.shell.ask_yes_no( + "Once deleted, variables cannot be recovered. Proceed (y/[n])? ", default='n') + except StdinNotImplementedError: + ans = True + if not ans: + print 'Nothing done.' + return + + if 's' in opts: # Soft reset + user_ns = self.shell.user_ns + for i in self.magic_who_ls(): + del(user_ns[i]) + elif len(args) == 0: # Hard reset + self.shell.reset(new_session = False) + + # reset in/out/dhist/array: previously extensinions/clearcmd.py + ip = self.shell + user_ns = self.shell.user_ns # local lookup, heavily used + + for target in args: + target = target.lower() # make matches case insensitive + if target == 'out': + print "Flushing output cache (%d entries)" % len(user_ns['_oh']) + self.shell.displayhook.flush() + + elif target == 'in': + print "Flushing input history" + pc = self.shell.displayhook.prompt_count + 1 + for n in range(1, pc): + key = '_i'+repr(n) + user_ns.pop(key,None) + user_ns.update(dict(_i=u'',_ii=u'',_iii=u'')) + hm = ip.history_manager + # don't delete these, as %save and %macro depending on the length + # of these lists to be preserved + hm.input_hist_parsed[:] = [''] * pc + hm.input_hist_raw[:] = [''] * pc + # hm has internal machinery for _i,_ii,_iii, clear it out + hm._i = hm._ii = hm._iii = hm._i00 = u'' + + elif target == 'array': + # Support cleaning up numpy arrays + try: + from numpy import ndarray + # This must be done with items and not iteritems because we're + # going to modify the dict in-place. + for x,val in user_ns.items(): + if isinstance(val,ndarray): + del user_ns[x] + except ImportError: + print "reset array only works if Numpy is available." + + elif target == 'dhist': + print "Flushing directory history" + del user_ns['_dh'][:] + + else: + print "Don't know how to reset ", + print target + ", please run `%reset?` for details" + + gc.collect() + + def magic_reset_selective(self, parameter_s=''): + """Resets the namespace by removing names defined by the user. + + Input/Output history are left around in case you need them. + + %reset_selective [-f] regex + + No action is taken if regex is not included + + Options + -f : force reset without asking for confirmation. + + See Also + -------- + magic_reset : invoked as ``%reset`` + + Examples + -------- + + We first fully reset the namespace so your output looks identical to + this example for pedagogical reasons; in practice you do not need a + full reset:: + + In [1]: %reset -f + + Now, with a clean namespace we can make a few variables and use + ``%reset_selective`` to only delete names that match our regexp:: + + In [2]: a=1; b=2; c=3; b1m=4; b2m=5; b3m=6; b4m=7; b2s=8 + + In [3]: who_ls + Out[3]: ['a', 'b', 'b1m', 'b2m', 'b2s', 'b3m', 'b4m', 'c'] + + In [4]: %reset_selective -f b[2-3]m + + In [5]: who_ls + Out[5]: ['a', 'b', 'b1m', 'b2s', 'b4m', 'c'] + + In [6]: %reset_selective -f d + + In [7]: who_ls + Out[7]: ['a', 'b', 'b1m', 'b2s', 'b4m', 'c'] + + In [8]: %reset_selective -f c + + In [9]: who_ls + Out[9]: ['a', 'b', 'b1m', 'b2s', 'b4m'] + + In [10]: %reset_selective -f b + + In [11]: who_ls + Out[11]: ['a'] + + Notes + ----- + Calling this magic from clients that do not implement standard input, + such as the ipython notebook interface, will reset the namespace + without confirmation. + """ + + opts, regex = self.parse_options(parameter_s,'f') + + if opts.has_key('f'): + ans = True + else: + try: + ans = self.shell.ask_yes_no( + "Once deleted, variables cannot be recovered. Proceed (y/[n])? ", + default='n') + except StdinNotImplementedError: + ans = True + if not ans: + print 'Nothing done.' + return + user_ns = self.shell.user_ns + if not regex: + print 'No regex pattern specified. Nothing done.' + return + else: + try: + m = re.compile(regex) + except TypeError: + raise TypeError('regex must be a string or compiled pattern') + for i in self.magic_who_ls(): + if m.search(i): + del(user_ns[i]) + + def magic_xdel(self, parameter_s=''): + """Delete a variable, trying to clear it from anywhere that + IPython's machinery has references to it. By default, this uses + the identity of the named object in the user namespace to remove + references held under other names. The object is also removed + from the output history. + + Options + -n : Delete the specified name from all namespaces, without + checking their identity. + """ + opts, varname = self.parse_options(parameter_s,'n') + try: + self.shell.del_var(varname, ('n' in opts)) + except (NameError, ValueError) as e: + print type(e).__name__ +": "+ str(e) + + +class ExecutionMagics(Magics): + """Magics related to code execution, debugging, profiling, etc. + + """ + + def __init__(self, shell): + super(ExecutionMagics, self).__init__(shell) + if profile is None: + self.magic_prun = self.profile_missing_notice + # Default execution function used to actually run user code. + self.default_runner = None + + def profile_missing_notice(self, *args, **kwargs): + error("""\ +The profile module could not be found. It has been removed from the standard +python packages because of its non-free license. To use profiling, install the +python-profiler package from non-free.""") + + @skip_doctest + def magic_prun(self, parameter_s ='',user_mode=1, + opts=None,arg_lst=None,prog_ns=None): + + """Run a statement through the python code profiler. + + Usage: + %prun [options] statement + + The given statement (which doesn't require quote marks) is run via the + python profiler in a manner similar to the profile.run() function. + Namespaces are internally managed to work correctly; profile.run + cannot be used in IPython because it makes certain assumptions about + namespaces which do not hold under IPython. + + Options: + + -l : you can place restrictions on what or how much of the + profile gets printed. The limit value can be: + + * A string: only information for function names containing this string + is printed. + + * An integer: only these many lines are printed. + + * A float (between 0 and 1): this fraction of the report is printed + (for example, use a limit of 0.4 to see the topmost 40% only). + + You can combine several limits with repeated use of the option. For + example, '-l __init__ -l 5' will print only the topmost 5 lines of + information about class constructors. + + -r: return the pstats.Stats object generated by the profiling. This + object has all the information about the profile in it, and you can + later use it for further analysis or in other functions. + + -s : sort profile by given key. You can provide more than one key + by using the option several times: '-s key1 -s key2 -s key3...'. The + default sorting key is 'time'. + + The following is copied verbatim from the profile documentation + referenced below: + + When more than one key is provided, additional keys are used as + secondary criteria when the there is equality in all keys selected + before them. + + Abbreviations can be used for any key names, as long as the + abbreviation is unambiguous. The following are the keys currently + defined: + + Valid Arg Meaning + "calls" call count + "cumulative" cumulative time + "file" file name + "module" file name + "pcalls" primitive call count + "line" line number + "name" function name + "nfl" name/file/line + "stdname" standard name + "time" internal time + + Note that all sorts on statistics are in descending order (placing + most time consuming items first), where as name, file, and line number + searches are in ascending order (i.e., alphabetical). The subtle + distinction between "nfl" and "stdname" is that the standard name is a + sort of the name as printed, which means that the embedded line + numbers get compared in an odd way. For example, lines 3, 20, and 40 + would (if the file names were the same) appear in the string order + "20" "3" and "40". In contrast, "nfl" does a numeric compare of the + line numbers. In fact, sort_stats("nfl") is the same as + sort_stats("name", "file", "line"). + + -T : save profile results as shown on screen to a text + file. The profile is still shown on screen. + + -D : save (via dump_stats) profile statistics to given + filename. This data is in a format understood by the pstats module, and + is generated by a call to the dump_stats() method of profile + objects. The profile is still shown on screen. + + -q: suppress output to the pager. Best used with -T and/or -D above. + + If you want to run complete programs under the profiler's control, use + '%run -p [prof_opts] filename.py [args to program]' where prof_opts + contains profiler specific options as described here. + + You can read the complete documentation for the profile module with:: + + In [1]: import profile; profile.help() + """ + + opts_def = Struct(D=[''],l=[],s=['time'],T=['']) + + if user_mode: # regular user call + opts,arg_str = self.parse_options(parameter_s,'D:l:rs:T:q', + list_all=1, posix=False) + namespace = self.shell.user_ns + else: # called to run a program by %run -p + try: + filename = get_py_filename(arg_lst[0]) + except IOError as e: + try: + msg = str(e) + except UnicodeError: + msg = e.message + error(msg) + return + + arg_str = 'execfile(filename,prog_ns)' + namespace = { + 'execfile': self.shell.safe_execfile, + 'prog_ns': prog_ns, + 'filename': filename + } + + opts.merge(opts_def) + + prof = profile.Profile() + try: + prof = prof.runctx(arg_str,namespace,namespace) + sys_exit = '' + except SystemExit: + sys_exit = """*** SystemExit exception caught in code being profiled.""" + + stats = pstats.Stats(prof).strip_dirs().sort_stats(*opts.s) + + lims = opts.l + if lims: + lims = [] # rebuild lims with ints/floats/strings + for lim in opts.l: + try: + lims.append(int(lim)) + except ValueError: + try: + lims.append(float(lim)) + except ValueError: + lims.append(lim) + + # Trap output. + stdout_trap = StringIO() + + if hasattr(stats,'stream'): + # In newer versions of python, the stats object has a 'stream' + # attribute to write into. + stats.stream = stdout_trap + stats.print_stats(*lims) + else: + # For older versions, we manually redirect stdout during printing + sys_stdout = sys.stdout + try: + sys.stdout = stdout_trap + stats.print_stats(*lims) + finally: + sys.stdout = sys_stdout + + output = stdout_trap.getvalue() + output = output.rstrip() + + if 'q' not in opts: + page.page(output) + print sys_exit, + + dump_file = opts.D[0] + text_file = opts.T[0] + if dump_file: + dump_file = unquote_filename(dump_file) + prof.dump_stats(dump_file) + print '\n*** Profile stats marshalled to file',\ + `dump_file`+'.',sys_exit + if text_file: + text_file = unquote_filename(text_file) + pfile = open(text_file,'w') + pfile.write(output) + pfile.close() + print '\n*** Profile printout saved to text file',\ + `text_file`+'.',sys_exit + + if opts.has_key('r'): + return stats + else: + return None + + def magic_pdb(self, parameter_s=''): + """Control the automatic calling of the pdb interactive debugger. + + Call as '%pdb on', '%pdb 1', '%pdb off' or '%pdb 0'. If called without + argument it works as a toggle. + + When an exception is triggered, IPython can optionally call the + interactive pdb debugger after the traceback printout. %pdb toggles + this feature on and off. + + The initial state of this feature is set in your configuration + file (the option is ``InteractiveShell.pdb``). + + If you want to just activate the debugger AFTER an exception has fired, + without having to type '%pdb on' and rerunning your code, you can use + the %debug magic.""" + + par = parameter_s.strip().lower() + + if par: + try: + new_pdb = {'off':0,'0':0,'on':1,'1':1}[par] + except KeyError: + print ('Incorrect argument. Use on/1, off/0, ' + 'or nothing for a toggle.') + return + else: + # toggle + new_pdb = not self.shell.call_pdb + + # set on the shell + self.shell.call_pdb = new_pdb + print 'Automatic pdb calling has been turned',on_off(new_pdb) + + def magic_debug(self, parameter_s=''): + """Activate the interactive debugger in post-mortem mode. + + If an exception has just occurred, this lets you inspect its stack + frames interactively. Note that this will always work only on the last + traceback that occurred, so you must call this quickly after an + exception that you wish to inspect has fired, because if another one + occurs, it clobbers the previous one. + + If you want IPython to automatically do this on every exception, see + the %pdb magic for more details. + """ + self.shell.debugger(force=True) + + def magic_tb(self, s): + """Print the last traceback with the currently active exception mode. + + See %xmode for changing exception reporting modes.""" + self.shell.showtraceback() + + @skip_doctest + def magic_run(self, parameter_s ='', runner=None, + file_finder=get_py_filename): + """Run the named file inside IPython as a program. + + Usage:\\ + %run [-n -i -t [-N] -d [-b] -p [profile options]] file [args] + + Parameters after the filename are passed as command-line arguments to + the program (put in sys.argv). Then, control returns to IPython's + prompt. + + This is similar to running at a system prompt:\\ + $ python file args\\ + but with the advantage of giving you IPython's tracebacks, and of + loading all variables into your interactive namespace for further use + (unless -p is used, see below). + + The file is executed in a namespace initially consisting only of + __name__=='__main__' and sys.argv constructed as indicated. It thus + sees its environment as if it were being run as a stand-alone program + (except for sharing global objects such as previously imported + modules). But after execution, the IPython interactive namespace gets + updated with all variables defined in the program (except for __name__ + and sys.argv). This allows for very convenient loading of code for + interactive work, while giving each program a 'clean sheet' to run in. + + Options: + + -n: __name__ is NOT set to '__main__', but to the running file's name + without extension (as python does under import). This allows running + scripts and reloading the definitions in them without calling code + protected by an ' if __name__ == "__main__" ' clause. + + -i: run the file in IPython's namespace instead of an empty one. This + is useful if you are experimenting with code written in a text editor + which depends on variables defined interactively. + + -e: ignore sys.exit() calls or SystemExit exceptions in the script + being run. This is particularly useful if IPython is being used to + run unittests, which always exit with a sys.exit() call. In such + cases you are interested in the output of the test results, not in + seeing a traceback of the unittest module. + + -t: print timing information at the end of the run. IPython will give + you an estimated CPU time consumption for your script, which under + Unix uses the resource module to avoid the wraparound problems of + time.clock(). Under Unix, an estimate of time spent on system tasks + is also given (for Windows platforms this is reported as 0.0). + + If -t is given, an additional -N option can be given, where + must be an integer indicating how many times you want the script to + run. The final timing report will include total and per run results. + + For example (testing the script uniq_stable.py):: + + In [1]: run -t uniq_stable + + IPython CPU timings (estimated):\\ + User : 0.19597 s.\\ + System: 0.0 s.\\ + + In [2]: run -t -N5 uniq_stable + + IPython CPU timings (estimated):\\ + Total runs performed: 5\\ + Times : Total Per run\\ + User : 0.910862 s, 0.1821724 s.\\ + System: 0.0 s, 0.0 s. + + -d: run your program under the control of pdb, the Python debugger. + This allows you to execute your program step by step, watch variables, + etc. Internally, what IPython does is similar to calling: + + pdb.run('execfile("YOURFILENAME")') + + with a breakpoint set on line 1 of your file. You can change the line + number for this automatic breakpoint to be by using the -bN option + (where N must be an integer). For example:: + + %run -d -b40 myscript + + will set the first breakpoint at line 40 in myscript.py. Note that + the first breakpoint must be set on a line which actually does + something (not a comment or docstring) for it to stop execution. + + When the pdb debugger starts, you will see a (Pdb) prompt. You must + first enter 'c' (without quotes) to start execution up to the first + breakpoint. + + Entering 'help' gives information about the use of the debugger. You + can easily see pdb's full documentation with "import pdb;pdb.help()" + at a prompt. + + -p: run program under the control of the Python profiler module (which + prints a detailed report of execution times, function calls, etc). + + You can pass other options after -p which affect the behavior of the + profiler itself. See the docs for %prun for details. + + In this mode, the program's variables do NOT propagate back to the + IPython interactive namespace (because they remain in the namespace + where the profiler executes them). + + Internally this triggers a call to %prun, see its documentation for + details on the options available specifically for profiling. + + There is one special usage for which the text above doesn't apply: + if the filename ends with .ipy, the file is run as ipython script, + just as if the commands were written on IPython prompt. + + -m: specify module name to load instead of script path. Similar to + the -m option for the python interpreter. Use this option last if you + want to combine with other %run options. Unlike the python interpreter + only source modules are allowed no .pyc or .pyo files. + For example:: + + %run -m example + + will run the example module. + + """ + + # get arguments and set sys.argv for program to be run. + opts, arg_lst = self.parse_options(parameter_s, 'nidtN:b:pD:l:rs:T:em:', + mode='list', list_all=1) + if "m" in opts: + modulename = opts["m"][0] + modpath = find_mod(modulename) + if modpath is None: + warn('%r is not a valid modulename on sys.path'%modulename) + return + arg_lst = [modpath] + arg_lst + try: + filename = file_finder(arg_lst[0]) + except IndexError: + warn('you must provide at least a filename.') + print '\n%run:\n', oinspect.getdoc(self.magic_run) + return + except IOError as e: + try: + msg = str(e) + except UnicodeError: + msg = e.message + error(msg) + return + + if filename.lower().endswith('.ipy'): + self.shell.safe_execfile_ipy(filename) + return + + # Control the response to exit() calls made by the script being run + exit_ignore = 'e' in opts + + # Make sure that the running script gets a proper sys.argv as if it + # were run from a system shell. + save_argv = sys.argv # save it for later restoring + + # simulate shell expansion on arguments, at least tilde expansion + args = [ os.path.expanduser(a) for a in arg_lst[1:] ] + + sys.argv = [filename] + args # put in the proper filename + # protect sys.argv from potential unicode strings on Python 2: + if not py3compat.PY3: + sys.argv = [ py3compat.cast_bytes(a) for a in sys.argv ] + + if 'i' in opts: + # Run in user's interactive namespace + prog_ns = self.shell.user_ns + __name__save = self.shell.user_ns['__name__'] + prog_ns['__name__'] = '__main__' + main_mod = self.shell.new_main_mod(prog_ns) + else: + # Run in a fresh, empty namespace + if 'n' in opts: + name = os.path.splitext(os.path.basename(filename))[0] + else: + name = '__main__' + + main_mod = self.shell.new_main_mod() + prog_ns = main_mod.__dict__ + prog_ns['__name__'] = name + + # Since '%run foo' emulates 'python foo.py' at the cmd line, we must + # set the __file__ global in the script's namespace + prog_ns['__file__'] = filename + + # pickle fix. See interactiveshell for an explanation. But we need to make sure + # that, if we overwrite __main__, we replace it at the end + main_mod_name = prog_ns['__name__'] + + if main_mod_name == '__main__': + restore_main = sys.modules['__main__'] + else: + restore_main = False + + # This needs to be undone at the end to prevent holding references to + # every single object ever created. + sys.modules[main_mod_name] = main_mod + + try: + stats = None + with self.shell.readline_no_record: + if 'p' in opts: + stats = self.magic_prun('', 0, opts, arg_lst, prog_ns) + else: + if 'd' in opts: + deb = debugger.Pdb(self.shell.colors) + # reset Breakpoint state, which is moronically kept + # in a class + bdb.Breakpoint.next = 1 + bdb.Breakpoint.bplist = {} + bdb.Breakpoint.bpbynumber = [None] + # Set an initial breakpoint to stop execution + maxtries = 10 + bp = int(opts.get('b', [1])[0]) + checkline = deb.checkline(filename, bp) + if not checkline: + for bp in range(bp + 1, bp + maxtries + 1): + if deb.checkline(filename, bp): + break + else: + msg = ("\nI failed to find a valid line to set " + "a breakpoint\n" + "after trying up to line: %s.\n" + "Please set a valid breakpoint manually " + "with the -b option." % bp) + error(msg) + return + # if we find a good linenumber, set the breakpoint + deb.do_break('%s:%s' % (filename, bp)) + # Start file run + print "NOTE: Enter 'c' at the", + print "%s prompt to start your script." % deb.prompt + ns = {'execfile': py3compat.execfile, 'prog_ns': prog_ns} + try: + deb.run('execfile("%s", prog_ns)' % filename, ns) + + except: + etype, value, tb = sys.exc_info() + # Skip three frames in the traceback: the %run one, + # one inside bdb.py, and the command-line typed by the + # user (run by exec in pdb itself). + self.shell.InteractiveTB(etype, value, tb, tb_offset=3) + else: + if runner is None: + runner = self.default_runner + if runner is None: + runner = self.shell.safe_execfile + if 't' in opts: + # timed execution + try: + nruns = int(opts['N'][0]) + if nruns < 1: + error('Number of runs must be >=1') + return + except (KeyError): + nruns = 1 + twall0 = time.time() + if nruns == 1: + t0 = clock2() + runner(filename, prog_ns, prog_ns, + exit_ignore=exit_ignore) + t1 = clock2() + t_usr = t1[0] - t0[0] + t_sys = t1[1] - t0[1] + print "\nIPython CPU timings (estimated):" + print " User : %10.2f s." % t_usr + print " System : %10.2f s." % t_sys + else: + runs = range(nruns) + t0 = clock2() + for nr in runs: + runner(filename, prog_ns, prog_ns, + exit_ignore=exit_ignore) + t1 = clock2() + t_usr = t1[0] - t0[0] + t_sys = t1[1] - t0[1] + print "\nIPython CPU timings (estimated):" + print "Total runs performed:", nruns + print " Times : %10.2f %10.2f" % ('Total', 'Per run') + print " User : %10.2f s, %10.2f s." % (t_usr, t_usr / nruns) + print " System : %10.2f s, %10.2f s." % (t_sys, t_sys / nruns) + twall1 = time.time() + print "Wall time: %10.2f s." % (twall1 - twall0) + + else: + # regular execution + runner(filename, prog_ns, prog_ns, exit_ignore=exit_ignore) + + if 'i' in opts: + self.shell.user_ns['__name__'] = __name__save + else: + # The shell MUST hold a reference to prog_ns so after %run + # exits, the python deletion mechanism doesn't zero it out + # (leaving dangling references). + self.shell.cache_main_mod(prog_ns, filename) + # update IPython interactive namespace + + # Some forms of read errors on the file may mean the + # __name__ key was never set; using pop we don't have to + # worry about a possible KeyError. + prog_ns.pop('__name__', None) + + self.shell.user_ns.update(prog_ns) + finally: + # It's a bit of a mystery why, but __builtins__ can change from + # being a module to becoming a dict missing some key data after + # %run. As best I can see, this is NOT something IPython is doing + # at all, and similar problems have been reported before: + # http://coding.derkeiler.com/Archive/Python/comp.lang.python/2004-10/0188.html + # Since this seems to be done by the interpreter itself, the best + # we can do is to at least restore __builtins__ for the user on + # exit. + self.shell.user_ns['__builtins__'] = builtin_mod + + # Ensure key global structures are restored + sys.argv = save_argv + if restore_main: + sys.modules['__main__'] = restore_main + else: + # Remove from sys.modules the reference to main_mod we'd + # added. Otherwise it will trap references to objects + # contained therein. + del sys.modules[main_mod_name] + + return stats + + @skip_doctest + def magic_timeit(self, parameter_s =''): + """Time execution of a Python statement or expression + + Usage:\\ + %timeit [-n -r [-t|-c]] statement + + Time execution of a Python statement or expression using the timeit + module. + + Options: + -n: execute the given statement times in a loop. If this value + is not given, a fitting value is chosen. + + -r: repeat the loop iteration times and take the best result. + Default: 3 + + -t: use time.time to measure the time, which is the default on Unix. + This function measures wall time. + + -c: use time.clock to measure the time, which is the default on + Windows and measures wall time. On Unix, resource.getrusage is used + instead and returns the CPU user time. + + -p

: use a precision of

digits to display the timing result. + Default: 3 + + + Examples + -------- + :: + + In [1]: %timeit pass + 10000000 loops, best of 3: 53.3 ns per loop + + In [2]: u = None + + In [3]: %timeit u is None + 10000000 loops, best of 3: 184 ns per loop + + In [4]: %timeit -r 4 u == None + 1000000 loops, best of 4: 242 ns per loop + + In [5]: import time + + In [6]: %timeit -n1 time.sleep(2) + 1 loops, best of 3: 2 s per loop + + + The times reported by %timeit will be slightly higher than those + reported by the timeit.py script when variables are accessed. This is + due to the fact that %timeit executes the statement in the namespace + of the shell, compared with timeit.py, which uses a single setup + statement to import function or create variables. Generally, the bias + does not matter as long as results from timeit.py are not mixed with + those from %timeit.""" + + import timeit + import math + + # XXX: Unfortunately the unicode 'micro' symbol can cause problems in + # certain terminals. Until we figure out a robust way of + # auto-detecting if the terminal can deal with it, use plain 'us' for + # microseconds. I am really NOT happy about disabling the proper + # 'micro' prefix, but crashing is worse... If anyone knows what the + # right solution for this is, I'm all ears... + # + # Note: using + # + # s = u'\xb5' + # s.encode(sys.getdefaultencoding()) + # + # is not sufficient, as I've seen terminals where that fails but + # print s + # + # succeeds + # + # See bug: https://bugs.launchpad.net/ipython/+bug/348466 + + #units = [u"s", u"ms",u'\xb5',"ns"] + units = [u"s", u"ms",u'us',"ns"] + + scaling = [1, 1e3, 1e6, 1e9] + + opts, stmt = self.parse_options(parameter_s,'n:r:tcp:', + posix=False, strict=False) + if stmt == "": + return + timefunc = timeit.default_timer + number = int(getattr(opts, "n", 0)) + repeat = int(getattr(opts, "r", timeit.default_repeat)) + precision = int(getattr(opts, "p", 3)) + if hasattr(opts, "t"): + timefunc = time.time + if hasattr(opts, "c"): + timefunc = clock + + timer = timeit.Timer(timer=timefunc) + # this code has tight coupling to the inner workings of timeit.Timer, + # but is there a better way to achieve that the code stmt has access + # to the shell namespace? + + src = timeit.template % {'stmt': timeit.reindent(stmt, 8), + 'setup': "pass"} + # Track compilation time so it can be reported if too long + # Minimum time above which compilation time will be reported + tc_min = 0.1 + + t0 = clock() + code = compile(src, "", "exec") + tc = clock()-t0 + + ns = {} + exec code in self.shell.user_ns, ns + timer.inner = ns["inner"] + + if number == 0: + # determine number so that 0.2 <= total time < 2.0 + number = 1 + for i in range(1, 10): + if timer.timeit(number) >= 0.2: + break + number *= 10 + + best = min(timer.repeat(repeat, number)) / number + + if best > 0.0 and best < 1000.0: + order = min(-int(math.floor(math.log10(best)) // 3), 3) + elif best >= 1000.0: + order = 0 + else: + order = 3 + print u"%d loops, best of %d: %.*g %s per loop" % (number, repeat, + precision, + best * scaling[order], + units[order]) + if tc > tc_min: + print "Compiler time: %.2f s" % tc + + @skip_doctest + @needs_local_scope + def magic_time(self,parameter_s, user_locals): + """Time execution of a Python statement or expression. + + The CPU and wall clock times are printed, and the value of the + expression (if any) is returned. Note that under Win32, system time + is always reported as 0, since it can not be measured. + + This function provides very basic timing functionality. In Python + 2.3, the timeit module offers more control and sophistication, so this + could be rewritten to use it (patches welcome). + + Examples + -------- + :: + + In [1]: time 2**128 + CPU times: user 0.00 s, sys: 0.00 s, total: 0.00 s + Wall time: 0.00 + Out[1]: 340282366920938463463374607431768211456L + + In [2]: n = 1000000 + + In [3]: time sum(range(n)) + CPU times: user 1.20 s, sys: 0.05 s, total: 1.25 s + Wall time: 1.37 + Out[3]: 499999500000L + + In [4]: time print 'hello world' + hello world + CPU times: user 0.00 s, sys: 0.00 s, total: 0.00 s + Wall time: 0.00 + + Note that the time needed by Python to compile the given expression + will be reported if it is more than 0.1s. In this example, the + actual exponentiation is done by Python at compilation time, so while + the expression can take a noticeable amount of time to compute, that + time is purely due to the compilation: + + In [5]: time 3**9999; + CPU times: user 0.00 s, sys: 0.00 s, total: 0.00 s + Wall time: 0.00 s + + In [6]: time 3**999999; + CPU times: user 0.00 s, sys: 0.00 s, total: 0.00 s + Wall time: 0.00 s + Compiler : 0.78 s + """ + + # fail immediately if the given expression can't be compiled + + expr = self.shell.prefilter(parameter_s,False) + + # Minimum time above which compilation time will be reported + tc_min = 0.1 + + try: + mode = 'eval' + t0 = clock() + code = compile(expr,'',mode) + tc = clock()-t0 + except SyntaxError: + mode = 'exec' + t0 = clock() + code = compile(expr,'',mode) + tc = clock()-t0 + # skew measurement as little as possible + glob = self.shell.user_ns + wtime = time.time + # time execution + wall_st = wtime() + if mode=='eval': + st = clock2() + out = eval(code, glob, user_locals) + end = clock2() + else: + st = clock2() + exec code in glob, user_locals + end = clock2() + out = None + wall_end = wtime() + # Compute actual times and report + wall_time = wall_end-wall_st + cpu_user = end[0]-st[0] + cpu_sys = end[1]-st[1] + cpu_tot = cpu_user+cpu_sys + print "CPU times: user %.2f s, sys: %.2f s, total: %.2f s" % \ + (cpu_user,cpu_sys,cpu_tot) + print "Wall time: %.2f s" % wall_time + if tc > tc_min: + print "Compiler : %.2f s" % tc + return out + + @skip_doctest + def magic_macro(self,parameter_s = ''): + """Define a macro for future re-execution. It accepts ranges of history, + filenames or string objects. + + Usage:\\ + %macro [options] name n1-n2 n3-n4 ... n5 .. n6 ... + + Options: + + -r: use 'raw' input. By default, the 'processed' history is used, + so that magics are loaded in their transformed version to valid + Python. If this option is given, the raw input as typed as the + command line is used instead. + + This will define a global variable called `name` which is a string + made of joining the slices and lines you specify (n1,n2,... numbers + above) from your input history into a single string. This variable + acts like an automatic function which re-executes those lines as if + you had typed them. You just type 'name' at the prompt and the code + executes. + + The syntax for indicating input ranges is described in %history. + + Note: as a 'hidden' feature, you can also use traditional python slice + notation, where N:M means numbers N through M-1. + + For example, if your history contains (%hist prints it):: + + 44: x=1 + 45: y=3 + 46: z=x+y + 47: print x + 48: a=5 + 49: print 'x',x,'y',y + + you can create a macro with lines 44 through 47 (included) and line 49 + called my_macro with:: + + In [55]: %macro my_macro 44-47 49 + + Now, typing `my_macro` (without quotes) will re-execute all this code + in one pass. + + You don't need to give the line-numbers in order, and any given line + number can appear multiple times. You can assemble macros with any + lines from your input history in any order. + + The macro is a simple object which holds its value in an attribute, + but IPython's display system checks for macros and executes them as + code instead of printing them when you type their name. + + You can view a macro's contents by explicitly printing it with:: + + print macro_name + + """ + opts,args = self.parse_options(parameter_s,'r',mode='list') + if not args: # List existing macros + return sorted(k for k,v in self.shell.user_ns.iteritems() if\ + isinstance(v, Macro)) + if len(args) == 1: + raise UsageError( + "%macro insufficient args; usage '%macro name n1-n2 n3-4...") + name, codefrom = args[0], " ".join(args[1:]) + + #print 'rng',ranges # dbg + try: + lines = self.shell.find_user_code(codefrom, 'r' in opts) + except (ValueError, TypeError) as e: + print e.args[0] + return + macro = Macro(lines) + self.shell.define_macro(name, macro) + print 'Macro `%s` created. To execute, type its name (without quotes).' % name + print '=== Macro contents: ===' + print macro, + + +class AutoMagics(Magics): + """Magics that control various autoX behaviors.""" + + def __init__(self, shell): + super(AutoMagics, self).__init__(shell) + # namespace for holding state we may need + self._magic_state = Bunch() + + def magic_automagic(self, parameter_s = ''): + """Make magic functions callable without having to type the initial %. + + Without argumentsl toggles on/off (when off, you must call it as + %automagic, of course). With arguments it sets the value, and you can + use any of (case insensitive): + + - on,1,True: to activate + + - off,0,False: to deactivate. + + Note that magic functions have lowest priority, so if there's a + variable whose name collides with that of a magic fn, automagic won't + work for that function (you get the variable instead). However, if you + delete the variable (del var), the previously shadowed magic function + becomes visible to automagic again.""" + + arg = parameter_s.lower() + if arg in ('on','1','true'): + self.shell.automagic = True + elif arg in ('off','0','false'): + self.shell.automagic = False + else: + self.shell.automagic = not self.shell.automagic + print '\n' + Magic.auto_status[self.shell.automagic] + + @skip_doctest + def magic_autocall(self, parameter_s = ''): + """Make functions callable without having to type parentheses. + + Usage: + + %autocall [mode] + + The mode can be one of: 0->Off, 1->Smart, 2->Full. If not given, the + value is toggled on and off (remembering the previous state). + + In more detail, these values mean: + + 0 -> fully disabled + + 1 -> active, but do not apply if there are no arguments on the line. + + In this mode, you get:: + + In [1]: callable + Out[1]: + + In [2]: callable 'hello' + ------> callable('hello') + Out[2]: False + + 2 -> Active always. Even if no arguments are present, the callable + object is called:: + + In [2]: float + ------> float() + Out[2]: 0.0 + + Note that even with autocall off, you can still use '/' at the start of + a line to treat the first argument on the command line as a function + and add parentheses to it:: + + In [8]: /str 43 + ------> str(43) + Out[8]: '43' + + # all-random (note for auto-testing) + """ + + if parameter_s: + arg = int(parameter_s) + else: + arg = 'toggle' + + if not arg in (0,1,2,'toggle'): + error('Valid modes: (0->Off, 1->Smart, 2->Full') + return + + if arg in (0,1,2): + self.shell.autocall = arg + else: # toggle + if self.shell.autocall: + self._magic_state.autocall_save = self.shell.autocall + self.shell.autocall = 0 + else: + try: + self.shell.autocall = self._magic_state.autocall_save + except AttributeError: + self.shell.autocall = self._magic_state.autocall_save = 1 + + print "Automatic calling is:",['OFF','Smart','Full'][self.shell.autocall] + + +class OSMagics(Magics): + """Magics to interact with the underlying OS (shell-type functionality). + """ + + @skip_doctest + def magic_alias(self, parameter_s = ''): + """Define an alias for a system command. + + '%alias alias_name cmd' defines 'alias_name' as an alias for 'cmd' + + Then, typing 'alias_name params' will execute the system command 'cmd + params' (from your underlying operating system). + + Aliases have lower precedence than magic functions and Python normal + variables, so if 'foo' is both a Python variable and an alias, the + alias can not be executed until 'del foo' removes the Python variable. + + You can use the %l specifier in an alias definition to represent the + whole line when the alias is called. For example:: + + In [2]: alias bracket echo "Input in brackets: <%l>" + In [3]: bracket hello world + Input in brackets: + + You can also define aliases with parameters using %s specifiers (one + per parameter):: + + In [1]: alias parts echo first %s second %s + In [2]: %parts A B + first A second B + In [3]: %parts A + Incorrect number of arguments: 2 expected. + parts is an alias to: 'echo first %s second %s' + + Note that %l and %s are mutually exclusive. You can only use one or + the other in your aliases. + + Aliases expand Python variables just like system calls using ! or !! + do: all expressions prefixed with '$' get expanded. For details of + the semantic rules, see PEP-215: + http://www.python.org/peps/pep-0215.html. This is the library used by + IPython for variable expansion. If you want to access a true shell + variable, an extra $ is necessary to prevent its expansion by + IPython:: + + In [6]: alias show echo + In [7]: PATH='A Python string' + In [8]: show $PATH + A Python string + In [9]: show $$PATH + /usr/local/lf9560/bin:/usr/local/intel/compiler70/ia32/bin:... + + You can use the alias facility to acess all of $PATH. See the %rehash + and %rehashx functions, which automatically create aliases for the + contents of your $PATH. + + If called with no parameters, %alias prints the current alias table.""" + + par = parameter_s.strip() + if not par: + aliases = sorted(self.shell.alias_manager.aliases) + # stored = self.shell.db.get('stored_aliases', {} ) + # for k, v in stored: + # atab.append(k, v[0]) + + print "Total number of aliases:", len(aliases) + sys.stdout.flush() + return aliases + + # Now try to define a new one + try: + alias,cmd = par.split(None, 1) + except: + print oinspect.getdoc(self.magic_alias) + else: + self.shell.alias_manager.soft_define_alias(alias, cmd) + # end magic_alias + + def magic_unalias(self, parameter_s = ''): + """Remove an alias""" + + aname = parameter_s.strip() + self.shell.alias_manager.undefine_alias(aname) + stored = self.shell.db.get('stored_aliases', {} ) + if aname in stored: + print "Removing %stored alias",aname + del stored[aname] + self.shell.db['stored_aliases'] = stored + + def magic_rehashx(self, parameter_s = ''): + """Update the alias table with all executable files in $PATH. + + This version explicitly checks that every entry in $PATH is a file + with execute access (os.X_OK), so it is much slower than %rehash. + + Under Windows, it checks executability as a match against a + '|'-separated string of extensions, stored in the IPython config + variable win_exec_ext. This defaults to 'exe|com|bat'. + + This function also resets the root module cache of module completer, + used on slow filesystems. + """ + from IPython.core.alias import InvalidAliasError + + # for the benefit of module completer in ipy_completers.py + del self.shell.db['rootmodules'] + + path = [os.path.abspath(os.path.expanduser(p)) for p in + os.environ.get('PATH','').split(os.pathsep)] + path = filter(os.path.isdir,path) + + syscmdlist = [] + # Now define isexec in a cross platform manner. + if os.name == 'posix': + isexec = lambda fname:os.path.isfile(fname) and \ + os.access(fname,os.X_OK) + else: + try: + winext = os.environ['pathext'].replace(';','|').replace('.','') + except KeyError: + winext = 'exe|com|bat|py' + if 'py' not in winext: + winext += '|py' + execre = re.compile(r'(.*)\.(%s)$' % winext,re.IGNORECASE) + isexec = lambda fname:os.path.isfile(fname) and execre.match(fname) + savedir = os.getcwdu() + + # Now walk the paths looking for executables to alias. + try: + # write the whole loop for posix/Windows so we don't have an if in + # the innermost part + if os.name == 'posix': + for pdir in path: + os.chdir(pdir) + for ff in os.listdir(pdir): + if isexec(ff): + try: + # Removes dots from the name since ipython + # will assume names with dots to be python. + self.shell.alias_manager.define_alias( + ff.replace('.',''), ff) + except InvalidAliasError: + pass + else: + syscmdlist.append(ff) + else: + no_alias = self.shell.alias_manager.no_alias + for pdir in path: + os.chdir(pdir) + for ff in os.listdir(pdir): + base, ext = os.path.splitext(ff) + if isexec(ff) and base.lower() not in no_alias: + if ext.lower() == '.exe': + ff = base + try: + # Removes dots from the name since ipython + # will assume names with dots to be python. + self.shell.alias_manager.define_alias( + base.lower().replace('.',''), ff) + except InvalidAliasError: + pass + syscmdlist.append(ff) + self.shell.db['syscmdlist'] = syscmdlist + finally: + os.chdir(savedir) + + @skip_doctest + def magic_pwd(self, parameter_s = ''): + """Return the current working directory path. + + Examples + -------- + :: + + In [9]: pwd + Out[9]: '/home/tsuser/sprint/ipython' + """ + return os.getcwdu() + + @skip_doctest + def magic_cd(self, parameter_s=''): + """Change the current working directory. + + This command automatically maintains an internal list of directories + you visit during your IPython session, in the variable _dh. The + command %dhist shows this history nicely formatted. You can also + do 'cd -' to see directory history conveniently. + + Usage: + + cd 'dir': changes to directory 'dir'. + + cd -: changes to the last visited directory. + + cd -: changes to the n-th directory in the directory history. + + cd --foo: change to directory that matches 'foo' in history + + cd -b : jump to a bookmark set by %bookmark + (note: cd is enough if there is no + directory , but a bookmark with the name exists.) + 'cd -b ' allows you to tab-complete bookmark names. + + Options: + + -q: quiet. Do not print the working directory after the cd command is + executed. By default IPython's cd command does print this directory, + since the default prompts do not display path information. + + Note that !cd doesn't work for this purpose because the shell where + !command runs is immediately discarded after executing 'command'. + + Examples + -------- + :: + + In [10]: cd parent/child + /home/tsuser/parent/child + """ + + parameter_s = parameter_s.strip() + #bkms = self.shell.persist.get("bookmarks",{}) + + oldcwd = os.getcwdu() + numcd = re.match(r'(-)(\d+)$',parameter_s) + # jump in directory history by number + if numcd: + nn = int(numcd.group(2)) + try: + ps = self.shell.user_ns['_dh'][nn] + except IndexError: + print 'The requested directory does not exist in history.' + return + else: + opts = {} + elif parameter_s.startswith('--'): + ps = None + fallback = None + pat = parameter_s[2:] + dh = self.shell.user_ns['_dh'] + # first search only by basename (last component) + for ent in reversed(dh): + if pat in os.path.basename(ent) and os.path.isdir(ent): + ps = ent + break + + if fallback is None and pat in ent and os.path.isdir(ent): + fallback = ent + + # if we have no last part match, pick the first full path match + if ps is None: + ps = fallback + + if ps is None: + print "No matching entry in directory history" + return + else: + opts = {} + + + else: + #turn all non-space-escaping backslashes to slashes, + # for c:\windows\directory\names\ + parameter_s = re.sub(r'\\(?! )','/', parameter_s) + opts,ps = self.parse_options(parameter_s,'qb',mode='string') + # jump to previous + if ps == '-': + try: + ps = self.shell.user_ns['_dh'][-2] + except IndexError: + raise UsageError('%cd -: No previous directory to change to.') + # jump to bookmark if needed + else: + if not os.path.isdir(ps) or opts.has_key('b'): + bkms = self.shell.db.get('bookmarks', {}) + + if bkms.has_key(ps): + target = bkms[ps] + print '(bookmark:%s) -> %s' % (ps,target) + ps = target + else: + if opts.has_key('b'): + raise UsageError("Bookmark '%s' not found. " + "Use '%%bookmark -l' to see your bookmarks." % ps) + + # strip extra quotes on Windows, because os.chdir doesn't like them + ps = unquote_filename(ps) + # at this point ps should point to the target dir + if ps: + try: + os.chdir(os.path.expanduser(ps)) + if hasattr(self.shell, 'term_title') and self.shell.term_title: + set_term_title('IPython: ' + abbrev_cwd()) + except OSError: + print sys.exc_info()[1] + else: + cwd = os.getcwdu() + dhist = self.shell.user_ns['_dh'] + if oldcwd != cwd: + dhist.append(cwd) + self.shell.db['dhist'] = compress_dhist(dhist)[-100:] + + else: + os.chdir(self.shell.home_dir) + if hasattr(self.shell, 'term_title') and self.shell.term_title: + set_term_title('IPython: ' + '~') + cwd = os.getcwdu() + dhist = self.shell.user_ns['_dh'] + + if oldcwd != cwd: + dhist.append(cwd) + self.shell.db['dhist'] = compress_dhist(dhist)[-100:] + if not 'q' in opts and self.shell.user_ns['_dh']: + print self.shell.user_ns['_dh'][-1] + + + def magic_env(self, parameter_s=''): + """List environment variables.""" + + return dict(os.environ) + + def magic_pushd(self, parameter_s=''): + """Place the current dir on stack and change directory. + + Usage:\\ + %pushd ['dirname'] + """ + + dir_s = self.shell.dir_stack + tgt = os.path.expanduser(unquote_filename(parameter_s)) + cwd = os.getcwdu().replace(self.shell.home_dir,'~') + if tgt: + self.magic_cd(parameter_s) + dir_s.insert(0,cwd) + return self.shell.magic('dirs') + + def magic_popd(self, parameter_s=''): + """Change to directory popped off the top of the stack. + """ + if not self.shell.dir_stack: + raise UsageError("%popd on empty stack") + top = self.shell.dir_stack.pop(0) + self.magic_cd(top) + print "popd ->",top + + def magic_dirs(self, parameter_s=''): + """Return the current directory stack.""" + + return self.shell.dir_stack + + def magic_dhist(self, parameter_s=''): + """Print your history of visited directories. + + %dhist -> print full history\\ + %dhist n -> print last n entries only\\ + %dhist n1 n2 -> print entries between n1 and n2 (n1 not included)\\ + + This history is automatically maintained by the %cd command, and + always available as the global list variable _dh. You can use %cd - + to go to directory number . + + Note that most of time, you should view directory history by entering + cd -. + + """ + + dh = self.shell.user_ns['_dh'] + if parameter_s: + try: + args = map(int,parameter_s.split()) + except: + self.arg_err(Magic.magic_dhist) + return + if len(args) == 1: + ini,fin = max(len(dh)-(args[0]),0),len(dh) + elif len(args) == 2: + ini,fin = args + else: + self.arg_err(Magic.magic_dhist) + return + else: + ini,fin = 0,len(dh) + nlprint(dh, + header = 'Directory history (kept in _dh)', + start=ini,stop=fin) + + @skip_doctest + def magic_sc(self, parameter_s=''): + """Shell capture - execute a shell command and capture its output. + + DEPRECATED. Suboptimal, retained for backwards compatibility. + + You should use the form 'var = !command' instead. Example: + + "%sc -l myfiles = ls ~" should now be written as + + "myfiles = !ls ~" + + myfiles.s, myfiles.l and myfiles.n still apply as documented + below. + + -- + %sc [options] varname=command + + IPython will run the given command using commands.getoutput(), and + will then update the user's interactive namespace with a variable + called varname, containing the value of the call. Your command can + contain shell wildcards, pipes, etc. + + The '=' sign in the syntax is mandatory, and the variable name you + supply must follow Python's standard conventions for valid names. + + (A special format without variable name exists for internal use) + + Options: + + -l: list output. Split the output on newlines into a list before + assigning it to the given variable. By default the output is stored + as a single string. + + -v: verbose. Print the contents of the variable. + + In most cases you should not need to split as a list, because the + returned value is a special type of string which can automatically + provide its contents either as a list (split on newlines) or as a + space-separated string. These are convenient, respectively, either + for sequential processing or to be passed to a shell command. + + For example:: + + # Capture into variable a + In [1]: sc a=ls *py + + # a is a string with embedded newlines + In [2]: a + Out[2]: 'setup.py\\nwin32_manual_post_install.py' + + # which can be seen as a list: + In [3]: a.l + Out[3]: ['setup.py', 'win32_manual_post_install.py'] + + # or as a whitespace-separated string: + In [4]: a.s + Out[4]: 'setup.py win32_manual_post_install.py' + + # a.s is useful to pass as a single command line: + In [5]: !wc -l $a.s + 146 setup.py + 130 win32_manual_post_install.py + 276 total + + # while the list form is useful to loop over: + In [6]: for f in a.l: + ...: !wc -l $f + ...: + 146 setup.py + 130 win32_manual_post_install.py + + Similarly, the lists returned by the -l option are also special, in + the sense that you can equally invoke the .s attribute on them to + automatically get a whitespace-separated string from their contents:: + + In [7]: sc -l b=ls *py + + In [8]: b + Out[8]: ['setup.py', 'win32_manual_post_install.py'] + + In [9]: b.s + Out[9]: 'setup.py win32_manual_post_install.py' + + In summary, both the lists and strings used for output capture have + the following special attributes:: + + .l (or .list) : value as list. + .n (or .nlstr): value as newline-separated string. + .s (or .spstr): value as space-separated string. + """ + + opts,args = self.parse_options(parameter_s,'lv') + # Try to get a variable name and command to run + try: + # the variable name must be obtained from the parse_options + # output, which uses shlex.split to strip options out. + var,_ = args.split('=',1) + var = var.strip() + # But the command has to be extracted from the original input + # parameter_s, not on what parse_options returns, to avoid the + # quote stripping which shlex.split performs on it. + _,cmd = parameter_s.split('=',1) + except ValueError: + var,cmd = '','' + # If all looks ok, proceed + split = 'l' in opts + out = self.shell.getoutput(cmd, split=split) + if opts.has_key('v'): + print '%s ==\n%s' % (var,pformat(out)) + if var: + self.shell.user_ns.update({var:out}) + else: + return out + + def magic_sx(self, parameter_s=''): + """Shell execute - run a shell command and capture its output. + + %sx command + + IPython will run the given command using commands.getoutput(), and + return the result formatted as a list (split on '\\n'). Since the + output is _returned_, it will be stored in ipython's regular output + cache Out[N] and in the '_N' automatic variables. + + Notes: + + 1) If an input line begins with '!!', then %sx is automatically + invoked. That is, while:: + + !ls + + causes ipython to simply issue system('ls'), typing:: + + !!ls + + is a shorthand equivalent to:: + + %sx ls + + 2) %sx differs from %sc in that %sx automatically splits into a list, + like '%sc -l'. The reason for this is to make it as easy as possible + to process line-oriented shell output via further python commands. + %sc is meant to provide much finer control, but requires more + typing. + + 3) Just like %sc -l, this is a list with special attributes: + :: + + .l (or .list) : value as list. + .n (or .nlstr): value as newline-separated string. + .s (or .spstr): value as whitespace-separated string. + + This is very useful when trying to use such lists as arguments to + system commands.""" + + if parameter_s: + return self.shell.getoutput(parameter_s) + + + def magic_bookmark(self, parameter_s=''): + """Manage IPython's bookmark system. + + %bookmark - set bookmark to current dir + %bookmark

- set bookmark to + %bookmark -l - list all bookmarks + %bookmark -d - remove bookmark + %bookmark -r - remove all bookmarks + + You can later on access a bookmarked folder with:: + + %cd -b + + or simply '%cd ' if there is no directory called AND + there is such a bookmark defined. + + Your bookmarks persist through IPython sessions, but they are + associated with each profile.""" + + opts,args = self.parse_options(parameter_s,'drl',mode='list') + if len(args) > 2: + raise UsageError("%bookmark: too many arguments") + + bkms = self.shell.db.get('bookmarks',{}) + + if opts.has_key('d'): + try: + todel = args[0] + except IndexError: + raise UsageError( + "%bookmark -d: must provide a bookmark to delete") + else: + try: + del bkms[todel] + except KeyError: + raise UsageError( + "%%bookmark -d: Can't delete bookmark '%s'" % todel) + + elif opts.has_key('r'): + bkms = {} + elif opts.has_key('l'): + bks = bkms.keys() + bks.sort() + if bks: + size = max(map(len,bks)) + else: + size = 0 + fmt = '%-'+str(size)+'s -> %s' + print 'Current bookmarks:' + for bk in bks: + print fmt % (bk,bkms[bk]) + else: + if not args: + raise UsageError("%bookmark: You must specify the bookmark name") + elif len(args)==1: + bkms[args[0]] = os.getcwdu() + elif len(args)==2: + bkms[args[0]] = args[1] + self.shell.db['bookmarks'] = bkms + + def magic_pycat(self, parameter_s=''): + """Show a syntax-highlighted file through a pager. + + This magic is similar to the cat utility, but it will assume the file + to be Python source and will show it with syntax highlighting. """ + + try: + filename = get_py_filename(parameter_s) + cont = file_read(filename) + except IOError: + try: + cont = eval(parameter_s, self.shell.user_ns) + except NameError: + cont = None + if cont is None: + print "Error: no such file or variable" + return + + page.page(self.shell.pycolorize(cont)) + + +class LoggingMagics(Magics): + """Magics related to all logging machinery.""" + def magic_logstart(self,parameter_s=''): + """Start logging anywhere in a session. + + %logstart [-o|-r|-t] [log_name [log_mode]] + + If no name is given, it defaults to a file named 'ipython_log.py' in your + current directory, in 'rotate' mode (see below). + + '%logstart name' saves to file 'name' in 'backup' mode. It saves your + history up to that point and then continues logging. + + %logstart takes a second optional parameter: logging mode. This can be one + of (note that the modes are given unquoted):\\ + append: well, that says it.\\ + backup: rename (if exists) to name~ and start name.\\ + global: single logfile in your home dir, appended to.\\ + over : overwrite existing log.\\ + rotate: create rotating logs name.1~, name.2~, etc. + + Options: + + -o: log also IPython's output. In this mode, all commands which + generate an Out[NN] prompt are recorded to the logfile, right after + their corresponding input line. The output lines are always + prepended with a '#[Out]# ' marker, so that the log remains valid + Python code. + + Since this marker is always the same, filtering only the output from + a log is very easy, using for example a simple awk call:: + + awk -F'#\\[Out\\]# ' '{if($2) {print $2}}' ipython_log.py + + -r: log 'raw' input. Normally, IPython's logs contain the processed + input, so that user lines are logged in their final form, converted + into valid Python. For example, %Exit is logged as + _ip.magic("Exit"). If the -r flag is given, all input is logged + exactly as typed, with no transformations applied. + + -t: put timestamps before each input line logged (these are put in + comments).""" + + opts,par = self.parse_options(parameter_s,'ort') + log_output = 'o' in opts + log_raw_input = 'r' in opts + timestamp = 't' in opts + + logger = self.shell.logger + + # if no args are given, the defaults set in the logger constructor by + # ipython remain valid + if par: + try: + logfname,logmode = par.split() + except: + logfname = par + logmode = 'backup' + else: + logfname = logger.logfname + logmode = logger.logmode + # put logfname into rc struct as if it had been called on the command + # line, so it ends up saved in the log header Save it in case we need + # to restore it... + old_logfile = self.shell.logfile + if logfname: + logfname = os.path.expanduser(logfname) + self.shell.logfile = logfname + + loghead = '# IPython log file\n\n' + try: + logger.logstart(logfname, loghead, logmode, log_output, timestamp, + log_raw_input) + except: + self.shell.logfile = old_logfile + warn("Couldn't start log: %s" % sys.exc_info()[1]) + else: + # log input history up to this point, optionally interleaving + # output if requested + + if timestamp: + # disable timestamping for the previous history, since we've + # lost those already (no time machine here). + logger.timestamp = False + + if log_raw_input: + input_hist = self.shell.history_manager.input_hist_raw + else: + input_hist = self.shell.history_manager.input_hist_parsed + + if log_output: + log_write = logger.log_write + output_hist = self.shell.history_manager.output_hist + for n in range(1,len(input_hist)-1): + log_write(input_hist[n].rstrip() + '\n') + if n in output_hist: + log_write(repr(output_hist[n]),'output') + else: + logger.log_write('\n'.join(input_hist[1:])) + logger.log_write('\n') + if timestamp: + # re-enable timestamping + logger.timestamp = True + + print ('Activating auto-logging. ' + 'Current session state plus future input saved.') + logger.logstate() + + def magic_logstop(self,parameter_s=''): + """Fully stop logging and close log file. + + In order to start logging again, a new %logstart call needs to be made, + possibly (though not necessarily) with a new filename, mode and other + options.""" + self.logger.logstop() + + def magic_logoff(self,parameter_s=''): + """Temporarily stop logging. + + You must have previously started logging.""" + self.shell.logger.switch_log(0) + + def magic_logon(self,parameter_s=''): + """Restart logging. + + This function is for restarting logging which you've temporarily + stopped with %logoff. For starting logging for the first time, you + must use the %logstart function, which allows you to specify an + optional log filename.""" + + self.shell.logger.switch_log(1) + + def magic_logstate(self,parameter_s=''): + """Print the status of the logging system.""" + + self.shell.logger.logstate() + +class ExtensionsMagics(Magics): + """Magics to manage the IPython extensions system.""" + def magic_install_ext(self, parameter_s): + """Download and install an extension from a URL, e.g.:: + + %install_ext https://bitbucket.org/birkenfeld/ipython-physics/raw/d1310a2ab15d/physics.py + + The URL should point to an importable Python module - either a .py file + or a .zip file. + + Parameters: + + -n filename : Specify a name for the file, rather than taking it from + the URL. + """ + opts, args = self.parse_options(parameter_s, 'n:') + try: + filename = self.shell.extension_manager.install_extension(args, + opts.get('n')) + except ValueError as e: + print e + return + + filename = os.path.basename(filename) + print "Installed %s. To use it, type:" % filename + print " %%load_ext %s" % os.path.splitext(filename)[0] + + + def magic_load_ext(self, module_str): + """Load an IPython extension by its module name.""" + return self.shell.extension_manager.load_extension(module_str) + + def magic_unload_ext(self, module_str): + """Unload an IPython extension by its module name.""" + self.shell.extension_manager.unload_extension(module_str) + + def magic_reload_ext(self, module_str): + """Reload an IPython extension by its module name.""" + self.shell.extension_manager.reload_extension(module_str) + + +class DeprecatedMagics(Magics): + """Magics slated for later removal.""" + def magic_install_profiles(self, s): + """%install_profiles has been deprecated.""" + print '\n'.join([ + "%install_profiles has been deprecated.", + "Use `ipython profile list` to view available profiles.", + "Requesting a profile with `ipython profile create `", + "or `ipython --profile=` will start with the bundled", + "profile of that name if it exists." + ]) + + def magic_install_default_config(self, s): + """%install_default_config has been deprecated.""" + print '\n'.join([ + "%install_default_config has been deprecated.", + "Use `ipython profile create ` to initialize a profile", + "with the default config files.", + "Add `--reset` to overwrite already existing config files with defaults." + ]) + + +class PylabMagics(Magics): + """Magics related to matplotlib's pylab support""" + + @skip_doctest + def magic_pylab(self, s): + """Load numpy and matplotlib to work interactively. + + %pylab [GUINAME] + + This function lets you activate pylab (matplotlib, numpy and + interactive support) at any point during an IPython session. + + It will import at the top level numpy as np, pyplot as plt, matplotlib, + pylab and mlab, as well as all names from numpy and pylab. + + If you are using the inline matplotlib backend for embedded figures, + you can adjust its behavior via the %config magic:: + + # enable SVG figures, necessary for SVG+XHTML export in the qtconsole + In [1]: %config InlineBackend.figure_format = 'svg' + + # change the behavior of closing all figures at the end of each + # execution (cell), or allowing reuse of active figures across + # cells: + In [2]: %config InlineBackend.close_figures = False + + Parameters + ---------- + guiname : optional + One of the valid arguments to the %gui magic ('qt', 'wx', 'gtk', + 'osx' or 'tk'). If given, the corresponding Matplotlib backend is + used, otherwise matplotlib's default (which you can override in your + matplotlib config file) is used. + + Examples + -------- + In this case, where the MPL default is TkAgg:: + + In [2]: %pylab + + Welcome to pylab, a matplotlib-based Python environment. + Backend in use: TkAgg + For more information, type 'help(pylab)'. + + But you can explicitly request a different backend:: + + In [3]: %pylab qt + + Welcome to pylab, a matplotlib-based Python environment. + Backend in use: Qt4Agg + For more information, type 'help(pylab)'. + """ + + if Application.initialized(): + app = Application.instance() + try: + import_all_status = app.pylab_import_all + except AttributeError: + import_all_status = True + else: + import_all_status = True + + self.shell.enable_pylab(s, import_all=import_all_status) From ebb8b705defec59f48c0be88c18cf32caf94a10c Mon Sep 17 00:00:00 2001 From: Fernando Perez Date: Sun, 13 May 2012 16:06:47 -0700 Subject: [PATCH 015/103] Update decorator module in externals to version 3.3.3 (upstream). --- IPython/external/decorator/_decorator.py | 222 ++++++++++------------- 1 file changed, 94 insertions(+), 128 deletions(-) diff --git a/IPython/external/decorator/_decorator.py b/IPython/external/decorator/_decorator.py index 2791c1d5430..2751ab145b3 100644 --- a/IPython/external/decorator/_decorator.py +++ b/IPython/external/decorator/_decorator.py @@ -1,37 +1,43 @@ ########################## LICENCE ############################### -## -## Copyright (c) 2005, Michele Simionato -## All rights reserved. -## -## Redistributions of source code must retain the above copyright -## notice, this list of conditions and the following disclaimer. -## Redistributions in bytecode form must reproduce the above copyright -## notice, this list of conditions and the following disclaimer in -## the documentation and/or other materials provided with the -## distribution. - -## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -## "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -## LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -## A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -## HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, -## INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, -## BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS -## OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -## ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR -## TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE -## USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH -## DAMAGE. + +# Copyright (c) 2005-2012, Michele Simionato +# All rights reserved. + +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: + +# Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# Redistributions in bytecode form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in +# the documentation and/or other materials provided with the +# distribution. + +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS +# OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR +# TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +# USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH +# DAMAGE. """ Decorator module, see http://pypi.python.org/pypi/decorator for the documentation. """ -__all__ = ["decorator", "FunctionMaker", "partial", - "deprecated", "getinfo", "new_wrapper"] +__version__ = '3.3.3' + +__all__ = ["decorator", "FunctionMaker", "partial"] + +import sys, re, inspect -import os, sys, re, inspect, string, warnings try: from functools import partial except ImportError: # for Python version < 2.5 @@ -46,6 +52,22 @@ def __call__(self, *otherargs, **otherkw): kw.update(otherkw) return self.func(*(self.args + otherargs), **kw) +if sys.version >= '3': + from inspect import getfullargspec +else: + class getfullargspec(object): + "A quick and dirty replacement for getfullargspec for Python 2.X" + def __init__(self, f): + self.args, self.varargs, self.varkw, self.defaults = \ + inspect.getargspec(f) + self.kwonlyargs = [] + self.kwonlydefaults = None + def __iter__(self): + yield self.args + yield self.varargs + yield self.varkw + yield self.defaults + DEF = re.compile('\s*def\s*([_\w][_\w\d]*)\s*\(') # basic functionality @@ -57,6 +79,7 @@ class FunctionMaker(object): """ def __init__(self, func=None, name=None, signature=None, defaults=None, doc=None, module=None, funcdict=None): + self.shortsignature = signature if func: # func can be a class or a callable, but not an instance method self.name = func.__name__ @@ -65,13 +88,31 @@ def __init__(self, func=None, name=None, signature=None, self.doc = func.__doc__ self.module = func.__module__ if inspect.isfunction(func): - argspec = inspect.getargspec(func) - self.args, self.varargs, self.keywords, self.defaults = argspec + argspec = getfullargspec(func) + self.annotations = getattr(func, '__annotations__', {}) + for a in ('args', 'varargs', 'varkw', 'defaults', 'kwonlyargs', + 'kwonlydefaults'): + setattr(self, a, getattr(argspec, a)) for i, arg in enumerate(self.args): setattr(self, 'arg%d' % i, arg) - self.signature = inspect.formatargspec( - formatvalue=lambda val: "", *argspec)[1:-1] + if sys.version < '3': # easy way + self.shortsignature = self.signature = \ + inspect.formatargspec( + formatvalue=lambda val: "", *argspec)[1:-1] + else: # Python 3 way + self.signature = self.shortsignature = ', '.join(self.args) + if self.varargs: + self.signature += ', *' + self.varargs + self.shortsignature += ', *' + self.varargs + if self.kwonlyargs: + for a in self.kwonlyargs: + self.signature += ', %s=None' % a + self.shortsignature += ', %s=%s' % (a, a) + if self.varkw: + self.signature += ', **' + self.varkw + self.shortsignature += ', **' + self.varkw self.dict = func.__dict__.copy() + # func=None happens when decorating a caller if name: self.name = name if signature is not None: @@ -95,6 +136,8 @@ def update(self, func, **kw): func.__doc__ = getattr(self, 'doc', None) func.__dict__ = getattr(self, 'dict', {}) func.func_defaults = getattr(self, 'defaults', ()) + func.__kwdefaults__ = getattr(self, 'kwonlydefaults', None) + func.__annotations__ = getattr(self, 'annotations', None) callermodule = sys._getframe(3).f_globals.get('__name__', '?') func.__module__ = getattr(self, 'module', callermodule) func.__dict__.update(kw) @@ -107,15 +150,16 @@ def make(self, src_templ, evaldict=None, addsource=False, **attrs): if mo is None: raise SyntaxError('not a valid function template\n%s' % src) name = mo.group(1) # extract the function name - reserved_names = set([name] + [ - arg.strip(' *') for arg in self.signature.split(',')]) - for n, v in evaldict.iteritems(): - if n in reserved_names: + names = set([name] + [arg.strip(' *') for arg in + self.shortsignature.split(',')]) + for n in names: + if n in ('_func_', '_call_'): raise NameError('%s is overridden in\n%s' % (n, src)) if not src.endswith('\n'): # add a newline just for safety - src += '\n' + src += '\n' # this is needed in old versions of Python try: code = compile(src, '', 'single') + # print >> sys.stderr, 'Compiling %s' % src exec code in evaldict except: print >> sys.stderr, 'Error in generated code:' @@ -129,7 +173,7 @@ def make(self, src_templ, evaldict=None, addsource=False, **attrs): @classmethod def create(cls, obj, body, evaldict, defaults=None, - doc=None, module=None, addsource=True,**attrs): + doc=None, module=None, addsource=True, **attrs): """ Create a function from the strings name, signature and body. evaldict is the evaluation dictionary. If addsource is true an attribute @@ -144,9 +188,9 @@ def create(cls, obj, body, evaldict, defaults=None, name = None signature = None func = obj - fun = cls(func, name, signature, defaults, doc, module) + self = cls(func, name, signature, defaults, doc, module) ibody = '\n'.join(' ' + line for line in body.splitlines()) - return fun.make('def %(name)s(%(signature)s):\n' + ibody, + return self.make('def %(name)s(%(signature)s):\n' + ibody, evaldict, addsource, **attrs) def decorator(caller, func=None): @@ -155,100 +199,22 @@ def decorator(caller, func=None): decorator(caller, func) decorates a function using a caller. """ if func is not None: # returns a decorated function + evaldict = func.func_globals.copy() + evaldict['_call_'] = caller + evaldict['_func_'] = func return FunctionMaker.create( - func, "return _call_(_func_, %(signature)s)", - dict(_call_=caller, _func_=func), undecorated=func) + func, "return _call_(_func_, %(shortsignature)s)", + evaldict, undecorated=func, __wrapped__=func) else: # returns a decorator if isinstance(caller, partial): return partial(decorator, caller) # otherwise assume caller is a function - f = inspect.getargspec(caller)[0][0] # first arg + first = inspect.getargspec(caller)[0][0] # first arg + evaldict = caller.func_globals.copy() + evaldict['_call_'] = caller + evaldict['decorator'] = decorator return FunctionMaker.create( - '%s(%s)' % (caller.__name__, f), - 'return decorator(_call_, %s)' % f, - dict(_call_=caller, decorator=decorator), undecorated=caller, + '%s(%s)' % (caller.__name__, first), + 'return decorator(_call_, %s)' % first, + evaldict, undecorated=caller, __wrapped__=caller, doc=caller.__doc__, module=caller.__module__) - -###################### deprecated functionality ######################### - -@decorator -def deprecated(func, *args, **kw): - "A decorator for deprecated functions" - warnings.warn( - ('Calling the deprecated function %r\n' - 'Downgrade to decorator 2.3 if you want to use this functionality') - % func.__name__, DeprecationWarning, stacklevel=3) - return func(*args, **kw) - -@deprecated -def getinfo(func): - """ - Returns an info dictionary containing: - - name (the name of the function : str) - - argnames (the names of the arguments : list) - - defaults (the values of the default arguments : tuple) - - signature (the signature : str) - - doc (the docstring : str) - - module (the module name : str) - - dict (the function __dict__ : str) - - >>> def f(self, x=1, y=2, *args, **kw): pass - - >>> info = getinfo(f) - - >>> info["name"] - 'f' - >>> info["argnames"] - ['self', 'x', 'y', 'args', 'kw'] - - >>> info["defaults"] - (1, 2) - - >>> info["signature"] - 'self, x, y, *args, **kw' - """ - assert inspect.ismethod(func) or inspect.isfunction(func) - regargs, varargs, varkwargs, defaults = inspect.getargspec(func) - argnames = list(regargs) - if varargs: - argnames.append(varargs) - if varkwargs: - argnames.append(varkwargs) - signature = inspect.formatargspec(regargs, varargs, varkwargs, defaults, - formatvalue=lambda value: "")[1:-1] - return dict(name=func.__name__, argnames=argnames, signature=signature, - defaults = func.func_defaults, doc=func.__doc__, - module=func.__module__, dict=func.__dict__, - globals=func.func_globals, closure=func.func_closure) - -@deprecated -def update_wrapper(wrapper, model, infodict=None): - "A replacement for functools.update_wrapper" - infodict = infodict or getinfo(model) - wrapper.__name__ = infodict['name'] - wrapper.__doc__ = infodict['doc'] - wrapper.__module__ = infodict['module'] - wrapper.__dict__.update(infodict['dict']) - wrapper.func_defaults = infodict['defaults'] - wrapper.undecorated = model - return wrapper - -@deprecated -def new_wrapper(wrapper, model): - """ - An improvement over functools.update_wrapper. The wrapper is a generic - callable object. It works by generating a copy of the wrapper with the - right signature and by updating the copy, not the original. - Moreovoer, 'model' can be a dictionary with keys 'name', 'doc', 'module', - 'dict', 'defaults'. - """ - if isinstance(model, dict): - infodict = model - else: # assume model is a function - infodict = getinfo(model) - assert not '_wrapper_' in infodict["argnames"], ( - '"_wrapper_" is a reserved argument name!') - src = "lambda %(signature)s: _wrapper_(%(signature)s)" % infodict - funcopy = eval(src, dict(_wrapper_=wrapper)) - return update_wrapper(funcopy, model, infodict) - From 2cba502c6f2b231f9ad7b2519326bf0a7eb8cf24 Mon Sep 17 00:00:00 2001 From: Fernando Perez Date: Sun, 13 May 2012 17:48:19 -0700 Subject: [PATCH 016/103] Implement magic registration and listing in magics manager. --- IPython/core/magic.py | 50 ++++++++++++++++++++++--------------------- 1 file changed, 26 insertions(+), 24 deletions(-) diff --git a/IPython/core/magic.py b/IPython/core/magic.py index 62943a3d369..754634feb9e 100644 --- a/IPython/core/magic.py +++ b/IPython/core/magic.py @@ -142,6 +142,10 @@ def mark(func, *a, **kw): class MagicManager(Configurable): """Object that handles all magic-related functionality for IPython. """ + # Non-configurable class attributes + line_magics = Dict + cell_magics = Dict + # An instance of the IPython shell we are attached to shell = Instance('IPython.core.interactiveshell.InteractiveShellABC') @@ -155,30 +159,28 @@ def __init__(self, shell=None, config=None, **traits): def lsmagic(self): - """Return a list of currently available magic functions. - - Gives a list of the bare names after mangling (['ls','cd', ...], not - ['magic_ls','magic_cd',...]""" - - # FIXME. This needs a cleanup, in the way the magics list is built. - - # magics in class definition - class_magic = lambda fn: fn.startswith('magic_') and \ - callable(Magic.__dict__[fn]) - # in instance namespace (run-time user additions) - inst_magic = lambda fn: fn.startswith('magic_') and \ - callable(self.__dict__[fn]) - # and bound magics by user (so they can access self): - inst_bound_magic = lambda fn: fn.startswith('magic_') and \ - callable(self.__class__.__dict__[fn]) - magics = filter(class_magic, Magic.__dict__.keys()) + \ - filter(inst_magic, self.__dict__.keys()) + \ - filter(inst_bound_magic, self.__class__.__dict__.keys()) - out = [] - for fn in set(magics): - out.append(fn.replace('magic_', '', 1)) - out.sort() - return out + """Return a dict of currently available magic functions. + + The return dict has the keys 'line' and 'cell', corresponding to the + two types of magics we support. Each value is a list of names. + """ + + return dict(line = sorted(self.line_magics), + cell = sorted(self.cell_magics)) + + def register(self, *magics): + """Register one or more instances of Magics. + """ + # Start by validating them to ensure they have all had their magic + # methods registered at the instance level + for m in magics: + if not m.registered: + raise ValueError("Class of magics %r was constructed without " + "the @register_macics class decorator") + self.line_magics.update(m.line_magics) + self.cell_magics.update(m.cell_magics) + + # Key base class that provides the central functionality for magics. From 05707667523b9a2bad8818b2770b2a7a7e0eb46f Mon Sep 17 00:00:00 2001 From: Fernando Perez Date: Sun, 13 May 2012 18:59:59 -0700 Subject: [PATCH 017/103] Add all decorators to tag magics. Now all magics are properly tagged with our decorators and the registration machinery is also up. --- IPython/core/magic.py | 62 +++--- IPython/core/magic_functions.py | 382 ++++++++++++++++++++------------ 2 files changed, 270 insertions(+), 174 deletions(-) diff --git a/IPython/core/magic.py b/IPython/core/magic.py index 754634feb9e..b5396fd40a0 100644 --- a/IPython/core/magic.py +++ b/IPython/core/magic.py @@ -28,14 +28,20 @@ from IPython.external.decorator import decorator from IPython.utils.ipstruct import Struct from IPython.utils.process import arg_split -from IPython.utils.traitlets import Dict, Enum, Instance +from IPython.utils.traitlets import Bool, Dict, Instance from IPython.utils.warn import error #----------------------------------------------------------------------------- # Globals #----------------------------------------------------------------------------- -line_magics = {} -cell_magics = {} + +# A dict we'll use for each class that has magics, used as temporary storage to +# pass information between the @line/cell_magic method decorators and the +# @register_magics class decorator, because the method decorators have no +# access to the class when they run. See for more details: +# http://stackoverflow.com/questions/2366713/can-a-python-decorator-of-an-instance-method-access-the-class + +magics = None #----------------------------------------------------------------------------- # Utility classes and functions @@ -44,10 +50,6 @@ class Bunch: pass -# Used for exception handling in magic_edit -class MacroToEdit(ValueError): pass - - def on_off(tag): """Return an ON/OFF string for a 1/0 input. Simple utility function.""" return ['OFF','ON'][tag] @@ -77,31 +79,27 @@ def needs_local_scope(func): #----------------------------------------------------------------------------- def register_magics(cls): - global line_magics, cell_magics + global magics - cls.line_magics = line_magics - cls.cell_magics = cell_magics + cls.magics = magics cls.registered = True - line_magics = {} - cell_magics = {} + magics = None return cls def _magic_marker(magic_type): - global line_magics, cell_magics + global magics if magic_type not in ('line', 'cell'): raise ValueError('magic_type must be one of ["line", "cell"], %s given' % magic_type) - if magic_type == 'line': - line_magics = {} - else: - cell_magics = {} + + magics = dict(line={}, cell={}) # This is a closure to capture the magic_type. We could also use a class, # but it's overkill for just that one bit of state. def magic_deco(arg): - global line_magics, cell_magics + global magics call = lambda f, *a, **k: f(*a, **k) if callable(arg): @@ -122,10 +120,7 @@ def mark(func, *a, **kw): "string or function") # Record the magic function in the global table that will then be # appended to the class via the register_magics class decorator - if magic_type == 'line': - line_magics[name] = retval - else: - cell_magics[name] = retval + magics[magic_type][name] = retval return retval @@ -143,20 +138,23 @@ class MagicManager(Configurable): """Object that handles all magic-related functionality for IPython. """ # Non-configurable class attributes - line_magics = Dict - cell_magics = Dict + magics = Dict - # An instance of the IPython shell we are attached to shell = Instance('IPython.core.interactiveshell.InteractiveShellABC') - auto_status = Enum([ + auto_magic = Bool + + _auto_status = [ 'Automagic is OFF, % prefix IS needed for magic functions.', - 'Automagic is ON, % prefix NOT needed for magic functions.']) + 'Automagic is ON, % prefix IS NOT needed for magic functions.'] def __init__(self, shell=None, config=None, **traits): super(MagicManager, self).__init__(shell=shell, config=config, **traits) + def auto_status(self): + """Return descriptive string with automagic status.""" + return self._auto_status[self.auto_magic] def lsmagic(self): """Return a dict of currently available magic functions. @@ -165,8 +163,7 @@ def lsmagic(self): two types of magics we support. Each value is a list of names. """ - return dict(line = sorted(self.line_magics), - cell = sorted(self.cell_magics)) + return self.magics def register(self, *magics): """Register one or more instances of Magics. @@ -177,9 +174,7 @@ def register(self, *magics): if not m.registered: raise ValueError("Class of magics %r was constructed without " "the @register_macics class decorator") - self.line_magics.update(m.line_magics) - self.cell_magics.update(m.cell_magics) - + self.magics.update(m.magics) # Key base class that provides the central functionality for magics. @@ -209,6 +204,9 @@ class Magics(object): help = """Dict holding all command-line options for each magic. """) + # Non-configurable class attributes + magics = Dict + class __metaclass__(type): def __new__(cls, name, bases, dct): cls.registered = False diff --git a/IPython/core/magic_functions.py b/IPython/core/magic_functions.py index 5f9da887de3..106c5392080 100644 --- a/IPython/core/magic_functions.py +++ b/IPython/core/magic_functions.py @@ -43,7 +43,7 @@ from IPython.core import magic_arguments, page from IPython.core.error import UsageError, StdinNotImplementedError, TryNext from IPython.core.macro import Macro -from IPython.core.magic import (Bunch, Magics, MacroToEdit, compress_dhist, +from IPython.core.magic import (Bunch, Magics, compress_dhist, on_off, needs_local_scope, register_magics, line_magic, cell_magic) from IPython.core.prefilter import ESC_MAGIC @@ -65,6 +65,7 @@ #----------------------------------------------------------------------------- # Magic implementation classes #----------------------------------------------------------------------------- +@register_magics class UserMagics(Magics): """Placeholder for user-defined magics to be added at runtime. @@ -74,21 +75,34 @@ class UserMagics(Magics): """ +@register_magics class BasicMagics(Magics): """Magics that provide central IPython functionality. These are various magics that don't fit into specific categories but that are all part of the base 'IPython experience'.""" - def magic_lsmagic(self, parameter_s = ''): - """List currently available magic functions.""" + def _lsmagic(self): mesc = ESC_MAGIC - print 'Available magic functions:\n'+mesc+\ - (' '+mesc).join(self.lsmagic()) - print '\n' + Magic.auto_status[self.shell.automagic] - return None + cesc = mesc*2 + mman = self.shell.magics_manager + magics = mman.lsmagic() + out = ['Available line magics:', + mesc + (' '+mesc).join(magics['line']), + '', + 'Available cell magics:', + cesc + (' '+cesc).join(magics['cell']), + '', + mman.auto_status[mman.automagic]] + return '\n'.join(out) + + @line_magic + def lsmagic(self, parameter_s=''): + """List currently available magic functions.""" + print self._lsmagic() - def magic_magic(self, parameter_s = ''): + @line_magic + def magic(self, parameter_s=''): """Print information about the magic function system. Supported formats: -latex, -brief, -rest @@ -107,35 +121,34 @@ def magic_magic(self, parameter_s = ''): pass magic_docs = [] - for fname in self.lsmagic(): - mname = 'magic_' + fname - for space in (Magic, self, self.__class__): - try: - fn = space.__dict__[mname] - except KeyError: - pass - else: - break - if mode == 'brief': - # only first line - if fn.__doc__: - fndoc = fn.__doc__.split('\n',1)[0] - else: - fndoc = 'No documentation' - else: - if fn.__doc__: - fndoc = fn.__doc__.rstrip() + escapes = dict(line=ESC_MAGIC, cell=ESC_MAGIC*2) + magics = self.shell.magics_manager.magics + + for mtype in ('line', 'cell'): + escape = escapes[mtype] + for fname, fn in magics: + + if mode == 'brief': + # only first line + if fn.__doc__: + fndoc = fn.__doc__.split('\n',1)[0] + else: + fndoc = 'No documentation' else: - fndoc = 'No documentation' + if fn.__doc__: + fndoc = fn.__doc__.rstrip() + else: + fndoc = 'No documentation' - if mode == 'rest': - rest_docs.append('**%s%s**::\n\n\t%s\n\n' %(ESC_MAGIC, - fname,fndoc)) + if mode == 'rest': + rest_docs.append('**%s%s**::\n\n\t%s\n\n' % + (escape, fname, fndoc)) + + else: + magic_docs.append('%s%s:\n\t%s\n' % + (escape, fname, fndoc)) - else: - magic_docs.append('%s%s:\n\t%s\n' %(ESC_MAGIC, - fname,fndoc)) magic_docs = ''.join(magic_docs) @@ -150,7 +163,7 @@ def magic_magic(self, parameter_s = ''): if mode == 'brief': return magic_docs - outmsg = """ + out = [""" IPython's 'magic' functions =========================== @@ -169,18 +182,16 @@ def magic_magic(self, parameter_s = ''): For a list of the available magic functions, use %lsmagic. For a description of any of them, type %magic_name?, e.g. '%cd?'. -Currently the magic system has the following functions:\n""" +Currently the magic system has the following functions:""", + magic_docs, + "Summary of magic functions (from %slsmagic):", + self._lsmagic, + ] + page.page('\n'.join(out)) - mesc = ESC_MAGIC - outmsg = ("%s\n%s\n\nSummary of magic functions (from %slsmagic):" - "\n\n%s%s\n\n%s" % (outmsg, - magic_docs,mesc,mesc, - (' '+mesc).join(self.lsmagic()), - Magic.auto_status[self.shell.automagic] ) ) - page.page(outmsg) - - def magic_page(self, parameter_s=''): + @line_magic + def page(self, parameter_s=''): """Pretty print the object and display it through a pager. %page [options] OBJECT @@ -194,7 +205,7 @@ def magic_page(self, parameter_s=''): # After a function contributed by Olivier Aubert, slightly modified. # Process options/args - opts,args = self.parse_options(parameter_s,'r') + opts, args = self.parse_options(parameter_s,'r') raw = 'r' in opts oname = args and args or '_' @@ -205,7 +216,8 @@ def magic_page(self, parameter_s=''): else: print 'Object `%s` not found' % oname - def magic_profile(self, parameter_s=''): + @line_magic + def profile(self, parameter_s=''): """Print your currently active IPython profile.""" from IPython.core.application import BaseIPythonApplication if BaseIPythonApplication.initialized(): @@ -213,14 +225,16 @@ def magic_profile(self, parameter_s=''): else: error("profile is an application-level value, but you don't appear to be in an IPython application") - def magic_pprint(self, parameter_s=''): + @line_magic + def pprint(self, parameter_s=''): """Toggle pretty printing on/off.""" ptformatter = self.shell.display_formatter.formatters['text/plain'] ptformatter.pprint = bool(1 - ptformatter.pprint) print 'Pretty printing has been turned', \ ['OFF','ON'][ptformatter.pprint] - def magic_colors(self,parameter_s = ''): + @line_magic + def colors(self, parameter_s=''): """Switch color scheme for prompts, info system and exception handlers. Currently implemented schemes: NoColor, Linux, LightBG. @@ -291,7 +305,8 @@ def color_switch_err(name): else: shell.inspector.set_active_scheme('NoColor') - def magic_xmode(self,parameter_s = ''): + @line_magic + def xmode(self, parameter_s=''): """Switch modes for the exception handlers. Valid modes: Plain, Context and Verbose. @@ -310,13 +325,15 @@ def xmode_switch_err(name): except: xmode_switch_err('user') - def magic_quickref(self,arg): + @line_magic + def quickref(self,arg): """ Show a quick reference sheet """ import IPython.core.usage qr = IPython.core.usage.quick_reference + self.magic_magic('-brief') page.page(qr) - def magic_doctest_mode(self,parameter_s=''): + @line_magic + def doctest_mode(self, parameter_s=''): """Toggle doctest mode on and off. This mode is intended to make IPython behave as much as possible like a @@ -401,7 +418,8 @@ def magic_doctest_mode(self,parameter_s=''): mode_label = ['OFF','ON'][dstore.mode] print 'Doctest mode is:', mode_label - def magic_gui(self, parameter_s=''): + @line_magic + def gui(self, parameter_s=''): """Enable or disable IPython GUI event loop integration. %gui [GUINAME] @@ -435,7 +453,8 @@ def magic_gui(self, parameter_s=''): error(str(e)) @skip_doctest - def magic_precision(self, s=''): + @line_magic + def precision(self, s=''): """Set floating point precision for pretty printing. Can set either integer precision or a format string. @@ -500,7 +519,8 @@ def magic_precision(self, s=''): 'filename', type=unicode, help='Notebook name or filename' ) - def magic_notebook(self, s): + @line_magic + def notebook(self, s): """Export and convert IPython notebooks. This function can export the current IPython history to a notebook file @@ -543,10 +563,16 @@ def magic_notebook(self, s): current.write(nb, f, new_format) +# Used for exception handling in magic_edit +class MacroToEdit(ValueError): pass + + +@register_magics class CodeMagics(Magics): """Magics related to code management (loading, saving, editing, ...).""" - def magic_save(self,parameter_s = ''): + @line_magic + def save(self, parameter_s=''): """Save a set of lines or a macro to a given filename. Usage:\\ @@ -585,7 +611,8 @@ def magic_save(self,parameter_s = ''): print 'The following commands were written to file `%s`:' % fname print cmds - def magic_pastebin(self, parameter_s = ''): + @line_magic + def pastebin(self, parameter_s=''): """Upload code to Github's Gist paste bin, returning the URL. Usage:\\ @@ -621,7 +648,8 @@ def magic_pastebin(self, parameter_s = ''): response_data = json.loads(response.read().decode('utf-8')) return response_data['html_url'] - def magic_loadpy(self, arg_s): + @line_magic + def loadpy(self, arg_s): """Load a .py python script into the GUI console. This magic command can either take a local filename or a url:: @@ -778,12 +806,14 @@ def _edit_macro(self,mname,macro): mfile.close() self.shell.user_ns[mname] = Macro(mvalue) - def magic_ed(self,parameter_s=''): + @line_magic + def ed(self, parameter_s=''): """Alias to %edit.""" return self.magic_edit(parameter_s) @skip_doctest - def magic_edit(self,parameter_s='',last_call=['','']): + @line_magic + def edit(self, parameter_s='',last_call=['','']): """Bring up an editor and execute the resulting code. Usage: @@ -973,13 +1003,15 @@ def magic_edit(self,parameter_s='',last_call=['','']): self.shell.showtraceback() +@register_magics class ConfigMagics(Magics): def __init__(self, shell): super(ConfigMagics, self).__init__(shell) self.configurables = [] - def magic_config(self, s): + @line_magic + def config(self, s): """configure IPython %config Class[.trait=value] @@ -1092,13 +1124,15 @@ def magic_config(self, s): error(e) +@register_magics class NamespaceMagics(Magics): """Magics to manage various aspects of the user's namespace. These include listing variables, introspecting into them, etc. """ - def magic_pinfo(self, parameter_s='', namespaces=None): + @line_magic + def pinfo(self, parameter_s='', namespaces=None): """Provide detailed information about an object. '%pinfo object' is just a synonym for object? or ?object.""" @@ -1120,7 +1154,8 @@ def magic_pinfo(self, parameter_s='', namespaces=None): self.shell._inspect('pinfo', oname, detail_level=detail_level, namespaces=namespaces) - def magic_pinfo2(self, parameter_s='', namespaces=None): + @line_magic + def pinfo2(self, parameter_s='', namespaces=None): """Provide extra detailed information about an object. '%pinfo2 object' is just a synonym for object?? or ??object.""" @@ -1128,7 +1163,8 @@ def magic_pinfo2(self, parameter_s='', namespaces=None): namespaces=namespaces) @skip_doctest - def magic_pdef(self, parameter_s='', namespaces=None): + @line_magic + def pdef(self, parameter_s='', namespaces=None): """Print the definition header for any callable object. If the object is a class, print the constructor information. @@ -1142,18 +1178,21 @@ def magic_pdef(self, parameter_s='', namespaces=None): """ self._inspect('pdef',parameter_s, namespaces) - def magic_pdoc(self, parameter_s='', namespaces=None): + @line_magic + def pdoc(self, parameter_s='', namespaces=None): """Print the docstring for an object. If the given object is a class, it will print both the class and the constructor docstrings.""" self._inspect('pdoc',parameter_s, namespaces) - def magic_psource(self, parameter_s='', namespaces=None): + @line_magic + def psource(self, parameter_s='', namespaces=None): """Print (or run through pager) the source code for an object.""" self._inspect('psource',parameter_s, namespaces) - def magic_pfile(self, parameter_s=''): + @line_magic + def pfile(self, parameter_s=''): """Print (or run through pager) the file where an object is defined. The file opens at the line where the object definition begins. IPython @@ -1176,7 +1215,8 @@ def magic_pfile(self, parameter_s=''): return page.page(self.shell.inspector.format(open(filename).read())) - def magic_psearch(self, parameter_s=''): + @line_magic + def psearch(self, parameter_s=''): """Search for object in namespaces by wildcard. %psearch [options] PATTERN [OBJECT TYPE] @@ -1290,7 +1330,8 @@ def magic_psearch(self, parameter_s=''): shell.showtraceback() @skip_doctest - def magic_who_ls(self, parameter_s=''): + @line_magic + def who_ls(self, parameter_s=''): """Return a sorted list of all interactive variables. If arguments are given, only variables of types matching these @@ -1330,7 +1371,8 @@ def magic_who_ls(self, parameter_s=''): return out @skip_doctest - def magic_who(self, parameter_s=''): + @line_magic + def who(self, parameter_s=''): """Print all interactive variables, with some minimal formatting. If any arguments are given, only variables whose type matches one of @@ -1393,7 +1435,8 @@ def magic_who(self, parameter_s=''): print @skip_doctest - def magic_whos(self, parameter_s=''): + @line_magic + def whos(self, parameter_s=''): """Like %who, but gives some extra information about each variable. The same type filtering of %who can be applied here. @@ -1520,7 +1563,8 @@ def type_name(v): else: print vstr[:25] + "<...>" + vstr[-25:] - def magic_reset(self, parameter_s=''): + @line_magic + def reset(self, parameter_s=''): """Resets the namespace by removing all names defined by the user, if called without arguments, or by removing some types of objects, such as everything currently in IPython's In[] and Out[] containers (see @@ -1644,7 +1688,8 @@ def magic_reset(self, parameter_s=''): gc.collect() - def magic_reset_selective(self, parameter_s=''): + @line_magic + def reset_selective(self, parameter_s=''): """Resets the namespace by removing names defined by the user. Input/Output history are left around in case you need them. @@ -1731,7 +1776,8 @@ def magic_reset_selective(self, parameter_s=''): if m.search(i): del(user_ns[i]) - def magic_xdel(self, parameter_s=''): + @line_magic + def xdel(self, parameter_s=''): """Delete a variable, trying to clear it from anywhere that IPython's machinery has references to it. By default, this uses the identity of the named object in the user namespace to remove @@ -1749,6 +1795,7 @@ def magic_xdel(self, parameter_s=''): print type(e).__name__ +": "+ str(e) +@register_magics class ExecutionMagics(Magics): """Magics related to code execution, debugging, profiling, etc. @@ -1768,7 +1815,8 @@ def profile_missing_notice(self, *args, **kwargs): python-profiler package from non-free.""") @skip_doctest - def magic_prun(self, parameter_s ='',user_mode=1, + @line_magic + def prun(self, parameter_s ='',user_mode=1, opts=None,arg_lst=None,prog_ns=None): """Run a statement through the python code profiler. @@ -1951,7 +1999,8 @@ def magic_prun(self, parameter_s ='',user_mode=1, else: return None - def magic_pdb(self, parameter_s=''): + @line_magic + def pdb(self, parameter_s=''): """Control the automatic calling of the pdb interactive debugger. Call as '%pdb on', '%pdb 1', '%pdb off' or '%pdb 0'. If called without @@ -1985,7 +2034,8 @@ def magic_pdb(self, parameter_s=''): self.shell.call_pdb = new_pdb print 'Automatic pdb calling has been turned',on_off(new_pdb) - def magic_debug(self, parameter_s=''): + @line_magic + def debug(self, parameter_s=''): """Activate the interactive debugger in post-mortem mode. If an exception has just occurred, this lets you inspect its stack @@ -1999,14 +2049,16 @@ def magic_debug(self, parameter_s=''): """ self.shell.debugger(force=True) - def magic_tb(self, s): + @line_magic + def tb(self, s): """Print the last traceback with the currently active exception mode. See %xmode for changing exception reporting modes.""" self.shell.showtraceback() @skip_doctest - def magic_run(self, parameter_s ='', runner=None, + @line_magic + def run(self, parameter_s ='', runner=None, file_finder=get_py_filename): """Run the named file inside IPython as a program. @@ -2334,7 +2386,8 @@ def magic_run(self, parameter_s ='', runner=None, return stats @skip_doctest - def magic_timeit(self, parameter_s =''): + @line_magic + def timeit(self, parameter_s =''): """Time execution of a Python statement or expression Usage:\\ @@ -2472,9 +2525,15 @@ def magic_timeit(self, parameter_s =''): if tc > tc_min: print "Compiler time: %.2f s" % tc + @cell_magic('timeit') + def cell_timeit(self, line, cell): + """Time execution of a Python cell.""" + raise NotImplementedError + @skip_doctest @needs_local_scope - def magic_time(self,parameter_s, user_locals): + @line_magic + def time(self,parameter_s, user_locals): """Time execution of a Python statement or expression. The CPU and wall clock times are printed, and the value of the @@ -2567,7 +2626,8 @@ def magic_time(self,parameter_s, user_locals): return out @skip_doctest - def magic_macro(self,parameter_s = ''): + @line_magic + def macro(self, parameter_s=''): """Define a macro for future re-execution. It accepts ranges of history, filenames or string objects. @@ -2645,6 +2705,7 @@ def magic_macro(self,parameter_s = ''): print macro, +@register_magics class AutoMagics(Magics): """Magics that control various autoX behaviors.""" @@ -2653,16 +2714,17 @@ def __init__(self, shell): # namespace for holding state we may need self._magic_state = Bunch() - def magic_automagic(self, parameter_s = ''): + @line_magic + def automagic(self, parameter_s=''): """Make magic functions callable without having to type the initial %. Without argumentsl toggles on/off (when off, you must call it as %automagic, of course). With arguments it sets the value, and you can use any of (case insensitive): - - on,1,True: to activate + - on, 1, True: to activate - - off,0,False: to deactivate. + - off, 0, False: to deactivate. Note that magic functions have lowest priority, so if there's a variable whose name collides with that of a magic fn, automagic won't @@ -2671,16 +2733,19 @@ def magic_automagic(self, parameter_s = ''): becomes visible to automagic again.""" arg = parameter_s.lower() - if arg in ('on','1','true'): - self.shell.automagic = True - elif arg in ('off','0','false'): - self.shell.automagic = False + mman = self.shell.magics_manager + if arg in ('on', '1', 'true'): + val = True + elif arg in ('off', '0', 'false'): + val = False else: - self.shell.automagic = not self.shell.automagic - print '\n' + Magic.auto_status[self.shell.automagic] + val = not mman.auto_magic + mman.auto_magic = val + print '\n' + self.shell.magics_manager.auto_status() @skip_doctest - def magic_autocall(self, parameter_s = ''): + @line_magic + def autocall(self, parameter_s=''): """Make functions callable without having to type parentheses. Usage: @@ -2728,11 +2793,11 @@ def magic_autocall(self, parameter_s = ''): else: arg = 'toggle' - if not arg in (0,1,2,'toggle'): + if not arg in (0, 1, 2,'toggle'): error('Valid modes: (0->Off, 1->Smart, 2->Full') return - if arg in (0,1,2): + if arg in (0, 1, 2): self.shell.autocall = arg else: # toggle if self.shell.autocall: @@ -2747,12 +2812,14 @@ def magic_autocall(self, parameter_s = ''): print "Automatic calling is:",['OFF','Smart','Full'][self.shell.autocall] +@register_magics class OSMagics(Magics): """Magics to interact with the underlying OS (shell-type functionality). """ @skip_doctest - def magic_alias(self, parameter_s = ''): + @line_magic + def alias(self, parameter_s=''): """Define an alias for a system command. '%alias alias_name cmd' defines 'alias_name' as an alias for 'cmd' @@ -2825,7 +2892,8 @@ def magic_alias(self, parameter_s = ''): self.shell.alias_manager.soft_define_alias(alias, cmd) # end magic_alias - def magic_unalias(self, parameter_s = ''): + @line_magic + def unalias(self, parameter_s=''): """Remove an alias""" aname = parameter_s.strip() @@ -2836,7 +2904,8 @@ def magic_unalias(self, parameter_s = ''): del stored[aname] self.shell.db['stored_aliases'] = stored - def magic_rehashx(self, parameter_s = ''): + @line_magic + def rehashx(self, parameter_s=''): """Update the alias table with all executable files in $PATH. This version explicitly checks that every entry in $PATH is a file @@ -2914,7 +2983,8 @@ def magic_rehashx(self, parameter_s = ''): os.chdir(savedir) @skip_doctest - def magic_pwd(self, parameter_s = ''): + @line_magic + def pwd(self, parameter_s=''): """Return the current working directory path. Examples @@ -2927,7 +2997,8 @@ def magic_pwd(self, parameter_s = ''): return os.getcwdu() @skip_doctest - def magic_cd(self, parameter_s=''): + @line_magic + def cd(self, parameter_s=''): """Change the current working directory. This command automatically maintains an internal list of directories @@ -3063,12 +3134,14 @@ def magic_cd(self, parameter_s=''): print self.shell.user_ns['_dh'][-1] - def magic_env(self, parameter_s=''): + @line_magic + def env(self, parameter_s=''): """List environment variables.""" return dict(os.environ) - def magic_pushd(self, parameter_s=''): + @line_magic + def pushd(self, parameter_s=''): """Place the current dir on stack and change directory. Usage:\\ @@ -3083,7 +3156,8 @@ def magic_pushd(self, parameter_s=''): dir_s.insert(0,cwd) return self.shell.magic('dirs') - def magic_popd(self, parameter_s=''): + @line_magic + def popd(self, parameter_s=''): """Change to directory popped off the top of the stack. """ if not self.shell.dir_stack: @@ -3092,12 +3166,14 @@ def magic_popd(self, parameter_s=''): self.magic_cd(top) print "popd ->",top - def magic_dirs(self, parameter_s=''): + @line_magic + def dirs(self, parameter_s=''): """Return the current directory stack.""" return self.shell.dir_stack - def magic_dhist(self, parameter_s=''): + @line_magic + def dhist(self, parameter_s=''): """Print your history of visited directories. %dhist -> print full history\\ @@ -3118,14 +3194,14 @@ def magic_dhist(self, parameter_s=''): try: args = map(int,parameter_s.split()) except: - self.arg_err(Magic.magic_dhist) + self.arg_err(self.dhist) return if len(args) == 1: ini,fin = max(len(dh)-(args[0]),0),len(dh) elif len(args) == 2: ini,fin = args else: - self.arg_err(Magic.magic_dhist) + self.arg_err(self.dhist) return else: ini,fin = 0,len(dh) @@ -3134,7 +3210,8 @@ def magic_dhist(self, parameter_s=''): start=ini,stop=fin) @skip_doctest - def magic_sc(self, parameter_s=''): + @line_magic + def sc(self, parameter_s=''): """Shell capture - execute a shell command and capture its output. DEPRECATED. Suboptimal, retained for backwards compatibility. @@ -3248,7 +3325,8 @@ def magic_sc(self, parameter_s=''): else: return out - def magic_sx(self, parameter_s=''): + @line_magic + def sx(self, parameter_s=''): """Shell execute - run a shell command and capture its output. %sx command @@ -3293,7 +3371,8 @@ def magic_sx(self, parameter_s=''): return self.shell.getoutput(parameter_s) - def magic_bookmark(self, parameter_s=''): + @line_magic + def bookmark(self, parameter_s=''): """Manage IPython's bookmark system. %bookmark - set bookmark to current dir @@ -3353,7 +3432,8 @@ def magic_bookmark(self, parameter_s=''): bkms[args[0]] = args[1] self.shell.db['bookmarks'] = bkms - def magic_pycat(self, parameter_s=''): + @line_magic + def pycat(self, parameter_s=''): """Show a syntax-highlighted file through a pager. This magic is similar to the cat utility, but it will assume the file @@ -3374,9 +3454,11 @@ def magic_pycat(self, parameter_s=''): page.page(self.shell.pycolorize(cont)) +@register_magics class LoggingMagics(Magics): """Magics related to all logging machinery.""" - def magic_logstart(self,parameter_s=''): + @line_magic + def logstart(self, parameter_s=''): """Start logging anywhere in a session. %logstart [-o|-r|-t] [log_name [log_mode]] @@ -3482,7 +3564,8 @@ def magic_logstart(self,parameter_s=''): 'Current session state plus future input saved.') logger.logstate() - def magic_logstop(self,parameter_s=''): + @line_magic + def logstop(self, parameter_s=''): """Fully stop logging and close log file. In order to start logging again, a new %logstart call needs to be made, @@ -3490,13 +3573,15 @@ def magic_logstop(self,parameter_s=''): options.""" self.logger.logstop() - def magic_logoff(self,parameter_s=''): + @line_magic + def logoff(self, parameter_s=''): """Temporarily stop logging. You must have previously started logging.""" self.shell.logger.switch_log(0) - def magic_logon(self,parameter_s=''): + @line_magic + def logon(self, parameter_s=''): """Restart logging. This function is for restarting logging which you've temporarily @@ -3506,14 +3591,19 @@ def magic_logon(self,parameter_s=''): self.shell.logger.switch_log(1) - def magic_logstate(self,parameter_s=''): + @line_magic + def logstate(self, parameter_s=''): """Print the status of the logging system.""" self.shell.logger.logstate() + +@register_magics class ExtensionsMagics(Magics): """Magics to manage the IPython extensions system.""" - def magic_install_ext(self, parameter_s): + + @line_magic + def install_ext(self, parameter_s=''): """Download and install an extension from a URL, e.g.:: %install_ext https://bitbucket.org/birkenfeld/ipython-physics/raw/d1310a2ab15d/physics.py @@ -3539,46 +3629,29 @@ def magic_install_ext(self, parameter_s): print " %%load_ext %s" % os.path.splitext(filename)[0] - def magic_load_ext(self, module_str): + @line_magic + def load_ext(self, module_str): """Load an IPython extension by its module name.""" return self.shell.extension_manager.load_extension(module_str) - def magic_unload_ext(self, module_str): + @line_magic + def unload_ext(self, module_str): """Unload an IPython extension by its module name.""" self.shell.extension_manager.unload_extension(module_str) - def magic_reload_ext(self, module_str): + @line_magic + def reload_ext(self, module_str): """Reload an IPython extension by its module name.""" self.shell.extension_manager.reload_extension(module_str) -class DeprecatedMagics(Magics): - """Magics slated for later removal.""" - def magic_install_profiles(self, s): - """%install_profiles has been deprecated.""" - print '\n'.join([ - "%install_profiles has been deprecated.", - "Use `ipython profile list` to view available profiles.", - "Requesting a profile with `ipython profile create `", - "or `ipython --profile=` will start with the bundled", - "profile of that name if it exists." - ]) - - def magic_install_default_config(self, s): - """%install_default_config has been deprecated.""" - print '\n'.join([ - "%install_default_config has been deprecated.", - "Use `ipython profile create ` to initialize a profile", - "with the default config files.", - "Add `--reset` to overwrite already existing config files with defaults." - ]) - - +@register_magics class PylabMagics(Magics): """Magics related to matplotlib's pylab support""" @skip_doctest - def magic_pylab(self, s): + @line_magic + def pylab(self, parameter_s=''): """Load numpy and matplotlib to work interactively. %pylab [GUINAME] @@ -3636,4 +3709,29 @@ def magic_pylab(self, s): else: import_all_status = True - self.shell.enable_pylab(s, import_all=import_all_status) + self.shell.enable_pylab(parameter_s, import_all=import_all_status) + + +@register_magics +class DeprecatedMagics(Magics): + """Magics slated for later removal.""" + @line_magic + def install_profiles(self, parameter_s=''): + """%install_profiles has been deprecated.""" + print '\n'.join([ + "%install_profiles has been deprecated.", + "Use `ipython profile list` to view available profiles.", + "Requesting a profile with `ipython profile create `", + "or `ipython --profile=` will start with the bundled", + "profile of that name if it exists." + ]) + + @line_magic + def install_default_config(self, parameter_s=''): + """%install_default_config has been deprecated.""" + print '\n'.join([ + "%install_default_config has been deprecated.", + "Use `ipython profile create ` to initialize a profile", + "with the default config files.", + "Add `--reset` to overwrite already existing config files with defaults." + ]) From f7dab01aa34ec0e10516256137dcd76afdf6a571 Mon Sep 17 00:00:00 2001 From: Fernando Perez Date: Sun, 13 May 2012 21:21:05 -0700 Subject: [PATCH 018/103] Add proper initialization of magics to main shell instance. Not everything works yet, but with this, at least IPython starts correctly again. --- IPython/core/interactiveshell.py | 36 +++++++++++---- IPython/core/magic.py | 75 +++++++++++++++++++++++--------- IPython/core/magic_functions.py | 25 +++++------ 3 files changed, 91 insertions(+), 45 deletions(-) diff --git a/IPython/core/interactiveshell.py b/IPython/core/interactiveshell.py index a53a4c7d29d..e73c9bb3363 100644 --- a/IPython/core/interactiveshell.py +++ b/IPython/core/interactiveshell.py @@ -33,6 +33,8 @@ from IPython.config.configurable import SingletonConfigurable from IPython.core import debugger, oinspect +from IPython.core import history as ipcorehist +from IPython.core import magic from IPython.core import page from IPython.core import prefilter from IPython.core import shadowns @@ -52,7 +54,6 @@ from IPython.core.inputsplitter import IPythonInputSplitter from IPython.core.logger import Logger from IPython.core.macro import Macro -from IPython.core.magic import Magic from IPython.core.payload import PayloadManager from IPython.core.plugin import PluginManager from IPython.core.prefilter import PrefilterManager, ESC_MAGIC @@ -431,8 +432,6 @@ def __init__(self, config=None, ipython_dir=None, profile_dir=None, self.init_encoding() self.init_prefilter() - self._magic = Magic(self) - self.init_syntax_highlighting() self.init_hooks() self.init_pushd_popd_magic() @@ -1994,6 +1993,17 @@ def set_completer_frame(self, frame=None): #------------------------------------------------------------------------- def init_magics(self): + from IPython.core import magic_functions as mf + self.magics_manager = magic.MagicsManager(shell=self, + confg=self.config, + user_magics=mf.UserMagics(self)) + self.configurables.append(self.magics_manager) + + self.magics_manager.register(mf.BasicMagics, mf.CodeMagics, + mf.ConfigMagics, mf.NamespaceMagics, mf.ExecutionMagics, + mf.AutoMagics, mf.OSMagics, mf.LoggingMagics, mf.ExtensionsMagics, + mf.PylabMagics, mf.DeprecatedMagics) + # FIXME: Move the color initialization to the DisplayHook, which # should be split into a prompt manager and displayhook. We probably # even need a centralize colors management object. @@ -2046,23 +2056,31 @@ def define_magic(self, magic_name, func): Example:: - def foo_impl(self,parameter_s=''): + def foo_impl(self, parameter_s=''): 'My very own magic!. (Use docstrings, IPython reads them).' print 'Magic function. Passed parameter is between < >:' print '<%s>' % parameter_s print 'The self object is:', self - ip.define_magic('foo',foo_impl) + ip.define_magic('foo', foo_impl) """ + return self.magics_manager im = types.MethodType(func, self._magic) old = self.find_magic(magic_name) setattr(self._magic, 'magic_' + magic_name, im) return old - def find_magic(self, magic_name): - """Find and return a magic function by name. - """ - return getattr(self._magic, 'magic_' + magic_name, None) + def find_line_magic(self, magic_name): + """Find and return a line magic by name.""" + return self.magics_manager.magics['line'].get(magic_name) + + def find_cell_magic(self, magic_name): + """Find and return a cell magic by name.""" + return self.magics_manager.magics['cell'].get(magic_name) + + def find_magic(self, magic_name, magic_type='line'): + """Find and return a magic of the given type by name.""" + return self.magics_manager.magics[magic_type].get(magic_name) #------------------------------------------------------------------------- # Things related to macros diff --git a/IPython/core/magic.py b/IPython/core/magic.py index b5396fd40a0..b031e27c195 100644 --- a/IPython/core/magic.py +++ b/IPython/core/magic.py @@ -18,6 +18,7 @@ import os import re import sys +import types from getopt import getopt, GetoptError # Our own @@ -41,7 +42,9 @@ # access to the class when they run. See for more details: # http://stackoverflow.com/questions/2366713/can-a-python-decorator-of-an-instance-method-access-the-class -magics = None +magics = dict(line={}, cell={}) + +magic_types = ('line', 'cell') #----------------------------------------------------------------------------- # Utility classes and functions @@ -79,27 +82,26 @@ def needs_local_scope(func): #----------------------------------------------------------------------------- def register_magics(cls): - global magics - - cls.magics = magics cls.registered = True - magics = None + cls.magics = dict(line = magics['line'], + cell = magics['cell']) + magics['line'] = {} + magics['cell'] = {} return cls -def _magic_marker(magic_type): - global magics +def validate_type(magic_type): + if magic_type not in magic_types: + raise ValueError('magic_type must be one of %s, %s given' % + magic_types, magic_type) - if magic_type not in ('line', 'cell'): - raise ValueError('magic_type must be one of ["line", "cell"], %s given' - % magic_type) - magics = dict(line={}, cell={}) +def _magic_marker(magic_type): + validate_type(magic_type) # This is a closure to capture the magic_type. We could also use a class, # but it's overkill for just that one bit of state. def magic_deco(arg): - global magics call = lambda f, *a, **k: f(*a, **k) if callable(arg): @@ -120,6 +122,7 @@ def mark(func, *a, **kw): "string or function") # Record the magic function in the global table that will then be # appended to the class via the register_magics class decorator + #print 'magics:', magics # dbg magics[magic_type][name] = retval return retval @@ -134,7 +137,7 @@ def mark(func, *a, **kw): # Core Magic classes #----------------------------------------------------------------------------- -class MagicManager(Configurable): +class MagicsManager(Configurable): """Object that handles all magic-related functionality for IPython. """ # Non-configurable class attributes @@ -148,9 +151,12 @@ class MagicManager(Configurable): 'Automagic is OFF, % prefix IS needed for magic functions.', 'Automagic is ON, % prefix IS NOT needed for magic functions.'] - def __init__(self, shell=None, config=None, **traits): + user_magics = Instance('IPython.core.magic_functions.UserMagics') - super(MagicManager, self).__init__(shell=shell, config=config, **traits) + def __init__(self, shell=None, config=None, user_magics=None, **traits): + + super(MagicsManager, self).__init__(shell=shell, config=config, + user_magics=user_magics, **traits) def auto_status(self): """Return descriptive string with automagic status.""" @@ -162,7 +168,6 @@ def lsmagic(self): The return dict has the keys 'line' and 'cell', corresponding to the two types of magics we support. Each value is a list of names. """ - return self.magics def register(self, *magics): @@ -174,8 +179,30 @@ def register(self, *magics): if not m.registered: raise ValueError("Class of magics %r was constructed without " "the @register_macics class decorator") + if type(m) is type: + # If we're given an uninstantiated class + m = m(self.shell) + self.magics.update(m.magics) + def define_magic(self, magic_name, func, magic_type='line'): + """Expose own function as magic function for ipython + + Example:: + + def foo_impl(self, parameter_s=''): + 'My very own magic!. (Use docstrings, IPython reads them).' + print 'Magic function. Passed parameter is between < >:' + print '<%s>' % parameter_s + print 'The self object is:', self + + ip.define_magic('foo', foo_impl) + """ + # Create the new method in the user_magics and register it in the + # global table + self.user_magics.new_magic(magic_name, func, magic_type) + self.magics[magic_type][magic_name] = \ + self.user_magics.magics[magic_type][magic_name] # Key base class that provides the central functionality for magics. @@ -199,10 +226,8 @@ class Magics(object): See :mod:`magic_functions` for examples of actual implementation classes. """ - - options_table = Dict(config=True, - help = """Dict holding all command-line options for each magic. - """) + # Dict holding all command-line options for each magic. + options_table = None # Non-configurable class attributes magics = Dict @@ -318,9 +343,17 @@ def parse_options(self, arg_str, opt_str, *long_opts, **kw): return opts,args - def default_option(self,fn,optstr): + def default_option(self, fn, optstr): """Make an entry in the options_table for fn, with value optstr""" if fn not in self.lsmagic(): error("%s is not a magic function" % fn) self.options_table[fn] = optstr + + def new_magic(self, magic_name, func, magic_type='line'): + """TODO + """ + validate_type(magic_type) + meth = types.MethodType(func, self) + setattr(self, magic_name, meth) + self.magics[magic_type][magic_name] = meth diff --git a/IPython/core/magic_functions.py b/IPython/core/magic_functions.py index 106c5392080..e8f3e241250 100644 --- a/IPython/core/magic_functions.py +++ b/IPython/core/magic_functions.py @@ -61,10 +61,10 @@ from IPython.utils.timing import clock, clock2 from IPython.utils.warn import warn, error - #----------------------------------------------------------------------------- # Magic implementation classes #----------------------------------------------------------------------------- + @register_magics class UserMagics(Magics): """Placeholder for user-defined magics to be added at runtime. @@ -110,12 +110,8 @@ def magic(self, parameter_s=''): mode = '' try: - if parameter_s.split()[0] == '-latex': - mode = 'latex' - if parameter_s.split()[0] == '-brief': - mode = 'brief' - if parameter_s.split()[0] == '-rest': - mode = 'rest' + mode = parameter_s.split()[0][1:] + if mode == 'rest': rest_docs = [] except: pass @@ -140,11 +136,9 @@ def magic(self, parameter_s=''): else: fndoc = 'No documentation' - if mode == 'rest': rest_docs.append('**%s%s**::\n\n\t%s\n\n' % (escape, fname, fndoc)) - else: magic_docs.append('%s%s:\n\t%s\n' % (escape, fname, fndoc)) @@ -185,7 +179,7 @@ def magic(self, parameter_s=''): Currently the magic system has the following functions:""", magic_docs, "Summary of magic functions (from %slsmagic):", - self._lsmagic, + self._lsmagic(), ] page.page('\n'.join(out)) @@ -205,7 +199,7 @@ def page(self, parameter_s=''): # After a function contributed by Olivier Aubert, slightly modified. # Process options/args - opts, args = self.parse_options(parameter_s,'r') + opts, args = self.parse_options(parameter_s, 'r') raw = 'r' in opts oname = args and args or '_' @@ -1816,7 +1810,7 @@ def profile_missing_notice(self, *args, **kwargs): @skip_doctest @line_magic - def prun(self, parameter_s ='',user_mode=1, + def prun(self, parameter_s='',user_mode=1, opts=None,arg_lst=None,prog_ns=None): """Run a statement through the python code profiler. @@ -2058,7 +2052,7 @@ def tb(self, s): @skip_doctest @line_magic - def run(self, parameter_s ='', runner=None, + def run(self, parameter_s='', runner=None, file_finder=get_py_filename): """Run the named file inside IPython as a program. @@ -2387,7 +2381,7 @@ def run(self, parameter_s ='', runner=None, @skip_doctest @line_magic - def timeit(self, parameter_s =''): + def timeit(self, parameter_s=''): """Time execution of a Python statement or expression Usage:\\ @@ -3038,7 +3032,6 @@ def cd(self, parameter_s=''): /home/tsuser/parent/child """ - parameter_s = parameter_s.strip() #bkms = self.shell.persist.get("bookmarks",{}) oldcwd = os.getcwdu() @@ -3457,6 +3450,7 @@ def pycat(self, parameter_s=''): @register_magics class LoggingMagics(Magics): """Magics related to all logging machinery.""" + @line_magic def logstart(self, parameter_s=''): """Start logging anywhere in a session. @@ -3715,6 +3709,7 @@ def pylab(self, parameter_s=''): @register_magics class DeprecatedMagics(Magics): """Magics slated for later removal.""" + @line_magic def install_profiles(self, parameter_s=''): """%install_profiles has been deprecated.""" From 07b7cbf878a091fdadbb196109bb714da0421c1b Mon Sep 17 00:00:00 2001 From: Fernando Perez Date: Sun, 13 May 2012 22:39:02 -0700 Subject: [PATCH 019/103] Fix magic decorator logic so we correctly fetch bound instance methods. --- IPython/core/interactiveshell.py | 8 +++++--- IPython/core/magic.py | 24 +++++++++++++----------- IPython/core/magic_functions.py | 1 - 3 files changed, 18 insertions(+), 15 deletions(-) diff --git a/IPython/core/interactiveshell.py b/IPython/core/interactiveshell.py index e73c9bb3363..cd1cb81e7d3 100644 --- a/IPython/core/interactiveshell.py +++ b/IPython/core/interactiveshell.py @@ -1999,15 +1999,17 @@ def init_magics(self): user_magics=mf.UserMagics(self)) self.configurables.append(self.magics_manager) - self.magics_manager.register(mf.BasicMagics, mf.CodeMagics, + all_m = [t(self) for t in [mf.BasicMagics, mf.CodeMagics, mf.ConfigMagics, mf.NamespaceMagics, mf.ExecutionMagics, mf.AutoMagics, mf.OSMagics, mf.LoggingMagics, mf.ExtensionsMagics, - mf.PylabMagics, mf.DeprecatedMagics) + mf.PylabMagics, mf.DeprecatedMagics] ] + self.all_m = all_m + self.magics_manager.register(*all_m) # FIXME: Move the color initialization to the DisplayHook, which # should be split into a prompt manager and displayhook. We probably # even need a centralize colors management object. - self.magic('colors %s' % self.colors) + #self.magic('colors %s' % self.colors) # History was moved to a separate module from IPython.core import history history.init_ipython(self) diff --git a/IPython/core/magic.py b/IPython/core/magic.py index b031e27c195..6a6cbcac518 100644 --- a/IPython/core/magic.py +++ b/IPython/core/magic.py @@ -110,20 +110,18 @@ def magic_deco(arg): name = func.func_name func.magic_name = name retval = decorator(call, func) + magics[magic_type][name] = name elif isinstance(arg, basestring): # Decorator called with arguments (@foo('bar')) name = arg def mark(func, *a, **kw): func.magic_name = name + magics[magic_type][name] = func.func_name return decorator(call, func) retval = mark else: raise ValueError("Decorator can only be called with " "string or function") - # Record the magic function in the global table that will then be - # appended to the class via the register_magics class decorator - #print 'magics:', magics # dbg - magics[magic_type][name] = retval return retval @@ -157,6 +155,7 @@ def __init__(self, shell=None, config=None, user_magics=None, **traits): super(MagicsManager, self).__init__(shell=shell, config=config, user_magics=user_magics, **traits) + self.magics = dict(line={}, cell={}) def auto_status(self): """Return descriptive string with automagic status.""" @@ -170,20 +169,17 @@ def lsmagic(self): """ return self.magics - def register(self, *magics): + def register(self, *magic_objects): """Register one or more instances of Magics. """ # Start by validating them to ensure they have all had their magic # methods registered at the instance level - for m in magics: + for m in magic_objects: if not m.registered: raise ValueError("Class of magics %r was constructed without " "the @register_macics class decorator") - if type(m) is type: - # If we're given an uninstantiated class - m = m(self.shell) - - self.magics.update(m.magics) + for mtype in magic_types: + self.magics[mtype].update(m.magics[mtype]) def define_magic(self, magic_name, func, magic_type='line'): """Expose own function as magic function for ipython @@ -241,6 +237,12 @@ def __init__(self, shell): if not(self.__class__.registered): raise ValueError('unregistered Magics') self.shell = shell + mtab = dict(line={}, cell={}) + for mtype in magic_types: + tab = mtab[mtype] + for magic_name, meth_name in self.magics[mtype].iteritems(): + tab[magic_name] = getattr(self, meth_name) + self.magics.update(mtab) def arg_err(self,func): """Print docstring if incorrect arguments were passed""" diff --git a/IPython/core/magic_functions.py b/IPython/core/magic_functions.py index e8f3e241250..f00be7fc96f 100644 --- a/IPython/core/magic_functions.py +++ b/IPython/core/magic_functions.py @@ -241,7 +241,6 @@ def colors(self, parameter_s=''): %colors nocolor """ - def color_switch_err(name): warn('Error changing %s color schemes.\n%s' % (name,sys.exc_info()[1])) From 3f85cbcc8254e33140e2764306e87bf0c4e1f7a3 Mon Sep 17 00:00:00 2001 From: Fernando Perez Date: Sun, 13 May 2012 22:43:48 -0700 Subject: [PATCH 020/103] Fix %lsmagic --- IPython/core/interactiveshell.py | 2 +- IPython/core/magic_functions.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/IPython/core/interactiveshell.py b/IPython/core/interactiveshell.py index cd1cb81e7d3..379c8a36ab5 100644 --- a/IPython/core/interactiveshell.py +++ b/IPython/core/interactiveshell.py @@ -2009,7 +2009,7 @@ def init_magics(self): # FIXME: Move the color initialization to the DisplayHook, which # should be split into a prompt manager and displayhook. We probably # even need a centralize colors management object. - #self.magic('colors %s' % self.colors) + self.magic('colors %s' % self.colors) # History was moved to a separate module from IPython.core import history history.init_ipython(self) diff --git a/IPython/core/magic_functions.py b/IPython/core/magic_functions.py index f00be7fc96f..d72ce22dc2f 100644 --- a/IPython/core/magic_functions.py +++ b/IPython/core/magic_functions.py @@ -93,7 +93,7 @@ def _lsmagic(self): 'Available cell magics:', cesc + (' '+cesc).join(magics['cell']), '', - mman.auto_status[mman.automagic]] + mman.auto_status()] return '\n'.join(out) @line_magic From e3752a8b2043460a8c1b421d3ef4ff46c2c7d10f Mon Sep 17 00:00:00 2001 From: Fernando Perez Date: Sun, 13 May 2012 22:46:27 -0700 Subject: [PATCH 021/103] Fix %magic --- IPython/core/magic_functions.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/IPython/core/magic_functions.py b/IPython/core/magic_functions.py index d72ce22dc2f..e6866468ed4 100644 --- a/IPython/core/magic_functions.py +++ b/IPython/core/magic_functions.py @@ -122,7 +122,7 @@ def magic(self, parameter_s=''): for mtype in ('line', 'cell'): escape = escapes[mtype] - for fname, fn in magics: + for fname, fn in magics[mtype].iteritems(): if mode == 'brief': # only first line From 580a081f89a939d0a03c8e0abfce33711e37c100 Mon Sep 17 00:00:00 2001 From: Fernando Perez Date: Sun, 13 May 2012 22:59:33 -0700 Subject: [PATCH 022/103] Remove unnecessary metaclass and allow registration of magic classes. This lets us register uninstantiated magic classes as well, making the init logic a bit cleaner in the main shell class. --- IPython/core/interactiveshell.py | 6 ++---- IPython/core/magic.py | 12 +++++++----- IPython/core/magic_functions.py | 1 - 3 files changed, 9 insertions(+), 10 deletions(-) diff --git a/IPython/core/interactiveshell.py b/IPython/core/interactiveshell.py index 379c8a36ab5..e73c9bb3363 100644 --- a/IPython/core/interactiveshell.py +++ b/IPython/core/interactiveshell.py @@ -1999,12 +1999,10 @@ def init_magics(self): user_magics=mf.UserMagics(self)) self.configurables.append(self.magics_manager) - all_m = [t(self) for t in [mf.BasicMagics, mf.CodeMagics, + self.magics_manager.register(mf.BasicMagics, mf.CodeMagics, mf.ConfigMagics, mf.NamespaceMagics, mf.ExecutionMagics, mf.AutoMagics, mf.OSMagics, mf.LoggingMagics, mf.ExtensionsMagics, - mf.PylabMagics, mf.DeprecatedMagics] ] - self.all_m = all_m - self.magics_manager.register(*all_m) + mf.PylabMagics, mf.DeprecatedMagics) # FIXME: Move the color initialization to the DisplayHook, which # should be split into a prompt manager and displayhook. We probably diff --git a/IPython/core/magic.py b/IPython/core/magic.py index 6a6cbcac518..1c3d3e84019 100644 --- a/IPython/core/magic.py +++ b/IPython/core/magic.py @@ -178,6 +178,10 @@ def register(self, *magic_objects): if not m.registered: raise ValueError("Class of magics %r was constructed without " "the @register_macics class decorator") + if type(m) is type: + # If we're given an uninstantiated class + m = m(self.shell) + for mtype in magic_types: self.magics[mtype].update(m.magics[mtype]) @@ -228,10 +232,7 @@ class Magics(object): # Non-configurable class attributes magics = Dict - class __metaclass__(type): - def __new__(cls, name, bases, dct): - cls.registered = False - return type.__new__(cls, name, bases, dct) + registered = False def __init__(self, shell): if not(self.__class__.registered): @@ -241,7 +242,8 @@ def __init__(self, shell): for mtype in magic_types: tab = mtab[mtype] for magic_name, meth_name in self.magics[mtype].iteritems(): - tab[magic_name] = getattr(self, meth_name) + if isinstance(meth_name, basestring): + tab[magic_name] = getattr(self, meth_name) self.magics.update(mtab) def arg_err(self,func): diff --git a/IPython/core/magic_functions.py b/IPython/core/magic_functions.py index e6866468ed4..ac0ee74bea0 100644 --- a/IPython/core/magic_functions.py +++ b/IPython/core/magic_functions.py @@ -143,7 +143,6 @@ def magic(self, parameter_s=''): magic_docs.append('%s%s:\n\t%s\n' % (escape, fname, fndoc)) - magic_docs = ''.join(magic_docs) if mode == 'rest': From 9966c19f4fa7d82f1d2e17b5e1bebc79597cc6c8 Mon Sep 17 00:00:00 2001 From: Fernando Perez Date: Sun, 13 May 2012 23:02:52 -0700 Subject: [PATCH 023/103] Fix tab completion for magics, though cell magics are ignored for now. --- IPython/core/completer.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/IPython/core/completer.py b/IPython/core/completer.py index 02f646feca0..333eaddfc11 100644 --- a/IPython/core/completer.py +++ b/IPython/core/completer.py @@ -602,7 +602,8 @@ def magic_matches(self, text): #print 'Completer->magic_matches:',text,'lb',self.text_until_cursor # dbg # Get all shell magics now rather than statically, so magics loaded at # runtime show up too - magics = self.shell._magic.lsmagic() + # FIXME - cell magics not implemented here yet. + magics = self.shell.magics_manager.lsmagic()['line'] pre = self.magic_escape baretext = text.lstrip(pre) return [ pre+m for m in magics if m.startswith(baretext)] From cfd2d6a68ee207e976eb576d6dac19936b135f61 Mon Sep 17 00:00:00 2001 From: Fernando Perez Date: Mon, 14 May 2012 00:14:17 -0700 Subject: [PATCH 024/103] Fix %run --- IPython/core/magic.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/IPython/core/magic.py b/IPython/core/magic.py index 1c3d3e84019..1dfde09b9d6 100644 --- a/IPython/core/magic.py +++ b/IPython/core/magic.py @@ -228,16 +228,16 @@ class Magics(object): """ # Dict holding all command-line options for each magic. options_table = None - - # Non-configurable class attributes - magics = Dict - + # Dict for the mapping of magic names to methods, set by class decorator + magics = None + # Flag to check that the class decorator was properly applied registered = False def __init__(self, shell): if not(self.__class__.registered): raise ValueError('unregistered Magics') self.shell = shell + self.options_table = {} mtab = dict(line={}, cell={}) for mtype in magic_types: tab = mtab[mtype] @@ -251,7 +251,7 @@ def arg_err(self,func): print 'Error in arguments:' print oinspect.getdoc(func) - def format_latex(self,strng): + def format_latex(self, strng): """Format a string for latex inclusion.""" # Characters that need to be escaped for latex: @@ -301,7 +301,7 @@ def parse_options(self, arg_str, opt_str, *long_opts, **kw): standard library.""" # inject default options at the beginning of the input line - caller = sys._getframe(1).f_code.co_name.replace('magic_','') + caller = sys._getframe(1).f_code.co_name arg_str = '%s %s' % (self.options_table.get(caller,''),arg_str) mode = kw.get('mode','string') From 55dfb450c56e509295b781931dc3643bc6eed5cd Mon Sep 17 00:00:00 2001 From: Fernando Perez Date: Mon, 14 May 2012 00:19:41 -0700 Subject: [PATCH 025/103] Cleaner magics constructor --- IPython/core/magic.py | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/IPython/core/magic.py b/IPython/core/magic.py index 1dfde09b9d6..b9c6022cffc 100644 --- a/IPython/core/magic.py +++ b/IPython/core/magic.py @@ -232,19 +232,27 @@ class Magics(object): magics = None # Flag to check that the class decorator was properly applied registered = False + # Instance of IPython shell + shell = None def __init__(self, shell): if not(self.__class__.registered): - raise ValueError('unregistered Magics') + raise ValueError('Magics subclass without registration - ' + 'did you forget to apply @register_magics?') self.shell = shell self.options_table = {} - mtab = dict(line={}, cell={}) + # The method decorators are run when the instance doesn't exist yet, so + # they can only record the names of the methods they are supposed to + # grab. Only now, that the instance exists, can we create the proper + # mapping to bound methods. So we read the info off the original names + # table and replace each method name by the actual bound method. for mtype in magic_types: - tab = mtab[mtype] - for magic_name, meth_name in self.magics[mtype].iteritems(): + tab = self.magics[mtype] + # must explicitly use keys, as we're mutating this puppy + for magic_name in tab.keys(): + meth_name = tab[magic_name] if isinstance(meth_name, basestring): tab[magic_name] = getattr(self, meth_name) - self.magics.update(mtab) def arg_err(self,func): """Print docstring if incorrect arguments were passed""" From 8e278dfdb40035f6a9179c28a64af39a83080501 Mon Sep 17 00:00:00 2001 From: Fernando Perez Date: Mon, 14 May 2012 00:22:53 -0700 Subject: [PATCH 026/103] Fix a few references to old-style names --- IPython/core/magic_functions.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/IPython/core/magic_functions.py b/IPython/core/magic_functions.py index ac0ee74bea0..1d9bb381557 100644 --- a/IPython/core/magic_functions.py +++ b/IPython/core/magic_functions.py @@ -1141,7 +1141,7 @@ def pinfo(self, parameter_s='', namespaces=None): if pinfo or qmark1 or qmark2: detail_level = 1 if "*" in oname: - self.magic_psearch(oname) + self.psearch(oname) else: self.shell._inspect('pinfo', oname, detail_level=detail_level, namespaces=namespaces) @@ -1796,7 +1796,7 @@ class ExecutionMagics(Magics): def __init__(self, shell): super(ExecutionMagics, self).__init__(shell) if profile is None: - self.magic_prun = self.profile_missing_notice + self.prun = self.profile_missing_notice # Default execution function used to actually run user code. self.default_runner = None @@ -2253,7 +2253,7 @@ def run(self, parameter_s='', runner=None, stats = None with self.shell.readline_no_record: if 'p' in opts: - stats = self.magic_prun('', 0, opts, arg_lst, prog_ns) + stats = self.prun('', 0, opts, arg_lst, prog_ns) else: if 'd' in opts: deb = debugger.Pdb(self.shell.colors) From f94a762aafafe2c52a95ec26dccdcaf58f01988b Mon Sep 17 00:00:00 2001 From: Fernando Perez Date: Sat, 19 May 2012 13:33:33 -0700 Subject: [PATCH 027/103] Update history magics to new API. --- IPython/core/history.py | 486 ++++++++++++++++++++-------------------- 1 file changed, 249 insertions(+), 237 deletions(-) diff --git a/IPython/core/history.py b/IPython/core/history.py index 8434872aef7..5ca060f166a 100644 --- a/IPython/core/history.py +++ b/IPython/core/history.py @@ -26,6 +26,7 @@ # Our own packages from IPython.core.error import StdinNotImplementedError +from IPython.core.magic import Magics, register_magics, line_magic from IPython.config.configurable import Configurable from IPython.external.decorator import decorator from IPython.testing.skipdoctest import skip_doctest @@ -74,13 +75,13 @@ class HistoryAccessor(Configurable): hist_file = Unicode(config=True, help="""Path to file to use for SQLite history database. - By default, IPython will put the history database in the IPython profile - directory. If you would rather share one history among profiles, - you ca set this value in each, so that they are consistent. + By default, IPython will put the history database in the IPython + profile directory. If you would rather share one history among + profiles, you ca set this value in each, so that they are consistent. - Due to an issue with fcntl, SQLite is known to misbehave on some NFS mounts. - If you see IPython hanging, try setting this to something on a local disk, - e.g:: + Due to an issue with fcntl, SQLite is known to misbehave on some NFS + mounts. If you see IPython hanging, try setting this to something on a + local disk, e.g:: ipython --HistoryManager.hist_file=/tmp/ipython_hist.sqlite @@ -153,7 +154,8 @@ def _get_hist_file_name(self, profile='default'): def init_db(self): """Connect to the database, and create tables if necessary.""" # use detect_types so that timestamps return datetime objects - self.db = sqlite3.connect(self.hist_file, detect_types=sqlite3.PARSE_DECLTYPES|sqlite3.PARSE_COLNAMES) + self.db = sqlite3.connect(self.hist_file, + detect_types=sqlite3.PARSE_DECLTYPES|sqlite3.PARSE_COLNAMES) self.db.execute("""CREATE TABLE IF NOT EXISTS sessions (session integer primary key autoincrement, start timestamp, end timestamp, num_cmds integer, remark text)""") @@ -216,7 +218,8 @@ def get_session_info(self, session=0): Returns ------- - (session_id [int], start [datetime], end [datetime], num_cmds [int], remark [unicode]) + (session_id [int], start [datetime], end [datetime], num_cmds [int], + remark [unicode]) Sessions that are running or did not exit cleanly will have `end=None` and `num_cmds=None`. @@ -512,7 +515,8 @@ def get_range(self, session=0, start=1, stop=None, raw=True,output=False): session += self.session_number if session==self.session_number: # Current session return self._get_range_session(start, stop, raw, output) - return super(HistoryManager, self).get_range(session, start, stop, raw, output) + return super(HistoryManager, self).get_range(session, start, stop, raw, + output) ## ---------------------------- ## Methods for storing history: @@ -611,7 +615,9 @@ def writeout_cache(self, conn=None): print("ERROR! Session/line number was not unique in", "database. History logging moved to new session", self.session_number) - try: # Try writing to the new session. If this fails, don't recurse + try: + # Try writing to the new session. If this fails, don't + # recurse self._writeout_input_cache(conn) except sqlite3.IntegrityError: pass @@ -718,264 +724,270 @@ def _format_lineno(session, line): return "%s#%s" % (session, line) -@skip_doctest -def magic_history(self, parameter_s = ''): - """Print input history (_i variables), with most recent last. +@register_magics +class HistoryMagics(Magics): - %history [-o -p -t -n] [-f filename] [range | -g pattern | -l number] + @skip_doctest + @line_magic + def history(self, parameter_s = ''): + """Print input history (_i variables), with most recent last. - By default, input history is printed without line numbers so it can be - directly pasted into an editor. Use -n to show them. + %history [-o -p -t -n] [-f filename] [range | -g pattern | -l number] - By default, all input history from the current session is displayed. - Ranges of history can be indicated using the syntax: - 4 : Line 4, current session - 4-6 : Lines 4-6, current session - 243/1-5: Lines 1-5, session 243 - ~2/7 : Line 7, session 2 before current - ~8/1-~6/5 : From the first line of 8 sessions ago, to the fifth line - of 6 sessions ago. - Multiple ranges can be entered, separated by spaces + By default, input history is printed without line numbers so it can be + directly pasted into an editor. Use -n to show them. - The same syntax is used by %macro, %save, %edit, %rerun + By default, all input history from the current session is displayed. + Ranges of history can be indicated using the syntax: + 4 : Line 4, current session + 4-6 : Lines 4-6, current session + 243/1-5: Lines 1-5, session 243 + ~2/7 : Line 7, session 2 before current + ~8/1-~6/5 : From the first line of 8 sessions ago, to the fifth line + of 6 sessions ago. + Multiple ranges can be entered, separated by spaces - Options: + The same syntax is used by %macro, %save, %edit, %rerun - -n: print line numbers for each input. - This feature is only available if numbered prompts are in use. + Options: - -o: also print outputs for each input. + -n: print line numbers for each input. + This feature is only available if numbered prompts are in use. - -p: print classic '>>>' python prompts before each input. This is useful - for making documentation, and in conjunction with -o, for producing - doctest-ready output. + -o: also print outputs for each input. - -r: (default) print the 'raw' history, i.e. the actual commands you typed. + -p: print classic '>>>' python prompts before each input. This is + useful for making documentation, and in conjunction with -o, for + producing doctest-ready output. - -t: print the 'translated' history, as IPython understands it. IPython - filters your input and converts it all into valid Python source before - executing it (things like magics or aliases are turned into function - calls, for example). With this option, you'll see the native history - instead of the user-entered version: '%cd /' will be seen as - 'get_ipython().magic("%cd /")' instead of '%cd /'. + -r: (default) print the 'raw' history, i.e. the actual commands you + typed. - -g: treat the arg as a pattern to grep for in (full) history. - This includes the saved history (almost all commands ever written). - Use '%hist -g' to show full saved history (may be very long). + -t: print the 'translated' history, as IPython understands it. + IPython filters your input and converts it all into valid Python + source before executing it (things like magics or aliases are turned + into function calls, for example). With this option, you'll see the + native history instead of the user-entered version: '%cd /' will be + seen as 'get_ipython().magic("%cd /")' instead of '%cd /'. - -l: get the last n lines from all sessions. Specify n as a single arg, or - the default is the last 10 lines. + -g: treat the arg as a pattern to grep for in (full) history. + This includes the saved history (almost all commands ever written). + Use '%hist -g' to show full saved history (may be very long). - -f FILENAME: instead of printing the output to the screen, redirect it to - the given file. The file is always overwritten, though *when it can*, - IPython asks for confirmation first. In particular, running the command - "history -f FILENAME" from the IPython Notebook interface will replace - FILENAME even if it already exists *without* confirmation. + -l: get the last n lines from all sessions. Specify n as a single + arg, or the default is the last 10 lines. - Examples - -------- - :: - - In [6]: %hist -n 4-6 - 4:a = 12 - 5:print a**2 - 6:%hist -n 4-6 - - """ - - if not self.shell.displayhook.do_full_cache: - print('This feature is only available if numbered prompts are in use.') - return - opts,args = self.parse_options(parameter_s,'noprtglf:',mode='string') - - # For brevity - history_manager = self.shell.history_manager - - def _format_lineno(session, line): - """Helper function to format line numbers properly.""" - if session in (0, history_manager.session_number): - return str(line) - return "%s/%s" % (session, line) - - # Check if output to specific file was requested. - try: - outfname = opts['f'] - except KeyError: - outfile = io.stdout # default - # We don't want to close stdout at the end! - close_at_end = False - else: - if os.path.exists(outfname): - try: - ans = io.ask_yes_no("File %r exists. Overwrite?" % outfname) - except StdinNotImplementedError: - ans = True - if not ans: - print('Aborting.') - return - print("Overwriting file.") - outfile = io_open(outfname, 'w', encoding='utf-8') - close_at_end = True - - print_nums = 'n' in opts - get_output = 'o' in opts - pyprompts = 'p' in opts - # Raw history is the default - raw = not('t' in opts) - - default_length = 40 - pattern = None - - if 'g' in opts: # Glob search - pattern = "*" + args + "*" if args else "*" - hist = history_manager.search(pattern, raw=raw, output=get_output) - print_nums = True - elif 'l' in opts: # Get 'tail' - try: - n = int(args) - except ValueError, IndexError: - n = 10 - hist = history_manager.get_tail(n, raw=raw, output=get_output) - else: - if args: # Get history by ranges - hist = history_manager.get_range_by_str(args, raw, get_output) - else: # Just get history for the current session - hist = history_manager.get_range(raw=raw, output=get_output) - - # We could be displaying the entire history, so let's not try to pull it - # into a list in memory. Anything that needs more space will just misalign. - width = 4 - - for session, lineno, inline in hist: - # Print user history with tabs expanded to 4 spaces. The GUI clients - # use hard tabs for easier usability in auto-indented code, but we want - # to produce PEP-8 compliant history for safe pasting into an editor. - if get_output: - inline, output = inline - inline = inline.expandtabs(4).rstrip() - - multiline = "\n" in inline - line_sep = '\n' if multiline else ' ' - if print_nums: - print(u'%s:%s' % (_format_lineno(session, lineno).rjust(width), - line_sep), file=outfile, end=u'') - if pyprompts: - print(u">>> ", end=u"", file=outfile) - if multiline: - inline = "\n... ".join(inline.splitlines()) + "\n..." - print(inline, file=outfile) - if get_output and output: - print(output, file=outfile) - - if close_at_end: - outfile.close() - - -def magic_rep(self, arg): - r"""Repeat a command, or get command to input line for editing. - - %recall and %rep are equivalent. + -f FILENAME: instead of printing the output to the screen, redirect + it to the given file. The file is always overwritten, though *when + it can*, IPython asks for confirmation first. In particular, running + the command 'history -f FILENAME' from the IPython Notebook + interface will replace FILENAME even if it already exists *without* + confirmation. - - %recall (no arguments): + Examples + -------- + :: - Place a string version of last computation result (stored in the special '_' - variable) to the next input prompt. Allows you to create elaborate command - lines without using copy-paste:: + In [6]: %hist -n 4-6 + 4:a = 12 + 5:print a**2 + 6:%hist -n 4-6 - In[1]: l = ["hei", "vaan"] - In[2]: "".join(l) - Out[2]: heivaan - In[3]: %rep - In[4]: heivaan_ <== cursor blinking - - %recall 45 - - Place history line 45 on the next input prompt. Use %hist to find - out the number. + """ - %recall 1-4 + if not self.shell.displayhook.do_full_cache: + print('This feature is only available if numbered prompts ' + 'are in use.') + return + opts,args = self.parse_options(parameter_s,'noprtglf:',mode='string') - Combine the specified lines into one cell, and place it on the next - input prompt. See %history for the slice syntax. + # For brevity + history_manager = self.shell.history_manager - %recall foo+bar + def _format_lineno(session, line): + """Helper function to format line numbers properly.""" + if session in (0, history_manager.session_number): + return str(line) + return "%s/%s" % (session, line) - If foo+bar can be evaluated in the user namespace, the result is - placed at the next input prompt. Otherwise, the history is searched - for lines which contain that substring, and the most recent one is - placed at the next input prompt. - """ - if not arg: # Last output - self.shell.set_next_input(str(self.shell.user_ns["_"])) - return - # Get history range - histlines = self.shell.history_manager.get_range_by_str(arg) - cmd = "\n".join(x[2] for x in histlines) - if cmd: - self.shell.set_next_input(cmd.rstrip()) - return - - try: # Variable in user namespace - cmd = str(eval(arg, self.shell.user_ns)) - except Exception: # Search for term in history - histlines = self.shell.history_manager.search("*"+arg+"*") - for h in reversed([x[2] for x in histlines]): - if 'rep' in h: - continue - self.shell.set_next_input(h.rstrip()) + # Check if output to specific file was requested. + try: + outfname = opts['f'] + except KeyError: + outfile = io.stdout # default + # We don't want to close stdout at the end! + close_at_end = False + else: + if os.path.exists(outfname): + try: + ans = io.ask_yes_no("File %r exists. Overwrite?" % outfname) + except StdinNotImplementedError: + ans = True + if not ans: + print('Aborting.') + return + print("Overwriting file.") + outfile = io_open(outfname, 'w', encoding='utf-8') + close_at_end = True + + print_nums = 'n' in opts + get_output = 'o' in opts + pyprompts = 'p' in opts + # Raw history is the default + raw = not('t' in opts) + + pattern = None + + if 'g' in opts: # Glob search + pattern = "*" + args + "*" if args else "*" + hist = history_manager.search(pattern, raw=raw, output=get_output) + print_nums = True + elif 'l' in opts: # Get 'tail' + try: + n = int(args) + except (ValueError, IndexError): + n = 10 + hist = history_manager.get_tail(n, raw=raw, output=get_output) + else: + if args: # Get history by ranges + hist = history_manager.get_range_by_str(args, raw, get_output) + else: # Just get history for the current session + hist = history_manager.get_range(raw=raw, output=get_output) + + # We could be displaying the entire history, so let's not try to pull + # it into a list in memory. Anything that needs more space will just + # misalign. + width = 4 + + for session, lineno, inline in hist: + # Print user history with tabs expanded to 4 spaces. The GUI + # clients use hard tabs for easier usability in auto-indented code, + # but we want to produce PEP-8 compliant history for safe pasting + # into an editor. + if get_output: + inline, output = inline + inline = inline.expandtabs(4).rstrip() + + multiline = "\n" in inline + line_sep = '\n' if multiline else ' ' + if print_nums: + print(u'%s:%s' % (_format_lineno(session, lineno).rjust(width), + line_sep), file=outfile, end=u'') + if pyprompts: + print(u">>> ", end=u"", file=outfile) + if multiline: + inline = "\n... ".join(inline.splitlines()) + "\n..." + print(inline, file=outfile) + if get_output and output: + print(output, file=outfile) + + if close_at_end: + outfile.close() + + @line_magic + def rep(self, arg): + r"""Repeat a command, or get command to input line for editing. + + %recall and %rep are equivalent. + + - %recall (no arguments): + + Place a string version of last computation result (stored in the + special '_' variable) to the next input prompt. Allows you to create + elaborate command lines without using copy-paste:: + + In[1]: l = ["hei", "vaan"] + In[2]: "".join(l) + Out[2]: heivaan + In[3]: %rep + In[4]: heivaan_ <== cursor blinking + + %recall 45 + + Place history line 45 on the next input prompt. Use %hist to find + out the number. + + %recall 1-4 + + Combine the specified lines into one cell, and place it on the next + input prompt. See %history for the slice syntax. + + %recall foo+bar + + If foo+bar can be evaluated in the user namespace, the result is + placed at the next input prompt. Otherwise, the history is searched + for lines which contain that substring, and the most recent one is + placed at the next input prompt. + """ + if not arg: # Last output + self.shell.set_next_input(str(self.shell.user_ns["_"])) + return + # Get history range + histlines = self.shell.history_manager.get_range_by_str(arg) + cmd = "\n".join(x[2] for x in histlines) + if cmd: + self.shell.set_next_input(cmd.rstrip()) return - else: - self.shell.set_next_input(cmd.rstrip()) - print("Couldn't evaluate or find in history:", arg) + try: # Variable in user namespace + cmd = str(eval(arg, self.shell.user_ns)) + except Exception: # Search for term in history + histlines = self.shell.history_manager.search("*"+arg+"*") + for h in reversed([x[2] for x in histlines]): + if 'rep' in h: + continue + self.shell.set_next_input(h.rstrip()) + return + else: + self.shell.set_next_input(cmd.rstrip()) + print("Couldn't evaluate or find in history:", arg) -def magic_rerun(self, parameter_s=''): - """Re-run previous input + @line_magic + def rerun(self, parameter_s=''): + """Re-run previous input - By default, you can specify ranges of input history to be repeated - (as with %history). With no arguments, it will repeat the last line. + By default, you can specify ranges of input history to be repeated + (as with %history). With no arguments, it will repeat the last line. - Options: + Options: - -l : Repeat the last n lines of input, not including the - current command. + -l : Repeat the last n lines of input, not including the + current command. - -g foo : Repeat the most recent line which contains foo - """ - opts, args = self.parse_options(parameter_s, 'l:g:', mode='string') - if "l" in opts: # Last n lines - n = int(opts['l']) - hist = self.shell.history_manager.get_tail(n) - elif "g" in opts: # Search - p = "*"+opts['g']+"*" - hist = list(self.shell.history_manager.search(p)) - for l in reversed(hist): - if "rerun" not in l[2]: - hist = [l] # The last match which isn't a %rerun - break - else: - hist = [] # No matches except %rerun - elif args: # Specify history ranges - hist = self.shell.history_manager.get_range_by_str(args) - else: # Last line - hist = self.shell.history_manager.get_tail(1) - hist = [x[2] for x in hist] - if not hist: - print("No lines in history match specification") - return - histlines = "\n".join(hist) - print("=== Executing: ===") - print(histlines) - print("=== Output: ===") - self.shell.run_cell("\n".join(hist), store_history=False) + -g foo : Repeat the most recent line which contains foo + """ + opts, args = self.parse_options(parameter_s, 'l:g:', mode='string') + if "l" in opts: # Last n lines + n = int(opts['l']) + hist = self.shell.history_manager.get_tail(n) + elif "g" in opts: # Search + p = "*"+opts['g']+"*" + hist = list(self.shell.history_manager.search(p)) + for l in reversed(hist): + if "rerun" not in l[2]: + hist = [l] # The last match which isn't a %rerun + break + else: + hist = [] # No matches except %rerun + elif args: # Specify history ranges + hist = self.shell.history_manager.get_range_by_str(args) + else: # Last line + hist = self.shell.history_manager.get_tail(1) + hist = [x[2] for x in hist] + if not hist: + print("No lines in history match specification") + return + histlines = "\n".join(hist) + print("=== Executing: ===") + print(histlines) + print("=== Output: ===") + self.shell.run_cell("\n".join(hist), store_history=False) def init_ipython(ip): - ip.define_magic("rep", magic_rep) - ip.define_magic("recall", magic_rep) - ip.define_magic("rerun", magic_rerun) - ip.define_magic("hist", magic_history) # Alternative name - ip.define_magic("history", magic_history) + ip.magics_manager.register(HistoryMagics) + #ip.define_magic('hist', HistoryMagics.history) + #ip.define_magic('recall', HistoryMagics.rep) # XXX - ipy_completers are in quarantine, need to be updated to new apis #import ipy_completers From e8c0cba4ba672aea5ab335aeb686abce9c0fde6f Mon Sep 17 00:00:00 2001 From: Fernando Perez Date: Sat, 19 May 2012 17:27:17 -0700 Subject: [PATCH 028/103] Clean up magic registration api and make it public. --- IPython/core/history.py | 5 +---- IPython/core/interactiveshell.py | 25 ++++++++++--------------- IPython/core/magic.py | 19 +++++++++++++------ 3 files changed, 24 insertions(+), 25 deletions(-) diff --git a/IPython/core/history.py b/IPython/core/history.py index 5ca060f166a..bafcd731256 100644 --- a/IPython/core/history.py +++ b/IPython/core/history.py @@ -985,10 +985,7 @@ def rerun(self, parameter_s=''): def init_ipython(ip): - ip.magics_manager.register(HistoryMagics) - #ip.define_magic('hist', HistoryMagics.history) - #ip.define_magic('recall', HistoryMagics.rep) - + ip.register_magics(HistoryMagics) # XXX - ipy_completers are in quarantine, need to be updated to new apis #import ipy_completers #ipy_completers.quick_completer('%hist' ,'-g -t -r -n') diff --git a/IPython/core/interactiveshell.py b/IPython/core/interactiveshell.py index e73c9bb3363..d9dc730fc8b 100644 --- a/IPython/core/interactiveshell.py +++ b/IPython/core/interactiveshell.py @@ -1999,7 +1999,11 @@ def init_magics(self): user_magics=mf.UserMagics(self)) self.configurables.append(self.magics_manager) - self.magics_manager.register(mf.BasicMagics, mf.CodeMagics, + # Expose as public API new_magic and registere_magics + self.new_magic = self.magics_manager.new_magic + self.register_magics = self.magics_manager.register + + self.register_magics(mf.BasicMagics, mf.CodeMagics, mf.ConfigMagics, mf.NamespaceMagics, mf.ExecutionMagics, mf.AutoMagics, mf.OSMagics, mf.LoggingMagics, mf.ExtensionsMagics, mf.PylabMagics, mf.DeprecatedMagics) @@ -2053,22 +2057,13 @@ def magic(self, arg_s, next_input=None): def define_magic(self, magic_name, func): """Expose own function as magic function for ipython - - Example:: - - def foo_impl(self, parameter_s=''): - 'My very own magic!. (Use docstrings, IPython reads them).' - print 'Magic function. Passed parameter is between < >:' - print '<%s>' % parameter_s - print 'The self object is:', self - ip.define_magic('foo', foo_impl) + Note: this API is now deprecated. Instead, you should use + `get_ipython().new_magic`. """ - return self.magics_manager - im = types.MethodType(func, self._magic) - old = self.find_magic(magic_name) - setattr(self._magic, 'magic_' + magic_name, im) - return old + warn('Deprecated API, use get_ipython().new_magic: %s\n' % + magic_name) + return self.new_magic(func, magic_name) def find_line_magic(self, magic_name): """Find and return a line magic by name.""" diff --git a/IPython/core/magic.py b/IPython/core/magic.py index b9c6022cffc..c5f24d3134d 100644 --- a/IPython/core/magic.py +++ b/IPython/core/magic.py @@ -89,6 +89,11 @@ def register_magics(cls): magics['cell'] = {} return cls +def _record_magic(dct, mtype, mname, func): + if mtype == 'line_cell': + dct['line'][mname] = dct['cell'][mname] = func + else: + dct[mtype][mname] = func def validate_type(magic_type): if magic_type not in magic_types: @@ -185,7 +190,7 @@ def register(self, *magic_objects): for mtype in magic_types: self.magics[mtype].update(m.magics[mtype]) - def define_magic(self, magic_name, func, magic_type='line'): + def new_magic(self, func, magic_name=None, magic_type='line'): """Expose own function as magic function for ipython Example:: @@ -198,11 +203,11 @@ def foo_impl(self, parameter_s=''): ip.define_magic('foo', foo_impl) """ + # Create the new method in the user_magics and register it in the # global table - self.user_magics.new_magic(magic_name, func, magic_type) - self.magics[magic_type][magic_name] = \ - self.user_magics.magics[magic_type][magic_name] + newm, name = self.user_magics.new_magic(func, magic_type, magic_name) + _record_magic(self.magics, magic_type, name, newm) # Key base class that provides the central functionality for magics. @@ -362,10 +367,12 @@ def default_option(self, fn, optstr): error("%s is not a magic function" % fn) self.options_table[fn] = optstr - def new_magic(self, magic_name, func, magic_type='line'): + def new_magic(self, func, magic_type='line', magic_name=None): """TODO """ + magic_name = func.func_name if magic_name is None else magic_name validate_type(magic_type) meth = types.MethodType(func, self) setattr(self, magic_name, meth) - self.magics[magic_type][magic_name] = meth + _record_magic(self.magics, magic_type, magic_name, meth) + return meth, magic_name From 65eda9a9fb2fe89fbc92a854a50e8e8f20d756be Mon Sep 17 00:00:00 2001 From: Fernando Perez Date: Mon, 21 May 2012 12:58:50 -0700 Subject: [PATCH 029/103] Fix simple comment. --- IPython/extensions/sympyprinting.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/IPython/extensions/sympyprinting.py b/IPython/extensions/sympyprinting.py index be0cfb90a41..03ddd0d457a 100644 --- a/IPython/extensions/sympyprinting.py +++ b/IPython/extensions/sympyprinting.py @@ -31,7 +31,7 @@ pass #----------------------------------------------------------------------------- -# Definitions of magic functions for use with IPython +# Definitions of special display functions for use with IPython #----------------------------------------------------------------------------- def print_basic_unicode(o, p, cycle): From 30e75350fbfe33c0a076a0f7cbab8842fc004ab3 Mon Sep 17 00:00:00 2001 From: Fernando Perez Date: Mon, 21 May 2012 17:26:08 -0700 Subject: [PATCH 030/103] Update storemagic extension to new API --- IPython/core/magic.py | 11 +- IPython/extensions/storemagic.py | 244 ++++++++++++++++--------------- 2 files changed, 130 insertions(+), 125 deletions(-) diff --git a/IPython/core/magic.py b/IPython/core/magic.py index c5f24d3134d..eb4e5690994 100644 --- a/IPython/core/magic.py +++ b/IPython/core/magic.py @@ -191,17 +191,8 @@ def register(self, *magic_objects): self.magics[mtype].update(m.magics[mtype]) def new_magic(self, func, magic_name=None, magic_type='line'): - """Expose own function as magic function for ipython + """Expose a standalone function as magic function for ipython. - Example:: - - def foo_impl(self, parameter_s=''): - 'My very own magic!. (Use docstrings, IPython reads them).' - print 'Magic function. Passed parameter is between < >:' - print '<%s>' % parameter_s - print 'The self object is:', self - - ip.define_magic('foo', foo_impl) """ # Create the new method in the user_magics and register it in the diff --git a/IPython/extensions/storemagic.py b/IPython/extensions/storemagic.py index 09e63fc17d0..9ff0bac53e2 100644 --- a/IPython/extensions/storemagic.py +++ b/IPython/extensions/storemagic.py @@ -8,10 +8,10 @@ :file:`ipython_config.py` file:: c.StoreMagic.autorestore = True - """ from IPython.core.error import TryNext, UsageError +from IPython.core.magic import Magics, register_magics, line_magic from IPython.core.plugin import Plugin from IPython.testing.skipdoctest import skip_doctest from IPython.utils import pickleshare @@ -19,6 +19,7 @@ import inspect,pickle,os,sys,textwrap from IPython.core.fakemodule import FakeModule + def restore_aliases(ip): staliases = ip.db.get('stored_aliases', {}) @@ -37,7 +38,7 @@ def refresh_variables(ip): obj = db[key] except KeyError: print "Unable to restore variable '%s', ignoring (use %%store -d to forget!)" % justkey - print "The error was:",sys.exc_info()[0] + print "The error was:", sys.exc_info()[0] else: #print "restored",justkey,"=",obj #dbg ip.user_ns[justkey] = obj @@ -46,141 +47,153 @@ def refresh_variables(ip): def restore_dhist(ip): ip.user_ns['_dh'] = ip.db.get('dhist',[]) + def restore_data(ip): refresh_variables(ip) restore_aliases(ip) restore_dhist(ip) -@skip_doctest -def magic_store(self, parameter_s=''): + +@register_magics +class StoreMagics(Magics): """Lightweight persistence for python variables. - Example:: + Provides the %store magic.""" - In [1]: l = ['hello',10,'world'] - In [2]: %store l - In [3]: exit + @skip_doctest + @line_magic + def store(self, parameter_s=''): + """Lightweight persistence for python variables. - (IPython session is closed and started again...) + Example:: - ville@badger:~$ ipython - In [1]: l - Out[1]: ['hello', 10, 'world'] + In [1]: l = ['hello',10,'world'] + In [2]: %store l + In [3]: exit - Usage: + (IPython session is closed and started again...) - * ``%store`` - Show list of all variables and their current values - * ``%store spam`` - Store the *current* value of the variable spam to disk - * ``%store -d spam`` - Remove the variable and its value from storage - * ``%store -z`` - Remove all variables from storage - * ``%store -r`` - Refresh all variables from store (delete current vals) - * ``%store foo >a.txt`` - Store value of foo to new file a.txt - * ``%store foo >>a.txt`` - Append value of foo to file a.txt + ville@badger:~$ ipython + In [1]: l + Out[1]: ['hello', 10, 'world'] - It should be noted that if you change the value of a variable, you - need to %store it again if you want to persist the new value. + Usage: - Note also that the variables will need to be pickleable; most basic - python types can be safely %store'd. + * ``%store`` - Show list of all variables and their current + values + * ``%store spam`` - Store the *current* value of the variable spam + to disk + * ``%store -d spam`` - Remove the variable and its value from storage + * ``%store -z`` - Remove all variables from storage + * ``%store -r`` - Refresh all variables from store (delete + current vals) + * ``%store foo >a.txt`` - Store value of foo to new file a.txt + * ``%store foo >>a.txt`` - Append value of foo to file a.txt - Also aliases can be %store'd across sessions. - """ + It should be noted that if you change the value of a variable, you + need to %store it again if you want to persist the new value. - opts,argsl = self.parse_options(parameter_s,'drz',mode='string') - args = argsl.split(None,1) - ip = self.shell - db = ip.db - # delete - if opts.has_key('d'): - try: - todel = args[0] - except IndexError: - raise UsageError('You must provide the variable to forget') - else: - try: - del db['autorestore/' + todel] - except: - raise UsageError("Can't delete variable '%s'" % todel) - # reset - elif opts.has_key('z'): - for k in db.keys('autorestore/*'): - del db[k] - - elif opts.has_key('r'): - refresh_variables(ip) - - - # run without arguments -> list variables & values - elif not args: - vars = self.db.keys('autorestore/*') - vars.sort() - if vars: - size = max(map(len,vars)) - else: - size = 0 - - print 'Stored variables and their in-db values:' - fmt = '%-'+str(size)+'s -> %s' - get = db.get - for var in vars: - justkey = os.path.basename(var) - # print 30 first characters from every var - print fmt % (justkey,repr(get(var,''))[:50]) - - # default action - store the variable - else: - # %store foo >file.txt or >>file.txt - if len(args) > 1 and args[1].startswith('>'): - fnam = os.path.expanduser(args[1].lstrip('>').lstrip()) - if args[1].startswith('>>'): - fil = open(fnam,'a') - else: - fil = open(fnam,'w') - obj = ip.ev(args[0]) - print "Writing '%s' (%s) to file '%s'." % (args[0], - obj.__class__.__name__, fnam) + Note also that the variables will need to be pickleable; most basic + python types can be safely %store'd. + Also aliases can be %store'd across sessions. + """ - if not isinstance (obj,basestring): - from pprint import pprint - pprint(obj,fil) + opts,argsl = self.parse_options(parameter_s,'drz',mode='string') + args = argsl.split(None,1) + ip = self.shell + db = ip.db + # delete + if opts.has_key('d'): + try: + todel = args[0] + except IndexError: + raise UsageError('You must provide the variable to forget') else: - fil.write(obj) - if not obj.endswith('\n'): - fil.write('\n') - - fil.close() - return - - # %store foo - try: - obj = ip.user_ns[args[0]] - except KeyError: - # it might be an alias - # This needs to be refactored to use the new AliasManager stuff. - if args[0] in self.alias_manager: - name = args[0] - nargs, cmd = self.alias_manager.alias_table[ name ] - staliases = db.get('stored_aliases',{}) - staliases[ name ] = cmd - db['stored_aliases'] = staliases - print "Alias stored: %s (%s)" % (name, cmd) - return + try: + del db['autorestore/' + todel] + except: + raise UsageError("Can't delete variable '%s'" % todel) + # reset + elif opts.has_key('z'): + for k in db.keys('autorestore/*'): + del db[k] + + elif opts.has_key('r'): + refresh_variables(ip) + + + # run without arguments -> list variables & values + elif not args: + vars = self.db.keys('autorestore/*') + vars.sort() + if vars: + size = max(map(len,vars)) else: - raise UsageError("Unknown variable '%s'" % args[0]) + size = 0 + + print 'Stored variables and their in-db values:' + fmt = '%-'+str(size)+'s -> %s' + get = db.get + for var in vars: + justkey = os.path.basename(var) + # print 30 first characters from every var + print fmt % (justkey,repr(get(var,''))[:50]) + # default action - store the variable else: - if isinstance(inspect.getmodule(obj), FakeModule): - print textwrap.dedent("""\ - Warning:%s is %s - Proper storage of interactively declared classes (or instances - of those classes) is not possible! Only instances - of classes in real modules on file system can be %%store'd. - """ % (args[0], obj) ) + # %store foo >file.txt or >>file.txt + if len(args) > 1 and args[1].startswith('>'): + fnam = os.path.expanduser(args[1].lstrip('>').lstrip()) + if args[1].startswith('>>'): + fil = open(fnam,'a') + else: + fil = open(fnam,'w') + obj = ip.ev(args[0]) + print "Writing '%s' (%s) to file '%s'." % (args[0], + obj.__class__.__name__, fnam) + + + if not isinstance (obj,basestring): + from pprint import pprint + pprint(obj,fil) + else: + fil.write(obj) + if not obj.endswith('\n'): + fil.write('\n') + + fil.close() return - #pickled = pickle.dumps(obj) - self.db[ 'autorestore/' + args[0] ] = obj - print "Stored '%s' (%s)" % (args[0], obj.__class__.__name__) + + # %store foo + try: + obj = ip.user_ns[args[0]] + except KeyError: + # it might be an alias + # This needs to be refactored to use the new AliasManager stuff. + if args[0] in self.alias_manager: + name = args[0] + nargs, cmd = self.alias_manager.alias_table[ name ] + staliases = db.get('stored_aliases',{}) + staliases[ name ] = cmd + db['stored_aliases'] = staliases + print "Alias stored: %s (%s)" % (name, cmd) + return + else: + raise UsageError("Unknown variable '%s'" % args[0]) + + else: + if isinstance(inspect.getmodule(obj), FakeModule): + print textwrap.dedent("""\ + Warning:%s is %s + Proper storage of interactively declared classes (or instances + of those classes) is not possible! Only instances + of classes in real modules on file system can be %%store'd. + """ % (args[0], obj) ) + return + #pickled = pickle.dumps(obj) + self.db[ 'autorestore/' + args[0] ] = obj + print "Stored '%s' (%s)" % (args[0], obj.__class__.__name__) class StoreMagic(Plugin): @@ -189,11 +202,12 @@ class StoreMagic(Plugin): def __init__(self, shell, config): super(StoreMagic, self).__init__(shell=shell, config=config) - shell.define_magic('store', magic_store) + shell.register_magics(StoreMagics) if self.autorestore: restore_data(shell) + _loaded = False def load_ipython_extension(ip): From 9c7626b30265a21fa8f1d0d2684f7468a071361e Mon Sep 17 00:00:00 2001 From: Fernando Perez Date: Mon, 21 May 2012 18:33:04 -0700 Subject: [PATCH 031/103] Settle on cleaner API for magic registration. The official API will be: - ip.register_magics(*args): for registering one or more classes or instances that subclass the main magic.Magics class. This will be the *only* method for registering magics that have a signature f(self, line,...). - ip.function_as_magic: for registering one-off magics made from a standalone function with the signatures f(line), f(line, cell) or f(line, cell=None). We will support, for backwards compatibility, the old ip.define_magic, but it will print a deprecation warning. --- IPython/core/interactiveshell.py | 15 +++------------ IPython/core/magic.py | 31 ++++++++++++++++++------------- 2 files changed, 21 insertions(+), 25 deletions(-) diff --git a/IPython/core/interactiveshell.py b/IPython/core/interactiveshell.py index d9dc730fc8b..352bbda4eb6 100644 --- a/IPython/core/interactiveshell.py +++ b/IPython/core/interactiveshell.py @@ -1999,9 +1999,10 @@ def init_magics(self): user_magics=mf.UserMagics(self)) self.configurables.append(self.magics_manager) - # Expose as public API new_magic and registere_magics - self.new_magic = self.magics_manager.new_magic + # Expose as public API from the magics manager self.register_magics = self.magics_manager.register + self.function_as_magic = self.magics_manager.function_as_magic + self.define_magic = self.magics_manager._define_magic self.register_magics(mf.BasicMagics, mf.CodeMagics, mf.ConfigMagics, mf.NamespaceMagics, mf.ExecutionMagics, @@ -2055,16 +2056,6 @@ def magic(self, arg_s, next_input=None): result = fn(*args) return result - def define_magic(self, magic_name, func): - """Expose own function as magic function for ipython - - Note: this API is now deprecated. Instead, you should use - `get_ipython().new_magic`. - """ - warn('Deprecated API, use get_ipython().new_magic: %s\n' % - magic_name) - return self.new_magic(func, magic_name) - def find_line_magic(self, magic_name): """Find and return a line magic by name.""" return self.magics_manager.magics['line'].get(magic_name) diff --git a/IPython/core/magic.py b/IPython/core/magic.py index eb4e5690994..c0ca043c9b9 100644 --- a/IPython/core/magic.py +++ b/IPython/core/magic.py @@ -30,7 +30,7 @@ from IPython.utils.ipstruct import Struct from IPython.utils.process import arg_split from IPython.utils.traitlets import Bool, Dict, Instance -from IPython.utils.warn import error +from IPython.utils.warn import error, warn #----------------------------------------------------------------------------- # Globals @@ -190,16 +190,31 @@ def register(self, *magic_objects): for mtype in magic_types: self.magics[mtype].update(m.magics[mtype]) - def new_magic(self, func, magic_name=None, magic_type='line'): + def function_as_magic(self, func, magic_type='line', magic_name=None): """Expose a standalone function as magic function for ipython. - """ # Create the new method in the user_magics and register it in the # global table + validate_type(magic_type) + magic_name = func.func_name if magic_name is None else magic_name + setattr(self.user_magics, magic_name, func) newm, name = self.user_magics.new_magic(func, magic_type, magic_name) _record_magic(self.magics, magic_type, name, newm) + + def _define_magic(self, name, func): + """Support for deprecated API. + + This method exists only to support the old-style definition of magics. + It will eventually be removed. Deliberately not documented further. + """ + warn('Deprecated API, use function_as_magic or register_magics: %s\n' % + name) + meth = types.MethodType(func, self.user_magics) + setattr(self.user_magics, name, meth) + _record_magic(self.magics, 'line', name, meth) + # Key base class that provides the central functionality for magics. class Magics(object): @@ -357,13 +372,3 @@ def default_option(self, fn, optstr): if fn not in self.lsmagic(): error("%s is not a magic function" % fn) self.options_table[fn] = optstr - - def new_magic(self, func, magic_type='line', magic_name=None): - """TODO - """ - magic_name = func.func_name if magic_name is None else magic_name - validate_type(magic_type) - meth = types.MethodType(func, self) - setattr(self, magic_name, meth) - _record_magic(self.magics, magic_type, magic_name, meth) - return meth, magic_name From 145a4ac6909471efb24b6e402170eb52622f86f7 Mon Sep 17 00:00:00 2001 From: Fernando Perez Date: Mon, 21 May 2012 18:52:38 -0700 Subject: [PATCH 032/103] Update autoreload: new magics api, various format fixes. --- IPython/extensions/autoreload.py | 84 ++++++++++++++++++++++---------- 1 file changed, 59 insertions(+), 25 deletions(-) diff --git a/IPython/extensions/autoreload.py b/IPython/extensions/autoreload.py index f3ffd1e16d9..daaa8613355 100644 --- a/IPython/extensions/autoreload.py +++ b/IPython/extensions/autoreload.py @@ -1,6 +1,7 @@ -""" -``autoreload`` is an IPython extension that reloads modules -automatically before executing the line of code typed. +"""IPython extension to reload modules before executing user code. + +``autoreload`` reloads modules automatically before entering the execution of +code typed at the IPython prompt. This makes for example the following workflow possible: @@ -20,8 +21,8 @@ In [6]: some_function() Out[6]: 43 -The module was reloaded without reloading it explicitly, and the -object imported with ``from foo import ...`` was also updated. +The module was reloaded without reloading it explicitly, and the object +imported with ``from foo import ...`` was also updated. Usage ===== @@ -84,26 +85,39 @@ - Functions that are removed (eg. via monkey-patching) from a module before it is reloaded are not upgraded. -- C extension modules cannot be reloaded, and so cannot be - autoreloaded. - +- C extension modules cannot be reloaded, and so cannot be autoreloaded. """ skip_doctest = True -# Pauli Virtanen , 2008. -# Thomas Heller, 2000. +#----------------------------------------------------------------------------- +# Copyright (C) 2000 Thomas Heller +# Copyright (C) 2008 Pauli Virtanen +# Copyright (C) 2012 The IPython Development Team +# +# Distributed under the terms of the BSD License. The full license is in +# the file COPYING, distributed as part of this software. +#----------------------------------------------------------------------------- # # This IPython module is written by Pauli Virtanen, based on the autoreload # code by Thomas Heller. -#------------------------------------------------------------------------------ -# Autoreload functionality -#------------------------------------------------------------------------------ - -import time, os, threading, sys, types, imp, inspect, traceback, atexit +#----------------------------------------------------------------------------- +# Imports +#----------------------------------------------------------------------------- +import atexit +import imp +import inspect +import os +import sys +import threading +import time +import traceback +import types import weakref + try: + # Reload is not defined by default in Python3. reload except NameError: from imp import reload @@ -111,14 +125,20 @@ from IPython.utils import pyfile from IPython.utils.py3compat import PY3 +#------------------------------------------------------------------------------ +# Autoreload functionality +#------------------------------------------------------------------------------ + def _get_compiled_ext(): """Official way to get the extension of compiled files (.pyc or .pyo)""" for ext, mode, typ in imp.get_suffixes(): if typ == imp.PY_COMPILED: return ext + PY_COMPILED_EXT = _get_compiled_ext() + class ModuleReloader(object): enabled = False """Whether this reloader is enabled""" @@ -239,6 +259,7 @@ def check(self, check_all=False): func_attrs = ['func_code', 'func_defaults', 'func_doc', 'func_closure', 'func_globals', 'func_dict'] + def update_function(old, new): """Upgrade the code object of a function""" for name in func_attrs: @@ -247,6 +268,7 @@ def update_function(old, new): except (AttributeError, TypeError): pass + def update_class(old, new): """Replace stuff in the __dict__ of a class, and upgrade method code objects""" @@ -270,15 +292,18 @@ def update_class(old, new): except (AttributeError, TypeError): pass # skip non-writable attributes + def update_property(old, new): """Replace get/set/del functions of a property""" update_generic(old.fdel, new.fdel) update_generic(old.fget, new.fget) update_generic(old.fset, new.fset) + def isinstance2(a, b, typ): return isinstance(a, typ) and isinstance(b, typ) + UPDATE_RULES = [ (lambda a, b: isinstance2(a, b, type), update_class), @@ -288,6 +313,7 @@ def isinstance2(a, b, typ): update_property), ] + if PY3: UPDATE_RULES.extend([(lambda a, b: isinstance2(a, b, types.MethodType), lambda a, b: update_function(a.__func__, b.__func__)), @@ -307,12 +333,14 @@ def update_generic(a, b): return True return False + class StrongRef(object): def __init__(self, obj): self.obj = obj def __call__(self): return self.obj + def superreload(module, reload=reload, old_objects={}): """Enhanced version of the builtin reload function. @@ -377,16 +405,19 @@ def superreload(module, reload=reload, old_objects={}): # IPython connectivity #------------------------------------------------------------------------------ -from IPython.core.plugin import Plugin from IPython.core.hooks import TryNext +from IPython.core.magic import Magics, register_magics, line_magic +from IPython.core.plugin import Plugin -class AutoreloadInterface(object): +@register_magics +class AutoreloadMagics(Magics): def __init__(self, *a, **kw): - super(AutoreloadInterface, self).__init__(*a, **kw) + super(AutoreloadMagics, self).__init__(*a, **kw) self._reloader = ModuleReloader() self._reloader.check_all = False - def magic_autoreload(self, ipself, parameter_s=''): + @line_magic + def autoreload(self, parameter_s=''): r"""%autoreload => Reload modules automatically %autoreload @@ -441,7 +472,8 @@ def magic_autoreload(self, ipself, parameter_s=''): self._reloader.check_all = True self._reloader.enabled = True - def magic_aimport(self, ipself, parameter_s='', stream=None): + @line_magic + def aimport(self, parameter_s='', stream=None): """%aimport => Import modules for automatic reloading. %aimport @@ -475,7 +507,7 @@ def magic_aimport(self, ipself, parameter_s='', stream=None): top_module, top_name = self._reloader.aimport_module(modname) # Inject module to user namespace - ipself.push({top_name: top_module}) + self.shell.push({top_name: top_module}) def pre_run_code_hook(self, ipself): if not self._reloader.enabled: @@ -485,16 +517,18 @@ def pre_run_code_hook(self, ipself): except: pass -class AutoreloadPlugin(AutoreloadInterface, Plugin): + +class AutoreloadPlugin(Plugin): def __init__(self, shell=None, config=None): super(AutoreloadPlugin, self).__init__(shell=shell, config=config) + auto = AutoreloadMagics(shell) + self.shell.register_magics(auto) + self.shell.set_hook('pre_run_code_hook', auto.pre_run_code_hook) - self.shell.define_magic('autoreload', self.magic_autoreload) - self.shell.define_magic('aimport', self.magic_aimport) - self.shell.set_hook('pre_run_code_hook', self.pre_run_code_hook) _loaded = False + def load_ipython_extension(ip): """Load the extension in IPython.""" global _loaded From a72dc83dcc68a6b7ad0e1b441d4121071ce57c25 Mon Sep 17 00:00:00 2001 From: Fernando Perez Date: Mon, 21 May 2012 19:06:27 -0700 Subject: [PATCH 033/103] Update terminal-only magics to new API. --- IPython/frontend/terminal/interactiveshell.py | 215 +++++++++--------- 1 file changed, 104 insertions(+), 111 deletions(-) diff --git a/IPython/frontend/terminal/interactiveshell.py b/IPython/frontend/terminal/interactiveshell.py index 58732fb82c5..7b45eb17b2a 100644 --- a/IPython/frontend/terminal/interactiveshell.py +++ b/IPython/frontend/terminal/interactiveshell.py @@ -14,22 +14,18 @@ # Imports #----------------------------------------------------------------------------- -import __builtin__ import bdb import os import re import sys import textwrap -try: - from contextlib import nested -except: - from IPython.utils.nested_context import nested +from contextlib import nested from IPython.core.error import TryNext, UsageError from IPython.core.usage import interactive_usage, default_banner from IPython.core.interactiveshell import InteractiveShell, InteractiveShellABC -from IPython.core.pylabtools import pylab_activate +from IPython.core.magic import Magics, register_magics, line_magic from IPython.testing.skipdoctest import skip_doctest from IPython.utils.encoding import get_stream_enc from IPython.utils import py3compat @@ -124,128 +120,133 @@ def rerun_pasted(shell, name='pasted_block'): # Terminal-specific magics #------------------------------------------------------------------------ -def magic_autoindent(self, parameter_s = ''): - """Toggle autoindent on/off (if available).""" +@register_magics +class TerminalMagics(Magics): - self.shell.set_autoindent() - print "Automatic indentation is:",['OFF','ON'][self.shell.autoindent] + @line_magic + def autoindent(self, parameter_s = ''): + """Toggle autoindent on/off (if available).""" -@skip_doctest -def magic_cpaste(self, parameter_s=''): - """Paste & execute a pre-formatted code block from clipboard. + self.shell.set_autoindent() + print "Automatic indentation is:",['OFF','ON'][self.shell.autoindent] - You must terminate the block with '--' (two minus-signs) or Ctrl-D - alone on the line. You can also provide your own sentinel with '%paste - -s %%' ('%%' is the new sentinel for this operation) + @skip_doctest + @line_magic + def cpaste(self, parameter_s=''): + """Paste & execute a pre-formatted code block from clipboard. - The block is dedented prior to execution to enable execution of method - definitions. '>' and '+' characters at the beginning of a line are - ignored, to allow pasting directly from e-mails, diff files and - doctests (the '...' continuation prompt is also stripped). The - executed block is also assigned to variable named 'pasted_block' for - later editing with '%edit pasted_block'. + You must terminate the block with '--' (two minus-signs) or Ctrl-D + alone on the line. You can also provide your own sentinel with '%paste + -s %%' ('%%' is the new sentinel for this operation) - You can also pass a variable name as an argument, e.g. '%cpaste foo'. - This assigns the pasted block to variable 'foo' as string, without - dedenting or executing it (preceding >>> and + is still stripped) + The block is dedented prior to execution to enable execution of method + definitions. '>' and '+' characters at the beginning of a line are + ignored, to allow pasting directly from e-mails, diff files and + doctests (the '...' continuation prompt is also stripped). The + executed block is also assigned to variable named 'pasted_block' for + later editing with '%edit pasted_block'. - '%cpaste -r' re-executes the block previously entered by cpaste. + You can also pass a variable name as an argument, e.g. '%cpaste foo'. + This assigns the pasted block to variable 'foo' as string, without + dedenting or executing it (preceding >>> and + is still stripped) - Do not be alarmed by garbled output on Windows (it's a readline bug). - Just press enter and type -- (and press enter again) and the block - will be what was just pasted. + '%cpaste -r' re-executes the block previously entered by cpaste. - IPython statements (magics, shell escapes) are not supported (yet). + Do not be alarmed by garbled output on Windows (it's a readline bug). + Just press enter and type -- (and press enter again) and the block + will be what was just pasted. - See also - -------- - paste: automatically pull code from clipboard. + IPython statements (magics, shell escapes) are not supported (yet). - Examples - -------- - :: + See also + -------- + paste: automatically pull code from clipboard. - In [8]: %cpaste - Pasting code; enter '--' alone on the line to stop. - :>>> a = ["world!", "Hello"] - :>>> print " ".join(sorted(a)) - :-- - Hello world! - """ - - opts, name = self.parse_options(parameter_s, 'rs:', mode='string') - if 'r' in opts: - rerun_pasted(self.shell) - return + Examples + -------- + :: - sentinel = opts.get('s', '--') - block = strip_email_quotes(get_pasted_lines(sentinel)) - store_or_execute(self.shell, block, name) + In [8]: %cpaste + Pasting code; enter '--' alone on the line to stop. + :>>> a = ["world!", "Hello"] + :>>> print " ".join(sorted(a)) + :-- + Hello world! + """ + opts, name = self.parse_options(parameter_s, 'rs:', mode='string') + if 'r' in opts: + rerun_pasted(self.shell) + return -def magic_paste(self, parameter_s=''): - """Paste & execute a pre-formatted code block from clipboard. + sentinel = opts.get('s', '--') + block = strip_email_quotes(get_pasted_lines(sentinel)) + store_or_execute(self.shell, block, name) - The text is pulled directly from the clipboard without user - intervention and printed back on the screen before execution (unless - the -q flag is given to force quiet mode). + @line_magic + def paste(self, parameter_s=''): + """Paste & execute a pre-formatted code block from clipboard. - The block is dedented prior to execution to enable execution of method - definitions. '>' and '+' characters at the beginning of a line are - ignored, to allow pasting directly from e-mails, diff files and - doctests (the '...' continuation prompt is also stripped). The - executed block is also assigned to variable named 'pasted_block' for - later editing with '%edit pasted_block'. + The text is pulled directly from the clipboard without user + intervention and printed back on the screen before execution (unless + the -q flag is given to force quiet mode). - You can also pass a variable name as an argument, e.g. '%paste foo'. - This assigns the pasted block to variable 'foo' as string, without - dedenting or executing it (preceding >>> and + is still stripped) + The block is dedented prior to execution to enable execution of method + definitions. '>' and '+' characters at the beginning of a line are + ignored, to allow pasting directly from e-mails, diff files and + doctests (the '...' continuation prompt is also stripped). The + executed block is also assigned to variable named 'pasted_block' for + later editing with '%edit pasted_block'. - Options - ------- + You can also pass a variable name as an argument, e.g. '%paste foo'. + This assigns the pasted block to variable 'foo' as string, without + dedenting or executing it (preceding >>> and + is still stripped) - -r: re-executes the block previously entered by cpaste. + Options + ------- - -q: quiet mode: do not echo the pasted text back to the terminal. + -r: re-executes the block previously entered by cpaste. - IPython statements (magics, shell escapes) are not supported (yet). + -q: quiet mode: do not echo the pasted text back to the terminal. - See also - -------- - cpaste: manually paste code into terminal until you mark its end. - """ - opts, name = self.parse_options(parameter_s, 'rq', mode='string') - if 'r' in opts: - rerun_pasted(self.shell) - return - try: - text = self.shell.hooks.clipboard_get() - block = strip_email_quotes(text.splitlines()) - except TryNext as clipboard_exc: - message = getattr(clipboard_exc, 'args') - if message: - error(message[0]) - else: - error('Could not get text from the clipboard.') - return + IPython statements (magics, shell escapes) are not supported (yet). - # By default, echo back to terminal unless quiet mode is requested - if 'q' not in opts: - write = self.shell.write - write(self.shell.pycolorize(block)) - if not block.endswith('\n'): - write('\n') - write("## -- End pasted text --\n") + See also + -------- + cpaste: manually paste code into terminal until you mark its end. + """ + opts, name = self.parse_options(parameter_s, 'rq', mode='string') + if 'r' in opts: + rerun_pasted(self.shell) + return + try: + text = self.shell.hooks.clipboard_get() + block = strip_email_quotes(text.splitlines()) + except TryNext as clipboard_exc: + message = getattr(clipboard_exc, 'args') + if message: + error(message[0]) + else: + error('Could not get text from the clipboard.') + return - store_or_execute(self.shell, block, name) + # By default, echo back to terminal unless quiet mode is requested + if 'q' not in opts: + write = self.shell.write + write(self.shell.pycolorize(block)) + if not block.endswith('\n'): + write('\n') + write("## -- End pasted text --\n") + store_or_execute(self.shell, block, name) -# Class-level: add a '%cls' magic only on Windows -if sys.platform == 'win32': - def magic_cls(self, s): - """Clear screen. - """ - os.system("cls") + # Class-level: add a '%cls' magic only on Windows + if sys.platform == 'win32': + @line_magic + def cls(self, s): + """Clear screen. + """ + os.system("cls") #----------------------------------------------------------------------------- # Main class @@ -664,15 +665,7 @@ def exit(self): def init_magics(self): super(TerminalInteractiveShell, self).init_magics() - self.define_magic('autoindent', magic_autoindent) - self.define_magic('cpaste', magic_cpaste) - self.define_magic('paste', magic_paste) - try: - magic_cls - except NameError: - pass - else: - self.define_magic('cls', magic_cls) + self.register_magics(TerminalMagics) def showindentationerror(self): super(TerminalInteractiveShell, self).showindentationerror() From 1b3fc25fade5acd9ac565dd2238ca7fa85f970c2 Mon Sep 17 00:00:00 2001 From: Fernando Perez Date: Mon, 21 May 2012 19:17:11 -0700 Subject: [PATCH 034/103] Update parallelmagics to new magic API. --- IPython/extensions/parallelmagic.py | 47 ++++++++++++++++------------- 1 file changed, 26 insertions(+), 21 deletions(-) diff --git a/IPython/extensions/parallelmagic.py b/IPython/extensions/parallelmagic.py index 2677d27a020..f93b01a87c9 100644 --- a/IPython/extensions/parallelmagic.py +++ b/IPython/extensions/parallelmagic.py @@ -37,21 +37,21 @@ import ast import re +from IPython.core.magic import Magics, register_magics, line_magic from IPython.core.plugin import Plugin -from IPython.utils.traitlets import Bool, Any, Instance from IPython.testing.skipdoctest import skip_doctest +from IPython.utils.traitlets import Bool, Any, Instance #----------------------------------------------------------------------------- # Definitions of magic functions for use with IPython #----------------------------------------------------------------------------- - NO_ACTIVE_VIEW = """ Use activate() on a DirectView object to activate it for magics. """ -class ParalleMagic(Plugin): +class ParallelTricks(Plugin): """A component to manage the %result, %px and %autopx magics.""" active_view = Instance('IPython.parallel.client.view.DirectView') @@ -59,19 +59,22 @@ class ParalleMagic(Plugin): shell = Instance('IPython.core.interactiveshell.InteractiveShellABC') def __init__(self, shell=None, config=None): - super(ParalleMagic, self).__init__(shell=shell, config=config) - self._define_magics() + super(ParallelTricks, self).__init__(shell=shell, config=config) + self.shell.register_magics(ParallelMagics) + + +@register_magics +class ParallelMagics(Magics): + """A set of magics useful when controlling a parallel IPython cluster. + """ + def __init__(self, shell): + super(ParallelMagics, self).__init__(shell) # A flag showing if autopx is activated or not self.autopx = False - def _define_magics(self): - """Define the magic functions.""" - self.shell.define_magic('result', self.magic_result) - self.shell.define_magic('px', self.magic_px) - self.shell.define_magic('autopx', self.magic_autopx) - @skip_doctest - def magic_result(self, ipself, parameter_s=''): + @line_magic + def result(self, parameter_s=''): """Print the result of command i on all engines.. To use this a :class:`DirectView` instance must be created @@ -103,7 +106,8 @@ def magic_result(self, ipself, parameter_s=''): return result @skip_doctest - def magic_px(self, ipself, parameter_s=''): + @line_magic + def px(self, parameter_s=''): """Executes the given python command in parallel. To use this a :class:`DirectView` instance must be created @@ -129,7 +133,8 @@ def magic_px(self, ipself, parameter_s=''): self._maybe_display_output(result) @skip_doctest - def magic_autopx(self, ipself, parameter_s=''): + @line_magic + def autopx(self, parameter_s=''): """Toggles auto parallel mode. To use this a :class:`DirectView` instance must be created @@ -282,8 +287,9 @@ def pxrun_code(self, code_obj): """ ipself = self.shell # check code object for the autopx magic - if 'get_ipython' in code_obj.co_names and 'magic' in code_obj.co_names and \ - any( [ isinstance(c, basestring) and 'autopx' in c for c in code_obj.co_consts ]): + if 'get_ipython' in code_obj.co_names and 'magic' in code_obj.co_names \ + and any( [ isinstance(c, basestring) and 'autopx' in c + for c in code_obj.co_consts ]): self._disable_autopx() return False else: @@ -305,11 +311,11 @@ def pxrun_code(self, code_obj): __doc__ = __doc__.replace('@AUTOPX_DOC@', - " " + ParalleMagic.magic_autopx.__doc__) + " " + ParallelTricks.magic_autopx.__doc__) __doc__ = __doc__.replace('@PX_DOC@', - " " + ParalleMagic.magic_px.__doc__) + " " + ParallelTricks.magic_px.__doc__) __doc__ = __doc__.replace('@RESULT_DOC@', - " " + ParalleMagic.magic_result.__doc__) + " " + ParallelTricks.magic_result.__doc__) _loaded = False @@ -319,7 +325,6 @@ def load_ipython_extension(ip): """Load the extension in IPython.""" global _loaded if not _loaded: - plugin = ParalleMagic(shell=ip, config=ip.config) + plugin = ParallelTricks(shell=ip, config=ip.config) ip.plugin_manager.register_plugin('parallelmagic', plugin) _loaded = True - From b9058413a207a8e048b04ebc9fbae8e2e6ca152a Mon Sep 17 00:00:00 2001 From: Fernando Perez Date: Mon, 21 May 2012 19:19:46 -0700 Subject: [PATCH 035/103] Update mglob to new magic API. --- IPython/external/mglob/_mglob.py | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/IPython/external/mglob/_mglob.py b/IPython/external/mglob/_mglob.py index 86056505064..f4da11c748d 100644 --- a/IPython/external/mglob/_mglob.py +++ b/IPython/external/mglob/_mglob.py @@ -210,17 +210,23 @@ def main(): print "\n".join(expand(sys.argv[1:])), -def mglob_f(self, arg): + +def mglob(self, arg): from IPython.utils.text import SList if arg.strip(): return SList(expand(arg)) print "Please specify pattern!" print globsyntax + +mglob.__doc__ = globsyntax + + def init_ipython(ip): """ register %mglob for IPython """ - mglob_f.__doc__ = globsyntax - ip.define_magic("mglob",mglob_f) + + ip.function_as_magic(mglob) + # test() if __name__ == "__main__": From 5df304ca1c025e9b7b094f4b31366983091a3439 Mon Sep 17 00:00:00 2001 From: Fernando Perez Date: Mon, 21 May 2012 19:25:01 -0700 Subject: [PATCH 036/103] Update embedded shell to new magics API. --- IPython/frontend/terminal/embed.py | 46 +++++++++++++++++------------- 1 file changed, 26 insertions(+), 20 deletions(-) diff --git a/IPython/frontend/terminal/embed.py b/IPython/frontend/terminal/embed.py index 7dbd5a8cf7e..5cdead6bdc3 100644 --- a/IPython/frontend/terminal/embed.py +++ b/IPython/frontend/terminal/embed.py @@ -23,16 +23,13 @@ #----------------------------------------------------------------------------- from __future__ import with_statement -import __main__ import sys -try: - from contextlib import nested -except: - from IPython.utils.nested_context import nested +from contextlib import nested import warnings from IPython.core import ultratb +from IPython.core.magic import Magics, register_magics, line_magic from IPython.frontend.terminal.interactiveshell import TerminalInteractiveShell from IPython.frontend.terminal.ipapp import load_default_config @@ -45,21 +42,27 @@ #----------------------------------------------------------------------------- # This is an additional magic that is exposed in embedded shells. -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. 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. - """ +@register_magics +class EmbeddedMagics(Magics): + + @line_magic + 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. 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 = ask_yes_no("Are you sure you want to kill this embedded instance " - "(y/n)? [y/N] ",'n') - if kill: - self.embedded_active = False - print "This embedded IPython will not reactivate anymore once you exit." + kill = ask_yes_no("Are you sure you want to kill this embedded instance " + "(y/n)? [y/N] ",'n') + if kill: + self.shell.embedded_active = False + print ("This embedded IPython will not reactivate anymore " + "once you exit.") class InteractiveShellEmbed(TerminalInteractiveShell): @@ -89,7 +92,6 @@ def __init__(self, config=None, ipython_dir=None, user_ns=None, ) self.exit_msg = exit_msg - self.define_magic("kill_embedded", kill_embedded) # don't use the ipython crash handler so that user exceptions aren't # trapped @@ -100,6 +102,10 @@ def __init__(self, config=None, ipython_dir=None, user_ns=None, def init_sys_modules(self): 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, global_ns=None): """Activate the interactive interpreter. From cc0d5b3606dd828c148acd1c8252a3513bb9c174 Mon Sep 17 00:00:00 2001 From: Fernando Perez Date: Mon, 21 May 2012 19:59:32 -0700 Subject: [PATCH 037/103] Fix various test failures from then new magics API. --- IPython/core/history.py | 7 +++++++ IPython/core/magic.py | 4 +--- IPython/core/magic_functions.py | 10 +++++----- IPython/core/tests/test_magic.py | 13 +++++++++---- IPython/core/tests/test_prefilter.py | 2 +- 5 files changed, 23 insertions(+), 13 deletions(-) diff --git a/IPython/core/history.py b/IPython/core/history.py index bafcd731256..24b7b1f6d24 100644 --- a/IPython/core/history.py +++ b/IPython/core/history.py @@ -884,6 +884,13 @@ def _format_lineno(session, line): if close_at_end: outfile.close() + # For a long time we've had %hist as well as %history + @line_magic + def hist(self, arg): + return self.history(arg) + + hist.__doc__ = history.__doc__ + @line_magic def rep(self, arg): r"""Repeat a command, or get command to input line for editing. diff --git a/IPython/core/magic.py b/IPython/core/magic.py index c0ca043c9b9..b90eb5afd6d 100644 --- a/IPython/core/magic.py +++ b/IPython/core/magic.py @@ -199,9 +199,7 @@ def function_as_magic(self, func, magic_type='line', magic_name=None): validate_type(magic_type) magic_name = func.func_name if magic_name is None else magic_name setattr(self.user_magics, magic_name, func) - newm, name = self.user_magics.new_magic(func, magic_type, magic_name) - _record_magic(self.magics, magic_type, name, newm) - + _record_magic(self.magics, magic_type, magic_name, func) def _define_magic(self, name, func): """Support for deprecated API. diff --git a/IPython/core/magic_functions.py b/IPython/core/magic_functions.py index 1d9bb381557..2530c0b49c7 100644 --- a/IPython/core/magic_functions.py +++ b/IPython/core/magic_functions.py @@ -320,8 +320,8 @@ def xmode_switch_err(name): @line_magic def quickref(self,arg): """ Show a quick reference sheet """ - import IPython.core.usage - qr = IPython.core.usage.quick_reference + self.magic_magic('-brief') + from IPython.core.usage import quick_reference + qr = quick_reference + self.magic('-brief') page.page(qr) @line_magic @@ -522,7 +522,7 @@ def notebook(self, s): "foo.ipynb" to "foo.json" do "%notebook -f json foo.ipynb". Possible formats include (json/ipynb, py). """ - args = magic_arguments.parse_argstring(self.magic_notebook, s) + args = magic_arguments.parse_argstring(self.notebook, s) from IPython.nbformat import current args.filename = unquote_filename(args.filename) @@ -1408,7 +1408,7 @@ def who(self, parameter_s=''): beta """ - varlist = self.magic_who_ls(parameter_s) + varlist = self.who_ls(parameter_s) if not varlist: if parameter_s: print 'No variables match your requested type.' @@ -1459,7 +1459,7 @@ def whos(self, parameter_s=''): beta str test """ - varnames = self.magic_who_ls(parameter_s) + varnames = self.who_ls(parameter_s) if not varnames: if parameter_s: print 'No variables match your requested type.' diff --git a/IPython/core/tests/test_magic.py b/IPython/core/tests/test_magic.py index 04664745a44..8e9a1bdceec 100644 --- a/IPython/core/tests/test_magic.py +++ b/IPython/core/tests/test_magic.py @@ -16,6 +16,8 @@ import nose.tools as nt +from IPython.core import magic +from IPython.core import magic_functions as mf from IPython.nbformat.v3.tests.nbexamples import nb0 from IPython.nbformat import current from IPython.testing import decorators as dec @@ -51,7 +53,8 @@ def test_magic_parse_options(): """Test that we don't mangle paths when parsing magic options.""" ip = get_ipython() path = 'c:\\x' - opts = ip._magic.parse_options('-f %s' % path,'f:')[0] + m = magic.Magics(ip) + opts = m.parse_options('-f %s' % path,'f:')[0] # argv splitting is os-dependent if os.name == 'posix': expected = 'c:x' @@ -284,8 +287,9 @@ def test_parse_options(): """Tests for basic options parsing in magics.""" # These are only the most minimal of tests, more should be added later. At # the very least we check that basic text/unicode calls work OK. - nt.assert_equal(_ip._magic.parse_options('foo', '')[1], 'foo') - nt.assert_equal(_ip._magic.parse_options(u'foo', '')[1], u'foo') + m = magic.Magics(ip) + nt.assert_equal(m.parse_options('foo', '')[1], 'foo') + nt.assert_equal(m.parse_options(u'foo', '')[1], u'foo') def test_dirops(): @@ -422,7 +426,8 @@ def test_timeit_arguments(): "Test valid timeit arguments, should not cause SyntaxError (GH #1269)" _ip.magic("timeit ('#')") -@dec.skipif(_ip._magic.magic_prun == _ip._magic.profile_missing_notice) + +@dec.skipif(mf.profile is None) def test_prun_quotes(): "Test that prun does not clobber string escapes (GH #1302)" _ip.magic("prun -q x = '\t'") diff --git a/IPython/core/tests/test_prefilter.py b/IPython/core/tests/test_prefilter.py index d14aba1ff61..8b262311cc2 100644 --- a/IPython/core/tests/test_prefilter.py +++ b/IPython/core/tests/test_prefilter.py @@ -79,7 +79,7 @@ def test_issue_114(): msp = ip.prefilter_manager.multi_line_specials ip.prefilter_manager.multi_line_specials = False try: - for mgk in ip._magic.lsmagic(): + for mgk in ip.magics_manager.lsmagic()['line']: raw = template % mgk yield nt.assert_equals(ip.prefilter(raw), raw) finally: From ed048d43651114582d241ac59c101dda7220026e Mon Sep 17 00:00:00 2001 From: Fernando Perez Date: Mon, 21 May 2012 20:06:07 -0700 Subject: [PATCH 038/103] Fix a few more test failures from magic API changes. --- IPython/core/magic_functions.py | 4 ++-- IPython/core/tests/test_magic.py | 6 ++++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/IPython/core/magic_functions.py b/IPython/core/magic_functions.py index 2530c0b49c7..65883bea656 100644 --- a/IPython/core/magic_functions.py +++ b/IPython/core/magic_functions.py @@ -3143,7 +3143,7 @@ def pushd(self, parameter_s=''): tgt = os.path.expanduser(unquote_filename(parameter_s)) cwd = os.getcwdu().replace(self.shell.home_dir,'~') if tgt: - self.magic_cd(parameter_s) + self.cd(parameter_s) dir_s.insert(0,cwd) return self.shell.magic('dirs') @@ -3154,7 +3154,7 @@ def popd(self, parameter_s=''): if not self.shell.dir_stack: raise UsageError("%popd on empty stack") top = self.shell.dir_stack.pop(0) - self.magic_cd(top) + self.cd(top) print "popd ->",top @line_magic diff --git a/IPython/core/tests/test_magic.py b/IPython/core/tests/test_magic.py index 8e9a1bdceec..e8f3ea65f5e 100644 --- a/IPython/core/tests/test_magic.py +++ b/IPython/core/tests/test_magic.py @@ -29,6 +29,8 @@ # Test functions begin #----------------------------------------------------------------------------- +@magic.register_magics +class DummyMagics(magic.Magics): pass def test_rehashx(): # clear up everything @@ -53,7 +55,7 @@ def test_magic_parse_options(): """Test that we don't mangle paths when parsing magic options.""" ip = get_ipython() path = 'c:\\x' - m = magic.Magics(ip) + m = DummyMagics(ip) opts = m.parse_options('-f %s' % path,'f:')[0] # argv splitting is os-dependent if os.name == 'posix': @@ -287,7 +289,7 @@ def test_parse_options(): """Tests for basic options parsing in magics.""" # These are only the most minimal of tests, more should be added later. At # the very least we check that basic text/unicode calls work OK. - m = magic.Magics(ip) + m = DummyMagics(_ip) nt.assert_equal(m.parse_options('foo', '')[1], 'foo') nt.assert_equal(m.parse_options(u'foo', '')[1], u'foo') From 4927abda5462c097569aa4bbf4f0692313795d46 Mon Sep 17 00:00:00 2001 From: Fernando Perez Date: Mon, 21 May 2012 20:15:14 -0700 Subject: [PATCH 039/103] Fix invalid attribute accesses in magics code. --- IPython/core/magic_functions.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/IPython/core/magic_functions.py b/IPython/core/magic_functions.py index 65883bea656..08846b61989 100644 --- a/IPython/core/magic_functions.py +++ b/IPython/core/magic_functions.py @@ -801,7 +801,7 @@ def _edit_macro(self,mname,macro): @line_magic def ed(self, parameter_s=''): """Alias to %edit.""" - return self.magic_edit(parameter_s) + return self.edit(parameter_s) @skip_doctest @line_magic @@ -1628,7 +1628,7 @@ def reset(self, parameter_s=''): if 's' in opts: # Soft reset user_ns = self.shell.user_ns - for i in self.magic_who_ls(): + for i in self.who_ls(): del(user_ns[i]) elif len(args) == 0: # Hard reset self.shell.reset(new_session = False) @@ -1764,7 +1764,7 @@ def reset_selective(self, parameter_s=''): m = re.compile(regex) except TypeError: raise TypeError('regex must be a string or compiled pattern') - for i in self.magic_who_ls(): + for i in self.who_ls(): if m.search(i): del(user_ns[i]) @@ -2186,7 +2186,7 @@ def run(self, parameter_s='', runner=None, filename = file_finder(arg_lst[0]) except IndexError: warn('you must provide at least a filename.') - print '\n%run:\n', oinspect.getdoc(self.magic_run) + print '\n%run:\n', oinspect.getdoc(self.run) return except IOError as e: try: @@ -2879,7 +2879,7 @@ def alias(self, parameter_s=''): try: alias,cmd = par.split(None, 1) except: - print oinspect.getdoc(self.magic_alias) + print oinspect.getdoc(self.alias) else: self.shell.alias_manager.soft_define_alias(alias, cmd) # end magic_alias From b1c2e3022498c8c3d60f2fd384725f5ea5ee2817 Mon Sep 17 00:00:00 2001 From: Fernando Perez Date: Mon, 21 May 2012 20:54:26 -0700 Subject: [PATCH 040/103] Fix bugs in completer.py with incompletely-implemented property. From a docstring that ended mid-sentence, it's pretty clear that we'd forgotten to finish the implementation of the completion splitter with real properties. I have no clue how the tests were passing, as there was also a nasty bit of state mangling being done by one of the tests; finished the implementation and fixed the tests. --- IPython/core/completer.py | 29 ++++++++++++++----------- IPython/core/tests/test_completer.py | 32 +++++++++++++++++----------- 2 files changed, 36 insertions(+), 25 deletions(-) diff --git a/IPython/core/completer.py b/IPython/core/completer.py index 333eaddfc11..d451fcd894d 100644 --- a/IPython/core/completer.py +++ b/IPython/core/completer.py @@ -178,11 +178,14 @@ def compress_user(path, tilde_expand, tilde_val): else: return path + class Bunch(object): pass + DELIMS = ' \t\n`!@#$^&*()=+[{]}\\|;:\'",<>?' GREEDY_DELIMS = ' \r\n' + class CompletionSplitter(object): """An object to split an input line in a manner similar to readline. @@ -194,7 +197,7 @@ class CompletionSplitter(object): What characters are used as splitting delimiters can be controlled by setting the `delims` attribute (this is a property that internally - automatically builds the necessary """ + automatically builds the necessary regular expression)""" # Private interface @@ -212,19 +215,21 @@ class CompletionSplitter(object): def __init__(self, delims=None): delims = CompletionSplitter._delims if delims is None else delims - self.set_delims(delims) + self.delims = delims - def set_delims(self, delims): + @property + def delims(self): + """Return the string of delimiter characters.""" + return self._delims + + @delims.setter + def delims(self, delims): """Set the delimiters for line splitting.""" expr = '[' + ''.join('\\'+ c for c in delims) + ']' self._delim_re = re.compile(expr) self._delims = delims self._delim_expr = expr - def get_delims(self): - """Return the string of delimiter characters.""" - return self._delims - def split_line(self, line, cursor_pos=None): """Split a line of text with a cursor at the given position. """ @@ -377,7 +382,7 @@ def attr_matches(self, text): def get__all__entries(obj): """returns the strings in the __all__ attribute""" try: - words = getattr(obj,'__all__') + words = getattr(obj, '__all__') except: return [] @@ -390,12 +395,12 @@ class IPCompleter(Completer): def _greedy_changed(self, name, old, new): """update the splitter and readline delims when greedy is changed""" if new: - self.splitter.set_delims(GREEDY_DELIMS) + self.splitter.delims = GREEDY_DELIMS else: - self.splitter.set_delims(DELIMS) + self.splitter.delims = DELIMS if self.readline: - self.readline.set_completer_delims(self.splitter.get_delims()) + self.readline.set_completer_delims(self.splitter.delims) merge_completions = CBool(True, config=True, help="""Whether to merge completion results into a single list @@ -821,7 +826,7 @@ def complete(self, text=None, line_buffer=None, cursor_pos=None): self.line_buffer = line_buffer self.text_until_cursor = self.line_buffer[:cursor_pos] - #io.rprint('\nCOMP2 %r %r %r' % (text, line_buffer, cursor_pos)) # dbg + #io.rprint('COMP2 %r %r %r' % (text, line_buffer, cursor_pos)) # dbg # Start with a clean slate of completions self.matches[:] = [] diff --git a/IPython/core/tests/test_completer.py b/IPython/core/tests/test_completer.py index 821eedd1f98..15285cca63f 100644 --- a/IPython/core/tests/test_completer.py +++ b/IPython/core/tests/test_completer.py @@ -119,8 +119,8 @@ def setUp(self): self.sp = completer.CompletionSplitter() def test_delim_setting(self): - self.sp.set_delims(' ') - nt.assert_equal(self.sp.get_delims(), ' ') + self.sp.delims = ' ' + nt.assert_equal(self.sp.delims, ' ') nt.assert_equal(self.sp._delim_expr, '[\ ]') def test_spaces(self): @@ -202,13 +202,18 @@ def test_local_file_completions(): def test_greedy_completions(): ip = get_ipython() - ip.Completer.greedy = False - ip.ex('a=range(5)') - _,c = ip.complete('.',line='a[0].') - nt.assert_false('a[0].real' in c, "Shouldn't have completed on a[0]: %s"%c) - ip.Completer.greedy = True - _,c = ip.complete('.',line='a[0].') - nt.assert_true('a[0].real' in c, "Should have completed on a[0]: %s"%c) + greedy_original = ip.Completer.greedy + try: + ip.Completer.greedy = False + ip.ex('a=range(5)') + _,c = ip.complete('.',line='a[0].') + nt.assert_false('a[0].real' in c, + "Shouldn't have completed on a[0]: %s"%c) + ip.Completer.greedy = True + _,c = ip.complete('.',line='a[0].') + nt.assert_true('a[0].real' in c, "Should have completed on a[0]: %s"%c) + finally: + ip.Completer.greedy = greedy_original def test_omit__names(): @@ -280,7 +285,8 @@ def test_func_kw_completions(): ip = get_ipython() c = ip.Completer ip.ex('def myfunc(a=1,b=2): return a+b') - s, matches = c.complete(None,'myfunc(1,b') - nt.assert_true('b=' in matches) - s, matches = c.complete(None,'myfunc(1,b)',10)#cursor is right after b - nt.assert_true('b=' in matches) + s, matches = c.complete(None, 'myfunc(1,b') + nt.assert_in('b=', matches) + # Simulate completing with cursor right after b (pos==10): + s, matches = c.complete(None,'myfunc(1,b)', 10) + nt.assert_in('b=', matches) From ed77ecc806c01c0e2587c6a2be5628c4c671bef6 Mon Sep 17 00:00:00 2001 From: Fernando Perez Date: Mon, 21 May 2012 20:59:52 -0700 Subject: [PATCH 041/103] Fix invalid attribute access in parallelmagic --- IPython/extensions/parallelmagic.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/IPython/extensions/parallelmagic.py b/IPython/extensions/parallelmagic.py index f93b01a87c9..f058338a7b9 100644 --- a/IPython/extensions/parallelmagic.py +++ b/IPython/extensions/parallelmagic.py @@ -311,11 +311,11 @@ def pxrun_code(self, code_obj): __doc__ = __doc__.replace('@AUTOPX_DOC@', - " " + ParallelTricks.magic_autopx.__doc__) + " " + ParallelMagics.autopx.__doc__) __doc__ = __doc__.replace('@PX_DOC@', - " " + ParallelTricks.magic_px.__doc__) + " " + ParallelMagics.px.__doc__) __doc__ = __doc__.replace('@RESULT_DOC@', - " " + ParallelTricks.magic_result.__doc__) + " " + ParallelMagics.result.__doc__) _loaded = False From 6c9a5759a96824c767c18085249d7811f2740293 Mon Sep 17 00:00:00 2001 From: Fernando Perez Date: Mon, 21 May 2012 21:19:12 -0700 Subject: [PATCH 042/103] Several fixes to autoreload extension for new API. I'm still seeing one failure, but this code is quite tricky to test, so this is still in-progress. --- IPython/extensions/autoreload.py | 6 +++--- IPython/extensions/tests/test_autoreload.py | 12 ++++++------ 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/IPython/extensions/autoreload.py b/IPython/extensions/autoreload.py index daaa8613355..b4df8a2e86d 100644 --- a/IPython/extensions/autoreload.py +++ b/IPython/extensions/autoreload.py @@ -521,9 +521,9 @@ def pre_run_code_hook(self, ipself): class AutoreloadPlugin(Plugin): def __init__(self, shell=None, config=None): super(AutoreloadPlugin, self).__init__(shell=shell, config=config) - auto = AutoreloadMagics(shell) - self.shell.register_magics(auto) - self.shell.set_hook('pre_run_code_hook', auto.pre_run_code_hook) + self.auto_magics = AutoreloadMagics(shell) + shell.register_magics(self.auto_magics) + shell.set_hook('pre_run_code_hook', self.auto_magics.pre_run_code_hook) _loaded = False diff --git a/IPython/extensions/tests/test_autoreload.py b/IPython/extensions/tests/test_autoreload.py index d4b4005b144..7b78dabbcdf 100644 --- a/IPython/extensions/tests/test_autoreload.py +++ b/IPython/extensions/tests/test_autoreload.py @@ -9,7 +9,7 @@ import nose.tools as nt import IPython.testing.tools as tt -from IPython.extensions.autoreload import AutoreloadInterface +from IPython.extensions.autoreload import AutoreloadPlugin from IPython.core.hooks import TryNext #----------------------------------------------------------------------------- @@ -19,11 +19,11 @@ class FakeShell(object): def __init__(self): self.ns = {} - self.reloader = AutoreloadInterface() + self.reloader = AutoreloadPlugin(shell=get_ipython()) def run_code(self, code): try: - self.reloader.pre_run_code_hook(self) + self.reloader.auto_magics.pre_run_code_hook(self) except TryNext: pass exec code in self.ns @@ -32,10 +32,10 @@ def push(self, items): self.ns.update(items) def magic_autoreload(self, parameter): - self.reloader.magic_autoreload(self, parameter) + self.reloader.auto_magics.autoreload(parameter) def magic_aimport(self, parameter, stream=None): - self.reloader.magic_aimport(self, parameter, stream=stream) + self.reloader.auto_magics.aimport(parameter, stream=stream) class Fixture(object): @@ -160,7 +160,7 @@ def foo(self): self.shell.magic_aimport("", stream=stream) nt.assert_true("Modules to reload:\nall-except-skipped" in stream.getvalue()) - nt.assert_true(mod_name in self.shell.ns) + nt.assert_in(mod_name, self.shell.ns) mod = sys.modules[mod_name] From f179c85c08c4dc100014b53dc0885274a906120d Mon Sep 17 00:00:00 2001 From: Fernando Perez Date: Mon, 21 May 2012 21:21:04 -0700 Subject: [PATCH 043/103] Fix import for zmqshell --- IPython/zmq/zmqshell.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/IPython/zmq/zmqshell.py b/IPython/zmq/zmqshell.py index b8d420f3088..518c76c1ee8 100644 --- a/IPython/zmq/zmqshell.py +++ b/IPython/zmq/zmqshell.py @@ -33,7 +33,7 @@ from IPython.core.autocall import ZMQExitAutocall from IPython.core.displaypub import DisplayPublisher from IPython.core.macro import Macro -from IPython.core.magic import MacroToEdit +from IPython.core.magic_functions import MacroToEdit from IPython.core.payloadpage import install_payload_page from IPython.lib.kernel import ( get_connection_file, get_connection_info, connect_qtconsole From dd9ca73c5b47502b52efdedf450bb1b2d78ec060 Mon Sep 17 00:00:00 2001 From: Fernando Perez Date: Mon, 21 May 2012 23:33:49 -0700 Subject: [PATCH 044/103] Fix %pylab magic. For a clean solution, added a .registry attribute to magics manager that will record all magic instances recorded. --- IPython/core/interactiveshell.py | 9 +++++---- IPython/core/magic.py | 13 +++++++++++++ 2 files changed, 18 insertions(+), 4 deletions(-) diff --git a/IPython/core/interactiveshell.py b/IPython/core/interactiveshell.py index 352bbda4eb6..961c4bafd2e 100644 --- a/IPython/core/interactiveshell.py +++ b/IPython/core/interactiveshell.py @@ -2004,9 +2004,9 @@ def init_magics(self): self.function_as_magic = self.magics_manager.function_as_magic self.define_magic = self.magics_manager._define_magic - self.register_magics(mf.BasicMagics, mf.CodeMagics, - mf.ConfigMagics, mf.NamespaceMagics, mf.ExecutionMagics, - mf.AutoMagics, mf.OSMagics, mf.LoggingMagics, mf.ExtensionsMagics, + self.register_magics(mf.BasicMagics, mf.CodeMagics, mf.ConfigMagics, + mf.ExecutionMagics, mf.NamespaceMagics, mf.AutoMagics, + mf.OSMagics, mf.LoggingMagics, mf.ExtensionsMagics, mf.PylabMagics, mf.DeprecatedMagics) # FIXME: Move the color initialization to the DisplayHook, which @@ -2695,7 +2695,8 @@ def enable_pylab(self, gui=None, import_all=True): # Now we must activate the gui pylab wants to use, and fix %run to take # plot updates into account self.enable_gui(gui) - self._magic.default_runner = mpl_runner(self.safe_execfile) + self.magics_manager.registry['ExecutionMagics'].default_runner = \ + mpl_runner(self.safe_execfile) #------------------------------------------------------------------------- # Utilities diff --git a/IPython/core/magic.py b/IPython/core/magic.py index b90eb5afd6d..3f48202ab84 100644 --- a/IPython/core/magic.py +++ b/IPython/core/magic.py @@ -144,8 +144,15 @@ class MagicsManager(Configurable): """Object that handles all magic-related functionality for IPython. """ # Non-configurable class attributes + + # A two-level dict, first keyed by magic type, then by magic function, and + # holding the actual callable object as value. This is the dict used for + # magic function dispatch magics = Dict + # A registry of the original objects that we've been given holding magics. + registry = Dict + shell = Instance('IPython.core.interactiveshell.InteractiveShellABC') auto_magic = Bool @@ -161,6 +168,9 @@ def __init__(self, shell=None, config=None, user_magics=None, **traits): super(MagicsManager, self).__init__(shell=shell, config=config, user_magics=user_magics, **traits) self.magics = dict(line={}, cell={}) + # Let's add the user_magics to the registry for uniformity, so *all* + # registered magic containers can be found there. + self.registry[user_magics.__class__.__name__] = user_magics def auto_status(self): """Return descriptive string with automagic status.""" @@ -187,6 +197,9 @@ def register(self, *magic_objects): # If we're given an uninstantiated class m = m(self.shell) + # Now that we have an instance, we can register it and update the + # table of callables + self.registry[m.__class__.__name__] = m for mtype in magic_types: self.magics[mtype].update(m.magics[mtype]) From f44bd72bc1f18937fcb926eb67df8e0f59ddf93b Mon Sep 17 00:00:00 2001 From: Fernando Perez Date: Mon, 21 May 2012 23:44:03 -0700 Subject: [PATCH 045/103] Fix parallel tests to work with new magic API. --- IPython/parallel/client/view.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/IPython/parallel/client/view.py b/IPython/parallel/client/view.py index 90c73e44c53..77075160c86 100644 --- a/IPython/parallel/client/view.py +++ b/IPython/parallel/client/view.py @@ -813,10 +813,10 @@ def activate(self): except NameError: print "The IPython parallel magics (%result, %px, %autopx) only work within IPython." else: - pmagic = ip.plugin_manager.get_plugin('parallelmagic') + pmagic = ip.magics_manager.registry.get('ParallelMagics') if pmagic is None: ip.magic('load_ext parallelmagic') - pmagic = ip.plugin_manager.get_plugin('parallelmagic') + pmagic = ip.magics_manager.registry.get('ParallelMagics') pmagic.active_view = self From bd70445289ffdad79eb54e703e6d9d82723a9445 Mon Sep 17 00:00:00 2001 From: Fernando Perez Date: Tue, 22 May 2012 00:17:53 -0700 Subject: [PATCH 046/103] Fix tests in autoreload extension for new API. --- IPython/core/hooks.py | 6 +++--- IPython/extensions/autoreload.py | 4 +--- IPython/extensions/tests/test_autoreload.py | 21 +++++++++++++++++++-- 3 files changed, 23 insertions(+), 8 deletions(-) diff --git a/IPython/core/hooks.py b/IPython/core/hooks.py index 29028d7b5ec..81340d2b492 100644 --- a/IPython/core/hooks.py +++ b/IPython/core/hooks.py @@ -128,9 +128,9 @@ def __init__(self,commands=None): def __call__(self,*args, **kw): """ Command chain is called just like normal func. - This will call all funcs in chain with the same args as were given to this - function, and return the result of first func that didn't raise - TryNext """ + This will call all funcs in chain with the same args as were given to + this function, and return the result of first func that didn't raise + TryNext""" for prio,cmd in self.chain: #print "prio",prio,"cmd",cmd #dbg diff --git a/IPython/extensions/autoreload.py b/IPython/extensions/autoreload.py index b4df8a2e86d..75b71c6ad35 100644 --- a/IPython/extensions/autoreload.py +++ b/IPython/extensions/autoreload.py @@ -484,9 +484,7 @@ def aimport(self, parameter_s='', stream=None): %aimport -foo Mark module 'foo' to not be autoreloaded for %autoreload 1 - """ - modname = parameter_s if not modname: to_reload = self._reloader.modules.keys() @@ -509,7 +507,7 @@ def aimport(self, parameter_s='', stream=None): # Inject module to user namespace self.shell.push({top_name: top_module}) - def pre_run_code_hook(self, ipself): + def pre_run_code_hook(self, ip): if not self._reloader.enabled: raise TryNext try: diff --git a/IPython/extensions/tests/test_autoreload.py b/IPython/extensions/tests/test_autoreload.py index 7b78dabbcdf..1ac1204acbf 100644 --- a/IPython/extensions/tests/test_autoreload.py +++ b/IPython/extensions/tests/test_autoreload.py @@ -1,3 +1,17 @@ +"""Tests for autoreload extension. +""" +#----------------------------------------------------------------------------- +# Copyright (c) 2012 IPython Development Team. +# +# Distributed under the terms of the Modified BSD License. +# +# The full license is in the file COPYING.txt, distributed with this software. +#----------------------------------------------------------------------------- + +#----------------------------------------------------------------------------- +# Imports +#----------------------------------------------------------------------------- + import os import sys import tempfile @@ -16,10 +30,14 @@ # Test fixture #----------------------------------------------------------------------------- +noop = lambda *a, **kw: None + class FakeShell(object): def __init__(self): self.ns = {} - self.reloader = AutoreloadPlugin(shell=get_ipython()) + self.reloader = AutoreloadPlugin(shell=self) + + register_magics = set_hook = noop def run_code(self, code): try: @@ -81,7 +99,6 @@ def write_file(self, filename, content): future, and without changing the timestamp of the .pyc file (because that is stored in the file). The only reliable way to achieve this seems to be to sleep. - """ # Sleep one second + eps From 20badbe73a2481c4ab569c9de02e03893da0cdec Mon Sep 17 00:00:00 2001 From: Fernando Perez Date: Tue, 22 May 2012 14:43:28 -0700 Subject: [PATCH 047/103] Fix call to set_next_input to happen on shell object --- IPython/core/magic_functions.py | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/IPython/core/magic_functions.py b/IPython/core/magic_functions.py index 08846b61989..6a4bc90ceaa 100644 --- a/IPython/core/magic_functions.py +++ b/IPython/core/magic_functions.py @@ -664,7 +664,7 @@ def loadpy(self, arg_s): else: contents = openpy.read_py_file(arg_s, skip_encoding_cookie=True) - self.set_next_input(contents) + self.shell.set_next_input(contents) def _find_edit_target(self, args, opts, last_call): """Utility method used by magic_edit to find what to edit.""" @@ -2517,11 +2517,6 @@ def timeit(self, parameter_s=''): if tc > tc_min: print "Compiler time: %.2f s" % tc - @cell_magic('timeit') - def cell_timeit(self, line, cell): - """Time execution of a Python cell.""" - raise NotImplementedError - @skip_doctest @needs_local_scope @line_magic From 894a639608f085979b82a239714dbc1f13bbd006 Mon Sep 17 00:00:00 2001 From: Fernando Perez Date: Tue, 22 May 2012 14:57:04 -0700 Subject: [PATCH 048/103] First working version of cell magic support. --- IPython/core/interactiveshell.py | 100 ++++++++++++++++++++++++------- IPython/core/magic.py | 16 +++-- IPython/core/magic_functions.py | 44 ++++++++------ 3 files changed, 112 insertions(+), 48 deletions(-) diff --git a/IPython/core/interactiveshell.py b/IPython/core/interactiveshell.py index 961c4bafd2e..7dd3fe2b652 100644 --- a/IPython/core/interactiveshell.py +++ b/IPython/core/interactiveshell.py @@ -2017,57 +2017,101 @@ def init_magics(self): from IPython.core import history history.init_ipython(self) - def magic(self, arg_s, next_input=None): - """Call a magic function by name. - - Input: a string containing the name of the magic function to call and - any additional arguments to be passed to the magic. - - magic('name -opt foo bar') is equivalent to typing at the ipython - prompt: + def line_magic(self, magic_name, line, next_input=None): + """Execute the given line magic. - In[1]: %name -opt foo bar + Parameters + ---------- + magic_name : str + Name of the desired magic function, without '%' prefix. - To call a magic without arguments, simply use magic('name'). + line : str + The rest of the input line as a single string. - This provides a proper Python function to call IPython's magics in any - valid Python code you can type at the interpreter, including loops and - compound statements. + next_input : str, optional + Text to pre-load into the next input line. """ # Allow setting the next input - this is used if the user does `a=abs?`. # We do this first so that magic functions can override it. if next_input: self.set_next_input(next_input) - magic_name, _, magic_arg_s = arg_s.partition(' ') - magic_name = magic_name.lstrip(prefilter.ESC_MAGIC) - - fn = self.find_magic(magic_name) + fn = self.find_line_magic(magic_name) if fn is None: error("Magic function `%s` not found." % magic_name) else: - magic_arg_s = self.var_expand(magic_arg_s, 1) + # Note: this is the distance in the stack to the user's frame. + # This will need to be updated if the internal calling logic gets + # refactored, or else we'll be expanding the wrong variables. + stack_depth = 2 + magic_arg_s = self.var_expand(line, stack_depth) # Put magic args in a list so we can call with f(*a) syntax args = [magic_arg_s] # Grab local namespace if we need it: if getattr(fn, "needs_local_scope", False): - args.append(sys._getframe(1).f_locals) + args.append(sys._getframe(stack_depth).f_locals) with self.builtin_trap: result = fn(*args) return result + def cell_magic(self, magic_name, line, cell): + """Execute the given cell magic. + """ + fn = self.find_cell_magic(magic_name) + if fn is None: + error("Magic function `%s` not found." % magic_name) + else: + # Note: this is the distance in the stack to the user's frame. + # This will need to be updated if the internal calling logic gets + # refactored, or else we'll be expanding the wrong variables. + stack_depth = 2 + magic_arg_s = self.var_expand(line, stack_depth) + with self.builtin_trap: + result = fn(line, cell) + return result + def find_line_magic(self, magic_name): - """Find and return a line magic by name.""" + """Find and return a line magic by name. + + Returns None if the magic isn't found.""" return self.magics_manager.magics['line'].get(magic_name) def find_cell_magic(self, magic_name): - """Find and return a cell magic by name.""" + """Find and return a cell magic by name. + + Returns None if the magic isn't found.""" return self.magics_manager.magics['cell'].get(magic_name) def find_magic(self, magic_name, magic_type='line'): - """Find and return a magic of the given type by name.""" + """Find and return a magic of the given type by name. + + Returns None if the magic isn't found.""" return self.magics_manager.magics[magic_type].get(magic_name) + def magic(self, arg_s, next_input=None): + """DEPRECATED. Use line_magic() instead. + + Call a magic function by name. + + Input: a string containing the name of the magic function to call and + any additional arguments to be passed to the magic. + + magic('name -opt foo bar') is equivalent to typing at the ipython + prompt: + + In[1]: %name -opt foo bar + + To call a magic without arguments, simply use magic('name'). + + This provides a proper Python function to call IPython's magics in any + valid Python code you can type at the interpreter, including loops and + compound statements. + """ + # TODO: should we issue a loud deprecation warning here? + magic_name, _, magic_arg_s = arg_s.partition(' ') + magic_name = magic_name.lstrip(prefilter.ESC_MAGIC) + return self.line_magic(magic_name, magic_arg_s, next_input) + #------------------------------------------------------------------------- # Things related to macros #------------------------------------------------------------------------- @@ -2436,6 +2480,12 @@ def safe_run_module(self, mod_name, where): self.showtraceback() warn('Unknown failure executing module: <%s>' % mod_name) + def call_cell_magic(self, raw_cell, store_history=False): + line, _, cell = raw_cell.partition(os.linesep) + magic_name, _, line = line.partition(' ') + magic_name = magic_name.lstrip(prefilter.ESC_MAGIC) + return self.cell_magic(magic_name, line, cell) + def run_cell(self, raw_cell, store_history=False, silent=False): """Run a complete IPython cell. @@ -2457,6 +2507,9 @@ def run_cell(self, raw_cell, store_history=False, silent=False): if silent: store_history = False + if raw_cell.startswith('%%'): + return self.call_cell_magic(raw_cell, store_history) + for line in raw_cell.splitlines(): self.input_splitter.push(line) cell = self.input_splitter.source_reset() @@ -2489,7 +2542,8 @@ def run_cell(self, raw_cell, store_history=False, silent=False): with self.display_trap: try: - code_ast = self.compile.ast_parse(cell, filename=cell_name) + code_ast = self.compile.ast_parse(cell, + filename=cell_name) except IndentationError: self.showindentationerror() if store_history: diff --git a/IPython/core/magic.py b/IPython/core/magic.py index 3f48202ab84..eca7996174a 100644 --- a/IPython/core/magic.py +++ b/IPython/core/magic.py @@ -45,6 +45,7 @@ magics = dict(line={}, cell={}) magic_types = ('line', 'cell') +magic_spec = ('line', 'cell', 'line_cell') #----------------------------------------------------------------------------- # Utility classes and functions @@ -89,14 +90,16 @@ def register_magics(cls): magics['cell'] = {} return cls -def _record_magic(dct, mtype, mname, func): + +def record_magic(dct, mtype, mname, func): if mtype == 'line_cell': dct['line'][mname] = dct['cell'][mname] = func else: dct[mtype][mname] = func + def validate_type(magic_type): - if magic_type not in magic_types: + if magic_type not in magic_spec: raise ValueError('magic_type must be one of %s, %s given' % magic_types, magic_type) @@ -115,13 +118,13 @@ def magic_deco(arg): name = func.func_name func.magic_name = name retval = decorator(call, func) - magics[magic_type][name] = name + record_magic(magics, magic_type, name, name) elif isinstance(arg, basestring): # Decorator called with arguments (@foo('bar')) name = arg def mark(func, *a, **kw): func.magic_name = name - magics[magic_type][name] = func.func_name + record_magic(magics, magic_type, name, func.func_name) return decorator(call, func) retval = mark else: @@ -135,6 +138,7 @@ def mark(func, *a, **kw): line_magic = _magic_marker('line') cell_magic = _magic_marker('cell') +line_cell_magic = _magic_marker('line_cell') #----------------------------------------------------------------------------- # Core Magic classes @@ -212,7 +216,7 @@ def function_as_magic(self, func, magic_type='line', magic_name=None): validate_type(magic_type) magic_name = func.func_name if magic_name is None else magic_name setattr(self.user_magics, magic_name, func) - _record_magic(self.magics, magic_type, magic_name, func) + record_magic(self.magics, magic_type, magic_name, func) def _define_magic(self, name, func): """Support for deprecated API. @@ -224,7 +228,7 @@ def _define_magic(self, name, func): name) meth = types.MethodType(func, self.user_magics) setattr(self.user_magics, name, meth) - _record_magic(self.magics, 'line', name, meth) + record_magic(self.magics, 'line', name, meth) # Key base class that provides the central functionality for magics. diff --git a/IPython/core/magic_functions.py b/IPython/core/magic_functions.py index 6a4bc90ceaa..1b06d7b5896 100644 --- a/IPython/core/magic_functions.py +++ b/IPython/core/magic_functions.py @@ -747,7 +747,8 @@ class DataIsObject(Exception): pass # For objects, try to edit the file where they are defined try: filename = inspect.getabsfile(data) - if 'fakemodule' in filename.lower() and inspect.isclass(data): + if 'fakemodule' in filename.lower() and \ + inspect.isclass(data): # class created by %edit? Try to find source # by looking for method definitions instead, the # __module__ in those classes is FakeModule. @@ -756,8 +757,10 @@ class DataIsObject(Exception): pass if not inspect.ismethod(attr): continue filename = inspect.getabsfile(attr) - if filename and 'fakemodule' not in filename.lower(): - # change the attribute to be the edit target instead + if filename and \ + 'fakemodule' not in filename.lower(): + # change the attribute to be the edit + # target instead data = attr break @@ -767,8 +770,8 @@ class DataIsObject(Exception): pass datafile = 1 warn('Could not find file where `%s` is defined.\n' 'Opening a file named `%s`' % (args,filename)) - # Now, make sure we can actually read the source (if it was in - # a temp file it's gone by now). + # Now, make sure we can actually read the source (if it was + # in a temp file it's gone by now). if datafile: try: if lineno is None: @@ -1049,8 +1052,8 @@ def config(self, s): IPCompleter.merge_completions= Current: True Whether to merge completion results into a single list - If False, only the completion results from the first non-empty completer - will be returned. + If False, only the completion results from the first non-empty + completer will be returned. IPCompleter.limit_to__all__= Current: False Instruct the completer to use __all__ for the completion @@ -1060,8 +1063,9 @@ def config(self, s): IPCompleter.greedy= Current: False Activate greedy completion - This will enable completion on elements of lists, results of function calls, - etc., but can be unsafe because the code is actually evaluated on TAB. + This will enable completion on elements of lists, results of + function calls, etc., but can be unsafe because the code is + actually evaluated on TAB. but the real use is in setting values:: @@ -1100,8 +1104,8 @@ def config(self, s): print help return elif '=' not in line: - raise UsageError("Invalid config statement: %r, should be Class.trait = value" % line) - + raise UsageError("Invalid config statement: %r, " + "should be Class.trait = value" % line) # otherwise, assume we are setting configurables. # leave quotes on args when splitting, because we want @@ -1285,7 +1289,8 @@ def psearch(self, parameter_s=''): Show objects beginning with a single _:: - %psearch -a _* list objects beginning with a single underscore""" + %psearch -a _* list objects beginning with a single underscore + """ try: parameter_s.encode('ascii') except UnicodeEncodeError: @@ -1619,7 +1624,8 @@ def reset(self, parameter_s=''): else: try: ans = self.shell.ask_yes_no( - "Once deleted, variables cannot be recovered. Proceed (y/[n])? ", default='n') + "Once deleted, variables cannot be recovered. Proceed (y/[n])?", + default='n') except StdinNotImplementedError: ans = True if not ans: @@ -1651,8 +1657,8 @@ def reset(self, parameter_s=''): user_ns.pop(key,None) user_ns.update(dict(_i=u'',_ii=u'',_iii=u'')) hm = ip.history_manager - # don't delete these, as %save and %macro depending on the length - # of these lists to be preserved + # don't delete these, as %save and %macro depending on the + # length of these lists to be preserved hm.input_hist_parsed[:] = [''] * pc hm.input_hist_raw[:] = [''] * pc # hm has internal machinery for _i,_ii,_iii, clear it out @@ -1662,8 +1668,8 @@ def reset(self, parameter_s=''): # Support cleaning up numpy arrays try: from numpy import ndarray - # This must be done with items and not iteritems because we're - # going to modify the dict in-place. + # This must be done with items and not iteritems because + # we're going to modify the dict in-place. for x,val in user_ns.items(): if isinstance(val,ndarray): del user_ns[x] @@ -2236,8 +2242,8 @@ def run(self, parameter_s='', runner=None, # set the __file__ global in the script's namespace prog_ns['__file__'] = filename - # pickle fix. See interactiveshell for an explanation. But we need to make sure - # that, if we overwrite __main__, we replace it at the end + # pickle fix. See interactiveshell for an explanation. But we need to + # make sure that, if we overwrite __main__, we replace it at the end main_mod_name = prog_ns['__name__'] if main_mod_name == '__main__': From 5d763afea62a4979973e5d25911e7a2c5fb79598 Mon Sep 17 00:00:00 2001 From: Fernando Perez Date: Tue, 22 May 2012 16:28:45 -0700 Subject: [PATCH 049/103] Update api names as per review discussion. --- IPython/core/interactiveshell.py | 4 ++-- IPython/core/magic.py | 6 ++---- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/IPython/core/interactiveshell.py b/IPython/core/interactiveshell.py index 7dd3fe2b652..c20e0dde007 100644 --- a/IPython/core/interactiveshell.py +++ b/IPython/core/interactiveshell.py @@ -2001,8 +2001,8 @@ def init_magics(self): # Expose as public API from the magics manager self.register_magics = self.magics_manager.register - self.function_as_magic = self.magics_manager.function_as_magic - self.define_magic = self.magics_manager._define_magic + self.register_magic_function = self.magics_manager.register_function + self.define_magic = self.magics_manager.define_magic self.register_magics(mf.BasicMagics, mf.CodeMagics, mf.ConfigMagics, mf.ExecutionMagics, mf.NamespaceMagics, mf.AutoMagics, diff --git a/IPython/core/magic.py b/IPython/core/magic.py index eca7996174a..6c375972887 100644 --- a/IPython/core/magic.py +++ b/IPython/core/magic.py @@ -207,7 +207,7 @@ def register(self, *magic_objects): for mtype in magic_types: self.magics[mtype].update(m.magics[mtype]) - def function_as_magic(self, func, magic_type='line', magic_name=None): + def register_function(self, func, magic_type='line', magic_name=None): """Expose a standalone function as magic function for ipython. """ @@ -218,14 +218,12 @@ def function_as_magic(self, func, magic_type='line', magic_name=None): setattr(self.user_magics, magic_name, func) record_magic(self.magics, magic_type, magic_name, func) - def _define_magic(self, name, func): + def define_magic(self, name, func): """Support for deprecated API. This method exists only to support the old-style definition of magics. It will eventually be removed. Deliberately not documented further. """ - warn('Deprecated API, use function_as_magic or register_magics: %s\n' % - name) meth = types.MethodType(func, self.user_magics) setattr(self.user_magics, name, meth) record_magic(self.magics, 'line', name, meth) From 781e85bc379911e47aab9299ce96d2bacb92de83 Mon Sep 17 00:00:00 2001 From: Fernando Perez Date: Tue, 22 May 2012 16:28:57 -0700 Subject: [PATCH 050/103] Simplify parallelmagics by removing unnecessary plugin. --- IPython/extensions/parallelmagic.py | 28 ++++++++-------------------- 1 file changed, 8 insertions(+), 20 deletions(-) diff --git a/IPython/extensions/parallelmagic.py b/IPython/extensions/parallelmagic.py index f058338a7b9..effada2ce5f 100644 --- a/IPython/extensions/parallelmagic.py +++ b/IPython/extensions/parallelmagic.py @@ -24,7 +24,7 @@ """ #----------------------------------------------------------------------------- -# Copyright (C) 2008-2011 The IPython Development Team +# Copyright (C) 2008 The IPython Development Team # # Distributed under the terms of the BSD License. The full license is in # the file COPYING, distributed as part of this software. @@ -38,9 +38,8 @@ import re from IPython.core.magic import Magics, register_magics, line_magic -from IPython.core.plugin import Plugin from IPython.testing.skipdoctest import skip_doctest -from IPython.utils.traitlets import Bool, Any, Instance +from IPython.utils.traitlets import Instance #----------------------------------------------------------------------------- # Definitions of magic functions for use with IPython @@ -51,22 +50,12 @@ """ -class ParallelTricks(Plugin): - """A component to manage the %result, %px and %autopx magics.""" - - active_view = Instance('IPython.parallel.client.view.DirectView') - verbose = Bool(False, config=True) - shell = Instance('IPython.core.interactiveshell.InteractiveShellABC') - - def __init__(self, shell=None, config=None): - super(ParallelTricks, self).__init__(shell=shell, config=config) - self.shell.register_magics(ParallelMagics) - - @register_magics class ParallelMagics(Magics): """A set of magics useful when controlling a parallel IPython cluster. """ + active_view = Instance('IPython.parallel.client.view.DirectView') + def __init__(self, shell): super(ParallelMagics, self).__init__(shell) # A flag showing if autopx is activated or not @@ -242,8 +231,9 @@ def pxrun_cell(self, raw_cell, store_history=False, silent=False): cell_name = ipself.compile.cache(cell, ipself.execution_count) try: - code_ast = ast.parse(cell, filename=cell_name) - except (OverflowError, SyntaxError, ValueError, TypeError, MemoryError): + ast.parse(cell, filename=cell_name) + except (OverflowError, SyntaxError, ValueError, TypeError, + MemoryError): # Case 1 ipself.showsyntaxerror() ipself.execution_count += 1 @@ -317,7 +307,6 @@ def pxrun_code(self, code_obj): __doc__ = __doc__.replace('@RESULT_DOC@', " " + ParallelMagics.result.__doc__) - _loaded = False @@ -325,6 +314,5 @@ def load_ipython_extension(ip): """Load the extension in IPython.""" global _loaded if not _loaded: - plugin = ParallelTricks(shell=ip, config=ip.config) - ip.plugin_manager.register_plugin('parallelmagic', plugin) + ip.register_magics(ParallelMagics) _loaded = True From fe287d7f88a4a22ee263c9dd198b624b160c9a6f Mon Sep 17 00:00:00 2001 From: Fernando Perez Date: Tue, 22 May 2012 16:53:17 -0700 Subject: [PATCH 051/103] Create new core.magics package and start populating with history. --- IPython/core/history.py | 277 ----------------------------- IPython/core/interactiveshell.py | 6 +- IPython/core/magics/__init__.py | 14 ++ IPython/core/magics/history.py | 294 +++++++++++++++++++++++++++++++ 4 files changed, 310 insertions(+), 281 deletions(-) create mode 100644 IPython/core/magics/__init__.py create mode 100644 IPython/core/magics/history.py diff --git a/IPython/core/history.py b/IPython/core/history.py index 24b7b1f6d24..bb03131f902 100644 --- a/IPython/core/history.py +++ b/IPython/core/history.py @@ -15,7 +15,6 @@ # Stdlib imports import atexit import datetime -from io import open as io_open import os import re try: @@ -25,12 +24,8 @@ import threading # Our own packages -from IPython.core.error import StdinNotImplementedError -from IPython.core.magic import Magics, register_magics, line_magic from IPython.config.configurable import Configurable from IPython.external.decorator import decorator -from IPython.testing.skipdoctest import skip_doctest -from IPython.utils import io from IPython.utils.path import locate_profile from IPython.utils.traitlets import Bool, Dict, Instance, Integer, List, Unicode from IPython.utils.warn import warn @@ -724,275 +719,3 @@ def _format_lineno(session, line): return "%s#%s" % (session, line) -@register_magics -class HistoryMagics(Magics): - - @skip_doctest - @line_magic - def history(self, parameter_s = ''): - """Print input history (_i variables), with most recent last. - - %history [-o -p -t -n] [-f filename] [range | -g pattern | -l number] - - By default, input history is printed without line numbers so it can be - directly pasted into an editor. Use -n to show them. - - By default, all input history from the current session is displayed. - Ranges of history can be indicated using the syntax: - 4 : Line 4, current session - 4-6 : Lines 4-6, current session - 243/1-5: Lines 1-5, session 243 - ~2/7 : Line 7, session 2 before current - ~8/1-~6/5 : From the first line of 8 sessions ago, to the fifth line - of 6 sessions ago. - Multiple ranges can be entered, separated by spaces - - The same syntax is used by %macro, %save, %edit, %rerun - - Options: - - -n: print line numbers for each input. - This feature is only available if numbered prompts are in use. - - -o: also print outputs for each input. - - -p: print classic '>>>' python prompts before each input. This is - useful for making documentation, and in conjunction with -o, for - producing doctest-ready output. - - -r: (default) print the 'raw' history, i.e. the actual commands you - typed. - - -t: print the 'translated' history, as IPython understands it. - IPython filters your input and converts it all into valid Python - source before executing it (things like magics or aliases are turned - into function calls, for example). With this option, you'll see the - native history instead of the user-entered version: '%cd /' will be - seen as 'get_ipython().magic("%cd /")' instead of '%cd /'. - - -g: treat the arg as a pattern to grep for in (full) history. - This includes the saved history (almost all commands ever written). - Use '%hist -g' to show full saved history (may be very long). - - -l: get the last n lines from all sessions. Specify n as a single - arg, or the default is the last 10 lines. - - -f FILENAME: instead of printing the output to the screen, redirect - it to the given file. The file is always overwritten, though *when - it can*, IPython asks for confirmation first. In particular, running - the command 'history -f FILENAME' from the IPython Notebook - interface will replace FILENAME even if it already exists *without* - confirmation. - - Examples - -------- - :: - - In [6]: %hist -n 4-6 - 4:a = 12 - 5:print a**2 - 6:%hist -n 4-6 - - """ - - if not self.shell.displayhook.do_full_cache: - print('This feature is only available if numbered prompts ' - 'are in use.') - return - opts,args = self.parse_options(parameter_s,'noprtglf:',mode='string') - - # For brevity - history_manager = self.shell.history_manager - - def _format_lineno(session, line): - """Helper function to format line numbers properly.""" - if session in (0, history_manager.session_number): - return str(line) - return "%s/%s" % (session, line) - - # Check if output to specific file was requested. - try: - outfname = opts['f'] - except KeyError: - outfile = io.stdout # default - # We don't want to close stdout at the end! - close_at_end = False - else: - if os.path.exists(outfname): - try: - ans = io.ask_yes_no("File %r exists. Overwrite?" % outfname) - except StdinNotImplementedError: - ans = True - if not ans: - print('Aborting.') - return - print("Overwriting file.") - outfile = io_open(outfname, 'w', encoding='utf-8') - close_at_end = True - - print_nums = 'n' in opts - get_output = 'o' in opts - pyprompts = 'p' in opts - # Raw history is the default - raw = not('t' in opts) - - pattern = None - - if 'g' in opts: # Glob search - pattern = "*" + args + "*" if args else "*" - hist = history_manager.search(pattern, raw=raw, output=get_output) - print_nums = True - elif 'l' in opts: # Get 'tail' - try: - n = int(args) - except (ValueError, IndexError): - n = 10 - hist = history_manager.get_tail(n, raw=raw, output=get_output) - else: - if args: # Get history by ranges - hist = history_manager.get_range_by_str(args, raw, get_output) - else: # Just get history for the current session - hist = history_manager.get_range(raw=raw, output=get_output) - - # We could be displaying the entire history, so let's not try to pull - # it into a list in memory. Anything that needs more space will just - # misalign. - width = 4 - - for session, lineno, inline in hist: - # Print user history with tabs expanded to 4 spaces. The GUI - # clients use hard tabs for easier usability in auto-indented code, - # but we want to produce PEP-8 compliant history for safe pasting - # into an editor. - if get_output: - inline, output = inline - inline = inline.expandtabs(4).rstrip() - - multiline = "\n" in inline - line_sep = '\n' if multiline else ' ' - if print_nums: - print(u'%s:%s' % (_format_lineno(session, lineno).rjust(width), - line_sep), file=outfile, end=u'') - if pyprompts: - print(u">>> ", end=u"", file=outfile) - if multiline: - inline = "\n... ".join(inline.splitlines()) + "\n..." - print(inline, file=outfile) - if get_output and output: - print(output, file=outfile) - - if close_at_end: - outfile.close() - - # For a long time we've had %hist as well as %history - @line_magic - def hist(self, arg): - return self.history(arg) - - hist.__doc__ = history.__doc__ - - @line_magic - def rep(self, arg): - r"""Repeat a command, or get command to input line for editing. - - %recall and %rep are equivalent. - - - %recall (no arguments): - - Place a string version of last computation result (stored in the - special '_' variable) to the next input prompt. Allows you to create - elaborate command lines without using copy-paste:: - - In[1]: l = ["hei", "vaan"] - In[2]: "".join(l) - Out[2]: heivaan - In[3]: %rep - In[4]: heivaan_ <== cursor blinking - - %recall 45 - - Place history line 45 on the next input prompt. Use %hist to find - out the number. - - %recall 1-4 - - Combine the specified lines into one cell, and place it on the next - input prompt. See %history for the slice syntax. - - %recall foo+bar - - If foo+bar can be evaluated in the user namespace, the result is - placed at the next input prompt. Otherwise, the history is searched - for lines which contain that substring, and the most recent one is - placed at the next input prompt. - """ - if not arg: # Last output - self.shell.set_next_input(str(self.shell.user_ns["_"])) - return - # Get history range - histlines = self.shell.history_manager.get_range_by_str(arg) - cmd = "\n".join(x[2] for x in histlines) - if cmd: - self.shell.set_next_input(cmd.rstrip()) - return - - try: # Variable in user namespace - cmd = str(eval(arg, self.shell.user_ns)) - except Exception: # Search for term in history - histlines = self.shell.history_manager.search("*"+arg+"*") - for h in reversed([x[2] for x in histlines]): - if 'rep' in h: - continue - self.shell.set_next_input(h.rstrip()) - return - else: - self.shell.set_next_input(cmd.rstrip()) - print("Couldn't evaluate or find in history:", arg) - - @line_magic - def rerun(self, parameter_s=''): - """Re-run previous input - - By default, you can specify ranges of input history to be repeated - (as with %history). With no arguments, it will repeat the last line. - - Options: - - -l : Repeat the last n lines of input, not including the - current command. - - -g foo : Repeat the most recent line which contains foo - """ - opts, args = self.parse_options(parameter_s, 'l:g:', mode='string') - if "l" in opts: # Last n lines - n = int(opts['l']) - hist = self.shell.history_manager.get_tail(n) - elif "g" in opts: # Search - p = "*"+opts['g']+"*" - hist = list(self.shell.history_manager.search(p)) - for l in reversed(hist): - if "rerun" not in l[2]: - hist = [l] # The last match which isn't a %rerun - break - else: - hist = [] # No matches except %rerun - elif args: # Specify history ranges - hist = self.shell.history_manager.get_range_by_str(args) - else: # Last line - hist = self.shell.history_manager.get_tail(1) - hist = [x[2] for x in hist] - if not hist: - print("No lines in history match specification") - return - histlines = "\n".join(hist) - print("=== Executing: ===") - print(histlines) - print("=== Output: ===") - self.shell.run_cell("\n".join(hist), store_history=False) - - -def init_ipython(ip): - ip.register_magics(HistoryMagics) - # XXX - ipy_completers are in quarantine, need to be updated to new apis - #import ipy_completers - #ipy_completers.quick_completer('%hist' ,'-g -t -r -n') diff --git a/IPython/core/interactiveshell.py b/IPython/core/interactiveshell.py index c20e0dde007..1068a5ae247 100644 --- a/IPython/core/interactiveshell.py +++ b/IPython/core/interactiveshell.py @@ -1994,6 +1994,7 @@ def set_completer_frame(self, frame=None): def init_magics(self): from IPython.core import magic_functions as mf + from IPython.core import magics as m self.magics_manager = magic.MagicsManager(shell=self, confg=self.config, user_magics=mf.UserMagics(self)) @@ -2007,15 +2008,12 @@ def init_magics(self): self.register_magics(mf.BasicMagics, mf.CodeMagics, mf.ConfigMagics, mf.ExecutionMagics, mf.NamespaceMagics, mf.AutoMagics, mf.OSMagics, mf.LoggingMagics, mf.ExtensionsMagics, - mf.PylabMagics, mf.DeprecatedMagics) + mf.PylabMagics, m.HistoryMagics, mf.DeprecatedMagics) # FIXME: Move the color initialization to the DisplayHook, which # should be split into a prompt manager and displayhook. We probably # even need a centralize colors management object. self.magic('colors %s' % self.colors) - # History was moved to a separate module - from IPython.core import history - history.init_ipython(self) def line_magic(self, magic_name, line, next_input=None): """Execute the given line magic. diff --git a/IPython/core/magics/__init__.py b/IPython/core/magics/__init__.py new file mode 100644 index 00000000000..91df414ef49 --- /dev/null +++ b/IPython/core/magics/__init__.py @@ -0,0 +1,14 @@ +"""Implementation of all the magic functions built into IPython. +""" +#----------------------------------------------------------------------------- +# Copyright (c) 2012, IPython Development Team. +# +# Distributed under the terms of the Modified BSD License. +# +# The full license is in the file COPYING.txt, distributed with this software. +#----------------------------------------------------------------------------- + +#----------------------------------------------------------------------------- +# Imports +#----------------------------------------------------------------------------- +from history import (HistoryMagics) diff --git a/IPython/core/magics/history.py b/IPython/core/magics/history.py new file mode 100644 index 00000000000..4516582e1ea --- /dev/null +++ b/IPython/core/magics/history.py @@ -0,0 +1,294 @@ +"""Implementation of magic functions related to History. +""" +#----------------------------------------------------------------------------- +# Copyright (c) 2012, IPython Development Team. +# +# Distributed under the terms of the Modified BSD License. +# +# The full license is in the file COPYING.txt, distributed with this software. +#----------------------------------------------------------------------------- + +#----------------------------------------------------------------------------- +# Imports +#----------------------------------------------------------------------------- +from __future__ import print_function + +# Stdlib +import os +from io import open as io_open + +# Our own packages +from IPython.core.error import StdinNotImplementedError +from IPython.core.magic import Magics, register_magics, line_magic +from IPython.testing.skipdoctest import skip_doctest +from IPython.utils import io + +#----------------------------------------------------------------------------- +# Magics class implementation +#----------------------------------------------------------------------------- + +@register_magics +class HistoryMagics(Magics): + + @skip_doctest + @line_magic + def history(self, parameter_s = ''): + """Print input history (_i variables), with most recent last. + + %history [-o -p -t -n] [-f filename] [range | -g pattern | -l number] + + By default, input history is printed without line numbers so it can be + directly pasted into an editor. Use -n to show them. + + By default, all input history from the current session is displayed. + Ranges of history can be indicated using the syntax: + 4 : Line 4, current session + 4-6 : Lines 4-6, current session + 243/1-5: Lines 1-5, session 243 + ~2/7 : Line 7, session 2 before current + ~8/1-~6/5 : From the first line of 8 sessions ago, to the fifth line + of 6 sessions ago. + Multiple ranges can be entered, separated by spaces + + The same syntax is used by %macro, %save, %edit, %rerun + + Options: + + -n: print line numbers for each input. + This feature is only available if numbered prompts are in use. + + -o: also print outputs for each input. + + -p: print classic '>>>' python prompts before each input. This is + useful for making documentation, and in conjunction with -o, for + producing doctest-ready output. + + -r: (default) print the 'raw' history, i.e. the actual commands you + typed. + + -t: print the 'translated' history, as IPython understands it. + IPython filters your input and converts it all into valid Python + source before executing it (things like magics or aliases are turned + into function calls, for example). With this option, you'll see the + native history instead of the user-entered version: '%cd /' will be + seen as 'get_ipython().magic("%cd /")' instead of '%cd /'. + + -g: treat the arg as a pattern to grep for in (full) history. + This includes the saved history (almost all commands ever written). + Use '%hist -g' to show full saved history (may be very long). + + -l: get the last n lines from all sessions. Specify n as a single + arg, or the default is the last 10 lines. + + -f FILENAME: instead of printing the output to the screen, redirect + it to the given file. The file is always overwritten, though *when + it can*, IPython asks for confirmation first. In particular, running + the command 'history -f FILENAME' from the IPython Notebook + interface will replace FILENAME even if it already exists *without* + confirmation. + + Examples + -------- + :: + + In [6]: %hist -n 4-6 + 4:a = 12 + 5:print a**2 + 6:%hist -n 4-6 + + """ + + if not self.shell.displayhook.do_full_cache: + print('This feature is only available if numbered prompts ' + 'are in use.') + return + opts,args = self.parse_options(parameter_s,'noprtglf:',mode='string') + + # For brevity + history_manager = self.shell.history_manager + + def _format_lineno(session, line): + """Helper function to format line numbers properly.""" + if session in (0, history_manager.session_number): + return str(line) + return "%s/%s" % (session, line) + + # Check if output to specific file was requested. + try: + outfname = opts['f'] + except KeyError: + outfile = io.stdout # default + # We don't want to close stdout at the end! + close_at_end = False + else: + if os.path.exists(outfname): + try: + ans = io.ask_yes_no("File %r exists. Overwrite?" % outfname) + except StdinNotImplementedError: + ans = True + if not ans: + print('Aborting.') + return + print("Overwriting file.") + outfile = io_open(outfname, 'w', encoding='utf-8') + close_at_end = True + + print_nums = 'n' in opts + get_output = 'o' in opts + pyprompts = 'p' in opts + # Raw history is the default + raw = not('t' in opts) + + pattern = None + + if 'g' in opts: # Glob search + pattern = "*" + args + "*" if args else "*" + hist = history_manager.search(pattern, raw=raw, output=get_output) + print_nums = True + elif 'l' in opts: # Get 'tail' + try: + n = int(args) + except (ValueError, IndexError): + n = 10 + hist = history_manager.get_tail(n, raw=raw, output=get_output) + else: + if args: # Get history by ranges + hist = history_manager.get_range_by_str(args, raw, get_output) + else: # Just get history for the current session + hist = history_manager.get_range(raw=raw, output=get_output) + + # We could be displaying the entire history, so let's not try to pull + # it into a list in memory. Anything that needs more space will just + # misalign. + width = 4 + + for session, lineno, inline in hist: + # Print user history with tabs expanded to 4 spaces. The GUI + # clients use hard tabs for easier usability in auto-indented code, + # but we want to produce PEP-8 compliant history for safe pasting + # into an editor. + if get_output: + inline, output = inline + inline = inline.expandtabs(4).rstrip() + + multiline = "\n" in inline + line_sep = '\n' if multiline else ' ' + if print_nums: + print(u'%s:%s' % (_format_lineno(session, lineno).rjust(width), + line_sep), file=outfile, end=u'') + if pyprompts: + print(u">>> ", end=u"", file=outfile) + if multiline: + inline = "\n... ".join(inline.splitlines()) + "\n..." + print(inline, file=outfile) + if get_output and output: + print(output, file=outfile) + + if close_at_end: + outfile.close() + + # For a long time we've had %hist as well as %history + @line_magic + def hist(self, arg): + return self.history(arg) + + hist.__doc__ = history.__doc__ + + @line_magic + def rep(self, arg): + r"""Repeat a command, or get command to input line for editing. + + %recall and %rep are equivalent. + + - %recall (no arguments): + + Place a string version of last computation result (stored in the + special '_' variable) to the next input prompt. Allows you to create + elaborate command lines without using copy-paste:: + + In[1]: l = ["hei", "vaan"] + In[2]: "".join(l) + Out[2]: heivaan + In[3]: %rep + In[4]: heivaan_ <== cursor blinking + + %recall 45 + + Place history line 45 on the next input prompt. Use %hist to find + out the number. + + %recall 1-4 + + Combine the specified lines into one cell, and place it on the next + input prompt. See %history for the slice syntax. + + %recall foo+bar + + If foo+bar can be evaluated in the user namespace, the result is + placed at the next input prompt. Otherwise, the history is searched + for lines which contain that substring, and the most recent one is + placed at the next input prompt. + """ + if not arg: # Last output + self.shell.set_next_input(str(self.shell.user_ns["_"])) + return + # Get history range + histlines = self.shell.history_manager.get_range_by_str(arg) + cmd = "\n".join(x[2] for x in histlines) + if cmd: + self.shell.set_next_input(cmd.rstrip()) + return + + try: # Variable in user namespace + cmd = str(eval(arg, self.shell.user_ns)) + except Exception: # Search for term in history + histlines = self.shell.history_manager.search("*"+arg+"*") + for h in reversed([x[2] for x in histlines]): + if 'rep' in h: + continue + self.shell.set_next_input(h.rstrip()) + return + else: + self.shell.set_next_input(cmd.rstrip()) + print("Couldn't evaluate or find in history:", arg) + + @line_magic + def rerun(self, parameter_s=''): + """Re-run previous input + + By default, you can specify ranges of input history to be repeated + (as with %history). With no arguments, it will repeat the last line. + + Options: + + -l : Repeat the last n lines of input, not including the + current command. + + -g foo : Repeat the most recent line which contains foo + """ + opts, args = self.parse_options(parameter_s, 'l:g:', mode='string') + if "l" in opts: # Last n lines + n = int(opts['l']) + hist = self.shell.history_manager.get_tail(n) + elif "g" in opts: # Search + p = "*"+opts['g']+"*" + hist = list(self.shell.history_manager.search(p)) + for l in reversed(hist): + if "rerun" not in l[2]: + hist = [l] # The last match which isn't a %rerun + break + else: + hist = [] # No matches except %rerun + elif args: # Specify history ranges + hist = self.shell.history_manager.get_range_by_str(args) + else: # Last line + hist = self.shell.history_manager.get_tail(1) + hist = [x[2] for x in hist] + if not hist: + print("No lines in history match specification") + return + histlines = "\n".join(hist) + print("=== Executing: ===") + print(histlines) + print("=== Output: ===") + self.shell.run_cell("\n".join(hist), store_history=False) From daebec637319b184fd3a83d17bff15780e8e127a Mon Sep 17 00:00:00 2001 From: Fernando Perez Date: Tue, 22 May 2012 16:57:25 -0700 Subject: [PATCH 052/103] Move UserMagics to core.magics --- IPython/core/interactiveshell.py | 2 +- IPython/core/magic.py | 2 +- IPython/core/magic_functions.py | 14 -------------- IPython/core/magics/__init__.py | 15 +++++++++++++++ 4 files changed, 17 insertions(+), 16 deletions(-) diff --git a/IPython/core/interactiveshell.py b/IPython/core/interactiveshell.py index 1068a5ae247..0298def09cd 100644 --- a/IPython/core/interactiveshell.py +++ b/IPython/core/interactiveshell.py @@ -1997,7 +1997,7 @@ def init_magics(self): from IPython.core import magics as m self.magics_manager = magic.MagicsManager(shell=self, confg=self.config, - user_magics=mf.UserMagics(self)) + user_magics=m.UserMagics(self)) self.configurables.append(self.magics_manager) # Expose as public API from the magics manager diff --git a/IPython/core/magic.py b/IPython/core/magic.py index 6c375972887..230175a28ef 100644 --- a/IPython/core/magic.py +++ b/IPython/core/magic.py @@ -165,7 +165,7 @@ class MagicsManager(Configurable): 'Automagic is OFF, % prefix IS needed for magic functions.', 'Automagic is ON, % prefix IS NOT needed for magic functions.'] - user_magics = Instance('IPython.core.magic_functions.UserMagics') + user_magics = Instance('IPython.core.magics.UserMagics') def __init__(self, shell=None, config=None, user_magics=None, **traits): diff --git a/IPython/core/magic_functions.py b/IPython/core/magic_functions.py index 1b06d7b5896..06acb23bd64 100644 --- a/IPython/core/magic_functions.py +++ b/IPython/core/magic_functions.py @@ -61,20 +61,6 @@ from IPython.utils.timing import clock, clock2 from IPython.utils.warn import warn, error -#----------------------------------------------------------------------------- -# Magic implementation classes -#----------------------------------------------------------------------------- - -@register_magics -class UserMagics(Magics): - """Placeholder for user-defined magics to be added at runtime. - - All magics are eventually merged into a single namespace at runtime, but we - use this class to isolate the magics defined dynamically by the user into - their own class. - """ - - @register_magics class BasicMagics(Magics): """Magics that provide central IPython functionality. diff --git a/IPython/core/magics/__init__.py b/IPython/core/magics/__init__.py index 91df414ef49..bf110ad26c1 100644 --- a/IPython/core/magics/__init__.py +++ b/IPython/core/magics/__init__.py @@ -11,4 +11,19 @@ #----------------------------------------------------------------------------- # Imports #----------------------------------------------------------------------------- +from IPython.core.magic import Magics, register_magics from history import (HistoryMagics) + + +#----------------------------------------------------------------------------- +# Magic implementation classes +#----------------------------------------------------------------------------- + +@register_magics +class UserMagics(Magics): + """Placeholder for user-defined magics to be added at runtime. + + All magics are eventually merged into a single namespace at runtime, but we + use this class to isolate the magics defined dynamically by the user into + their own class. + """ From 302f198c55585daf210cc1d63b440cb39bfa832d Mon Sep 17 00:00:00 2001 From: Fernando Perez Date: Tue, 22 May 2012 17:00:32 -0700 Subject: [PATCH 053/103] Cleanup of storemagic with pyflakes. --- IPython/extensions/storemagic.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/IPython/extensions/storemagic.py b/IPython/extensions/storemagic.py index 9ff0bac53e2..607d2ab6bfe 100644 --- a/IPython/extensions/storemagic.py +++ b/IPython/extensions/storemagic.py @@ -10,16 +10,15 @@ c.StoreMagic.autorestore = True """ -from IPython.core.error import TryNext, UsageError +import inspect, os, sys, textwrap + +from IPython.core.error import UsageError +from IPython.core.fakemodule import FakeModule from IPython.core.magic import Magics, register_magics, line_magic from IPython.core.plugin import Plugin from IPython.testing.skipdoctest import skip_doctest -from IPython.utils import pickleshare from IPython.utils.traitlets import Bool, Instance -import inspect,pickle,os,sys,textwrap -from IPython.core.fakemodule import FakeModule - def restore_aliases(ip): staliases = ip.db.get('stored_aliases', {}) From 64de6d7b73a2baccd5bdfa7ae3a193d986d04b56 Mon Sep 17 00:00:00 2001 From: Fernando Perez Date: Wed, 23 May 2012 12:05:15 -0700 Subject: [PATCH 054/103] Create core.magics.basic according to new API. --- IPython/core/interactiveshell.py | 2 +- IPython/core/magic_functions.py | 483 +---------------------------- IPython/core/magics/__init__.py | 5 +- IPython/core/magics/basic.py | 513 +++++++++++++++++++++++++++++++ 4 files changed, 518 insertions(+), 485 deletions(-) create mode 100644 IPython/core/magics/basic.py diff --git a/IPython/core/interactiveshell.py b/IPython/core/interactiveshell.py index 0298def09cd..aabf0352d45 100644 --- a/IPython/core/interactiveshell.py +++ b/IPython/core/interactiveshell.py @@ -2005,7 +2005,7 @@ def init_magics(self): self.register_magic_function = self.magics_manager.register_function self.define_magic = self.magics_manager.define_magic - self.register_magics(mf.BasicMagics, mf.CodeMagics, mf.ConfigMagics, + self.register_magics(m.BasicMagics, mf.CodeMagics, mf.ConfigMagics, mf.ExecutionMagics, mf.NamespaceMagics, mf.AutoMagics, mf.OSMagics, mf.LoggingMagics, mf.ExtensionsMagics, mf.PylabMagics, m.HistoryMagics, mf.DeprecatedMagics) diff --git a/IPython/core/magic_functions.py b/IPython/core/magic_functions.py index 06acb23bd64..f1f2c5e76c3 100644 --- a/IPython/core/magic_functions.py +++ b/IPython/core/magic_functions.py @@ -40,13 +40,12 @@ from IPython.config.application import Application from IPython.core import debugger, oinspect -from IPython.core import magic_arguments, page +from IPython.core import page from IPython.core.error import UsageError, StdinNotImplementedError, TryNext from IPython.core.macro import Macro from IPython.core.magic import (Bunch, Magics, compress_dhist, on_off, needs_local_scope, register_magics, line_magic, cell_magic) -from IPython.core.prefilter import ESC_MAGIC from IPython.testing.skipdoctest import skip_doctest from IPython.utils import openpy from IPython.utils import py3compat @@ -57,489 +56,9 @@ from IPython.utils.path import get_py_filename, unquote_filename from IPython.utils.process import abbrev_cwd from IPython.utils.terminal import set_term_title -from IPython.utils.text import format_screen from IPython.utils.timing import clock, clock2 from IPython.utils.warn import warn, error -@register_magics -class BasicMagics(Magics): - """Magics that provide central IPython functionality. - - These are various magics that don't fit into specific categories but that - are all part of the base 'IPython experience'.""" - - def _lsmagic(self): - mesc = ESC_MAGIC - cesc = mesc*2 - mman = self.shell.magics_manager - magics = mman.lsmagic() - out = ['Available line magics:', - mesc + (' '+mesc).join(magics['line']), - '', - 'Available cell magics:', - cesc + (' '+cesc).join(magics['cell']), - '', - mman.auto_status()] - return '\n'.join(out) - - @line_magic - def lsmagic(self, parameter_s=''): - """List currently available magic functions.""" - print self._lsmagic() - - @line_magic - def magic(self, parameter_s=''): - """Print information about the magic function system. - - Supported formats: -latex, -brief, -rest - """ - - mode = '' - try: - mode = parameter_s.split()[0][1:] - if mode == 'rest': - rest_docs = [] - except: - pass - - magic_docs = [] - escapes = dict(line=ESC_MAGIC, cell=ESC_MAGIC*2) - magics = self.shell.magics_manager.magics - - for mtype in ('line', 'cell'): - escape = escapes[mtype] - for fname, fn in magics[mtype].iteritems(): - - if mode == 'brief': - # only first line - if fn.__doc__: - fndoc = fn.__doc__.split('\n',1)[0] - else: - fndoc = 'No documentation' - else: - if fn.__doc__: - fndoc = fn.__doc__.rstrip() - else: - fndoc = 'No documentation' - - if mode == 'rest': - rest_docs.append('**%s%s**::\n\n\t%s\n\n' % - (escape, fname, fndoc)) - else: - magic_docs.append('%s%s:\n\t%s\n' % - (escape, fname, fndoc)) - - magic_docs = ''.join(magic_docs) - - if mode == 'rest': - return "".join(rest_docs) - - if mode == 'latex': - print self.format_latex(magic_docs) - return - else: - magic_docs = format_screen(magic_docs) - if mode == 'brief': - return magic_docs - - out = [""" -IPython's 'magic' functions -=========================== - -The magic function system provides a series of functions which allow you to -control the behavior of IPython itself, plus a lot of system-type -features. All these functions are prefixed with a % character, but parameters -are given without parentheses or quotes. - -NOTE: If you have 'automagic' enabled (via the command line option or with the -%automagic function), you don't need to type in the % explicitly. By default, -IPython ships with automagic on, so you should only rarely need the % escape. - -Example: typing '%cd mydir' (without the quotes) changes you working directory -to 'mydir', if it exists. - -For a list of the available magic functions, use %lsmagic. For a description -of any of them, type %magic_name?, e.g. '%cd?'. - -Currently the magic system has the following functions:""", - magic_docs, - "Summary of magic functions (from %slsmagic):", - self._lsmagic(), - ] - page.page('\n'.join(out)) - - - @line_magic - def page(self, parameter_s=''): - """Pretty print the object and display it through a pager. - - %page [options] OBJECT - - If no object is given, use _ (last output). - - Options: - - -r: page str(object), don't pretty-print it.""" - - # After a function contributed by Olivier Aubert, slightly modified. - - # Process options/args - opts, args = self.parse_options(parameter_s, 'r') - raw = 'r' in opts - - oname = args and args or '_' - info = self._ofind(oname) - if info['found']: - txt = (raw and str or pformat)( info['obj'] ) - page.page(txt) - else: - print 'Object `%s` not found' % oname - - @line_magic - def profile(self, parameter_s=''): - """Print your currently active IPython profile.""" - from IPython.core.application import BaseIPythonApplication - if BaseIPythonApplication.initialized(): - print BaseIPythonApplication.instance().profile - else: - error("profile is an application-level value, but you don't appear to be in an IPython application") - - @line_magic - def pprint(self, parameter_s=''): - """Toggle pretty printing on/off.""" - ptformatter = self.shell.display_formatter.formatters['text/plain'] - ptformatter.pprint = bool(1 - ptformatter.pprint) - print 'Pretty printing has been turned', \ - ['OFF','ON'][ptformatter.pprint] - - @line_magic - def colors(self, parameter_s=''): - """Switch color scheme for prompts, info system and exception handlers. - - Currently implemented schemes: NoColor, Linux, LightBG. - - Color scheme names are not case-sensitive. - - Examples - -------- - To get a plain black and white terminal:: - - %colors nocolor - """ - def color_switch_err(name): - warn('Error changing %s color schemes.\n%s' % - (name,sys.exc_info()[1])) - - - new_scheme = parameter_s.strip() - if not new_scheme: - raise UsageError( - "%colors: you must specify a color scheme. See '%colors?'") - return - # local shortcut - shell = self.shell - - import IPython.utils.rlineimpl as readline - - if not shell.colors_force and \ - not readline.have_readline and sys.platform == "win32": - msg = """\ -Proper color support under MS Windows requires the pyreadline library. -You can find it at: -http://ipython.org/pyreadline.html -Gary's readline needs the ctypes module, from: -http://starship.python.net/crew/theller/ctypes -(Note that ctypes is already part of Python versions 2.5 and newer). - -Defaulting color scheme to 'NoColor'""" - new_scheme = 'NoColor' - warn(msg) - - # readline option is 0 - if not shell.colors_force and not shell.has_readline: - new_scheme = 'NoColor' - - # Set prompt colors - try: - shell.prompt_manager.color_scheme = new_scheme - except: - color_switch_err('prompt') - else: - shell.colors = \ - shell.prompt_manager.color_scheme_table.active_scheme_name - # Set exception colors - try: - shell.InteractiveTB.set_colors(scheme = new_scheme) - shell.SyntaxTB.set_colors(scheme = new_scheme) - except: - color_switch_err('exception') - - # Set info (for 'object?') colors - if shell.color_info: - try: - shell.inspector.set_active_scheme(new_scheme) - except: - color_switch_err('object inspector') - else: - shell.inspector.set_active_scheme('NoColor') - - @line_magic - def xmode(self, parameter_s=''): - """Switch modes for the exception handlers. - - Valid modes: Plain, Context and Verbose. - - If called without arguments, acts as a toggle.""" - - def xmode_switch_err(name): - warn('Error changing %s exception modes.\n%s' % - (name,sys.exc_info()[1])) - - shell = self.shell - new_mode = parameter_s.strip().capitalize() - try: - shell.InteractiveTB.set_mode(mode=new_mode) - print 'Exception reporting mode:',shell.InteractiveTB.mode - except: - xmode_switch_err('user') - - @line_magic - def quickref(self,arg): - """ Show a quick reference sheet """ - from IPython.core.usage import quick_reference - qr = quick_reference + self.magic('-brief') - page.page(qr) - - @line_magic - def doctest_mode(self, parameter_s=''): - """Toggle doctest mode on and off. - - This mode is intended to make IPython behave as much as possible like a - plain Python shell, from the perspective of how its prompts, exceptions - and output look. This makes it easy to copy and paste parts of a - session into doctests. It does so by: - - - Changing the prompts to the classic ``>>>`` ones. - - Changing the exception reporting mode to 'Plain'. - - Disabling pretty-printing of output. - - Note that IPython also supports the pasting of code snippets that have - leading '>>>' and '...' prompts in them. This means that you can paste - doctests from files or docstrings (even if they have leading - whitespace), and the code will execute correctly. You can then use - '%history -t' to see the translated history; this will give you the - input after removal of all the leading prompts and whitespace, which - can be pasted back into an editor. - - With these features, you can switch into this mode easily whenever you - need to do testing and changes to doctests, without having to leave - your existing IPython session. - """ - - # Shorthands - shell = self.shell - pm = shell.prompt_manager - meta = shell.meta - disp_formatter = self.shell.display_formatter - ptformatter = disp_formatter.formatters['text/plain'] - # dstore is a data store kept in the instance metadata bag to track any - # changes we make, so we can undo them later. - dstore = meta.setdefault('doctest_mode',Struct()) - save_dstore = dstore.setdefault - - # save a few values we'll need to recover later - mode = save_dstore('mode',False) - save_dstore('rc_pprint',ptformatter.pprint) - save_dstore('xmode',shell.InteractiveTB.mode) - save_dstore('rc_separate_out',shell.separate_out) - save_dstore('rc_separate_out2',shell.separate_out2) - save_dstore('rc_prompts_pad_left',pm.justify) - save_dstore('rc_separate_in',shell.separate_in) - save_dstore('rc_plain_text_only',disp_formatter.plain_text_only) - save_dstore('prompt_templates',(pm.in_template, pm.in2_template, pm.out_template)) - - if mode == False: - # turn on - pm.in_template = '>>> ' - pm.in2_template = '... ' - pm.out_template = '' - - # Prompt separators like plain python - shell.separate_in = '' - shell.separate_out = '' - shell.separate_out2 = '' - - pm.justify = False - - ptformatter.pprint = False - disp_formatter.plain_text_only = True - - shell.magic('xmode Plain') - else: - # turn off - pm.in_template, pm.in2_template, pm.out_template = dstore.prompt_templates - - shell.separate_in = dstore.rc_separate_in - - shell.separate_out = dstore.rc_separate_out - shell.separate_out2 = dstore.rc_separate_out2 - - pm.justify = dstore.rc_prompts_pad_left - - ptformatter.pprint = dstore.rc_pprint - disp_formatter.plain_text_only = dstore.rc_plain_text_only - - shell.magic('xmode ' + dstore.xmode) - - # Store new mode and inform - dstore.mode = bool(1-int(mode)) - mode_label = ['OFF','ON'][dstore.mode] - print 'Doctest mode is:', mode_label - - @line_magic - def gui(self, parameter_s=''): - """Enable or disable IPython GUI event loop integration. - - %gui [GUINAME] - - This magic replaces IPython's threaded shells that were activated - using the (pylab/wthread/etc.) command line flags. GUI toolkits - can now be enabled at runtime and keyboard - interrupts should work without any problems. The following toolkits - are supported: wxPython, PyQt4, PyGTK, Tk and Cocoa (OSX):: - - %gui wx # enable wxPython event loop integration - %gui qt4|qt # enable PyQt4 event loop integration - %gui gtk # enable PyGTK event loop integration - %gui gtk3 # enable Gtk3 event loop integration - %gui tk # enable Tk event loop integration - %gui OSX # enable Cocoa event loop integration - # (requires %matplotlib 1.1) - %gui # disable all event loop integration - - WARNING: after any of these has been called you can simply create - an application object, but DO NOT start the event loop yourself, as - we have already handled that. - """ - opts, arg = self.parse_options(parameter_s, '') - if arg=='': arg = None - try: - return self.enable_gui(arg) - except Exception as e: - # print simple error message, rather than traceback if we can't - # hook up the GUI - error(str(e)) - - @skip_doctest - @line_magic - def precision(self, s=''): - """Set floating point precision for pretty printing. - - Can set either integer precision or a format string. - - If numpy has been imported and precision is an int, - numpy display precision will also be set, via ``numpy.set_printoptions``. - - If no argument is given, defaults will be restored. - - Examples - -------- - :: - - In [1]: from math import pi - - In [2]: %precision 3 - Out[2]: u'%.3f' - - In [3]: pi - Out[3]: 3.142 - - In [4]: %precision %i - Out[4]: u'%i' - - In [5]: pi - Out[5]: 3 - - In [6]: %precision %e - Out[6]: u'%e' - - In [7]: pi**10 - Out[7]: 9.364805e+04 - - In [8]: %precision - Out[8]: u'%r' - - In [9]: pi**10 - Out[9]: 93648.047476082982 - """ - ptformatter = self.shell.display_formatter.formatters['text/plain'] - ptformatter.float_precision = s - return ptformatter.float_format - - @magic_arguments.magic_arguments() - @magic_arguments.argument( - '-e', '--export', action='store_true', default=False, - help='Export IPython history as a notebook. The filename argument ' - 'is used to specify the notebook name and format. For example ' - 'a filename of notebook.ipynb will result in a notebook name ' - 'of "notebook" and a format of "xml". Likewise using a ".json" ' - 'or ".py" file extension will write the notebook in the json ' - 'or py formats.' - ) - @magic_arguments.argument( - '-f', '--format', - help='Convert an existing IPython notebook to a new format. This option ' - 'specifies the new format and can have the values: xml, json, py. ' - 'The target filename is chosen automatically based on the new ' - 'format. The filename argument gives the name of the source file.' - ) - @magic_arguments.argument( - 'filename', type=unicode, - help='Notebook name or filename' - ) - @line_magic - def notebook(self, s): - """Export and convert IPython notebooks. - - This function can export the current IPython history to a notebook file - or can convert an existing notebook file into a different format. For - example, to export the history to "foo.ipynb" do "%notebook -e foo.ipynb". - To export the history to "foo.py" do "%notebook -e foo.py". To convert - "foo.ipynb" to "foo.json" do "%notebook -f json foo.ipynb". Possible - formats include (json/ipynb, py). - """ - args = magic_arguments.parse_argstring(self.notebook, s) - - from IPython.nbformat import current - args.filename = unquote_filename(args.filename) - if args.export: - fname, name, format = current.parse_filename(args.filename) - cells = [] - hist = list(self.shell.history_manager.get_range()) - for session, prompt_number, input in hist[:-1]: - cells.append(current.new_code_cell(prompt_number=prompt_number, - input=input)) - worksheet = current.new_worksheet(cells=cells) - nb = current.new_notebook(name=name,worksheets=[worksheet]) - with io.open(fname, 'w', encoding='utf-8') as f: - current.write(nb, f, format); - elif args.format is not None: - old_fname, old_name, old_format = current.parse_filename(args.filename) - new_format = args.format - if new_format == u'xml': - raise ValueError('Notebooks cannot be written as xml.') - elif new_format == u'ipynb' or new_format == u'json': - new_fname = old_name + u'.ipynb' - new_format = u'json' - elif new_format == u'py': - new_fname = old_name + u'.py' - else: - raise ValueError('Invalid notebook format: %s' % new_format) - with io.open(old_fname, 'r', encoding='utf-8') as f: - nb = current.read(f, old_format) - with io.open(new_fname, 'w', encoding='utf-8') as f: - current.write(nb, f, new_format) - # Used for exception handling in magic_edit class MacroToEdit(ValueError): pass diff --git a/IPython/core/magics/__init__.py b/IPython/core/magics/__init__.py index bf110ad26c1..d51657eca9f 100644 --- a/IPython/core/magics/__init__.py +++ b/IPython/core/magics/__init__.py @@ -11,9 +11,10 @@ #----------------------------------------------------------------------------- # Imports #----------------------------------------------------------------------------- -from IPython.core.magic import Magics, register_magics -from history import (HistoryMagics) +from IPython.core.magic import Magics, register_magics +from basic import BasicMagics +from history import HistoryMagics #----------------------------------------------------------------------------- # Magic implementation classes diff --git a/IPython/core/magics/basic.py b/IPython/core/magics/basic.py new file mode 100644 index 00000000000..57b5953d2e8 --- /dev/null +++ b/IPython/core/magics/basic.py @@ -0,0 +1,513 @@ +"""Implementation of basic magic functions. +""" +#----------------------------------------------------------------------------- +# Copyright (c) 2012 The IPython Development Team. +# +# Distributed under the terms of the Modified BSD License. +# +# The full license is in the file COPYING.txt, distributed with this software. +#----------------------------------------------------------------------------- + +#----------------------------------------------------------------------------- +# Imports +#----------------------------------------------------------------------------- +from __future__ import print_function + +# Stdlib +import io +import sys +from pprint import pformat + +# Our own packages +from IPython.core.error import UsageError +from IPython.core.magic import Magics, register_magics, line_magic +from IPython.core.prefilter import ESC_MAGIC +from IPython.utils.text import format_screen +from IPython.core import magic_arguments, page +from IPython.testing.skipdoctest import skip_doctest +from IPython.utils.ipstruct import Struct +from IPython.utils.path import unquote_filename +from IPython.utils.warn import warn, error + +#----------------------------------------------------------------------------- +# Magics class implementation +#----------------------------------------------------------------------------- + +@register_magics +class BasicMagics(Magics): + """Magics that provide central IPython functionality. + + These are various magics that don't fit into specific categories but that + are all part of the base 'IPython experience'.""" + + def _lsmagic(self): + mesc = ESC_MAGIC + cesc = mesc*2 + mman = self.shell.magics_manager + magics = mman.lsmagic() + out = ['Available line magics:', + mesc + (' '+mesc).join(magics['line']), + '', + 'Available cell magics:', + cesc + (' '+cesc).join(magics['cell']), + '', + mman.auto_status()] + return '\n'.join(out) + + @line_magic + def lsmagic(self, parameter_s=''): + """List currently available magic functions.""" + print(self._lsmagic()) + + @line_magic + def magic(self, parameter_s=''): + """Print information about the magic function system. + + Supported formats: -latex, -brief, -rest + """ + + mode = '' + try: + mode = parameter_s.split()[0][1:] + if mode == 'rest': + rest_docs = [] + except: + pass + + magic_docs = [] + escapes = dict(line=ESC_MAGIC, cell=ESC_MAGIC*2) + magics = self.shell.magics_manager.magics + + for mtype in ('line', 'cell'): + escape = escapes[mtype] + for fname, fn in magics[mtype].iteritems(): + + if mode == 'brief': + # only first line + if fn.__doc__: + fndoc = fn.__doc__.split('\n',1)[0] + else: + fndoc = 'No documentation' + else: + if fn.__doc__: + fndoc = fn.__doc__.rstrip() + else: + fndoc = 'No documentation' + + if mode == 'rest': + rest_docs.append('**%s%s**::\n\n\t%s\n\n' % + (escape, fname, fndoc)) + else: + magic_docs.append('%s%s:\n\t%s\n' % + (escape, fname, fndoc)) + + magic_docs = ''.join(magic_docs) + + if mode == 'rest': + return "".join(rest_docs) + + if mode == 'latex': + print(self.format_latex(magic_docs)) + return + else: + magic_docs = format_screen(magic_docs) + if mode == 'brief': + return magic_docs + + out = [""" +IPython's 'magic' functions +=========================== + +The magic function system provides a series of functions which allow you to +control the behavior of IPython itself, plus a lot of system-type +features. All these functions are prefixed with a % character, but parameters +are given without parentheses or quotes. + +NOTE: If you have 'automagic' enabled (via the command line option or with the +%automagic function), you don't need to type in the % explicitly. By default, +IPython ships with automagic on, so you should only rarely need the % escape. + +Example: typing '%cd mydir' (without the quotes) changes you working directory +to 'mydir', if it exists. + +For a list of the available magic functions, use %lsmagic. For a description +of any of them, type %magic_name?, e.g. '%cd?'. + +Currently the magic system has the following functions:""", + magic_docs, + "Summary of magic functions (from %slsmagic):", + self._lsmagic(), + ] + page.page('\n'.join(out)) + + + @line_magic + def page(self, parameter_s=''): + """Pretty print the object and display it through a pager. + + %page [options] OBJECT + + If no object is given, use _ (last output). + + Options: + + -r: page str(object), don't pretty-print it.""" + + # After a function contributed by Olivier Aubert, slightly modified. + + # Process options/args + opts, args = self.parse_options(parameter_s, 'r') + raw = 'r' in opts + + oname = args and args or '_' + info = self._ofind(oname) + if info['found']: + txt = (raw and str or pformat)( info['obj'] ) + page.page(txt) + else: + print('Object `%s` not found' % oname) + + @line_magic + def profile(self, parameter_s=''): + """Print your currently active IPython profile.""" + from IPython.core.application import BaseIPythonApplication + if BaseIPythonApplication.initialized(): + print(BaseIPythonApplication.instance().profile) + else: + error("profile is an application-level value, but you don't appear to be in an IPython application") + + @line_magic + def pprint(self, parameter_s=''): + """Toggle pretty printing on/off.""" + ptformatter = self.shell.display_formatter.formatters['text/plain'] + ptformatter.pprint = bool(1 - ptformatter.pprint) + print('Pretty printing has been turned', + ['OFF','ON'][ptformatter.pprint]) + + @line_magic + def colors(self, parameter_s=''): + """Switch color scheme for prompts, info system and exception handlers. + + Currently implemented schemes: NoColor, Linux, LightBG. + + Color scheme names are not case-sensitive. + + Examples + -------- + To get a plain black and white terminal:: + + %colors nocolor + """ + def color_switch_err(name): + warn('Error changing %s color schemes.\n%s' % + (name, sys.exc_info()[1])) + + + new_scheme = parameter_s.strip() + if not new_scheme: + raise UsageError( + "%colors: you must specify a color scheme. See '%colors?'") + return + # local shortcut + shell = self.shell + + import IPython.utils.rlineimpl as readline + + if not shell.colors_force and \ + not readline.have_readline and sys.platform == "win32": + msg = """\ +Proper color support under MS Windows requires the pyreadline library. +You can find it at: +http://ipython.org/pyreadline.html +Gary's readline needs the ctypes module, from: +http://starship.python.net/crew/theller/ctypes +(Note that ctypes is already part of Python versions 2.5 and newer). + +Defaulting color scheme to 'NoColor'""" + new_scheme = 'NoColor' + warn(msg) + + # readline option is 0 + if not shell.colors_force and not shell.has_readline: + new_scheme = 'NoColor' + + # Set prompt colors + try: + shell.prompt_manager.color_scheme = new_scheme + except: + color_switch_err('prompt') + else: + shell.colors = \ + shell.prompt_manager.color_scheme_table.active_scheme_name + # Set exception colors + try: + shell.InteractiveTB.set_colors(scheme = new_scheme) + shell.SyntaxTB.set_colors(scheme = new_scheme) + except: + color_switch_err('exception') + + # Set info (for 'object?') colors + if shell.color_info: + try: + shell.inspector.set_active_scheme(new_scheme) + except: + color_switch_err('object inspector') + else: + shell.inspector.set_active_scheme('NoColor') + + @line_magic + def xmode(self, parameter_s=''): + """Switch modes for the exception handlers. + + Valid modes: Plain, Context and Verbose. + + If called without arguments, acts as a toggle.""" + + def xmode_switch_err(name): + warn('Error changing %s exception modes.\n%s' % + (name,sys.exc_info()[1])) + + shell = self.shell + new_mode = parameter_s.strip().capitalize() + try: + shell.InteractiveTB.set_mode(mode=new_mode) + print('Exception reporting mode:',shell.InteractiveTB.mode) + except: + xmode_switch_err('user') + + @line_magic + def quickref(self,arg): + """ Show a quick reference sheet """ + from IPython.core.usage import quick_reference + qr = quick_reference + self.magic('-brief') + page.page(qr) + + @line_magic + def doctest_mode(self, parameter_s=''): + """Toggle doctest mode on and off. + + This mode is intended to make IPython behave as much as possible like a + plain Python shell, from the perspective of how its prompts, exceptions + and output look. This makes it easy to copy and paste parts of a + session into doctests. It does so by: + + - Changing the prompts to the classic ``>>>`` ones. + - Changing the exception reporting mode to 'Plain'. + - Disabling pretty-printing of output. + + Note that IPython also supports the pasting of code snippets that have + leading '>>>' and '...' prompts in them. This means that you can paste + doctests from files or docstrings (even if they have leading + whitespace), and the code will execute correctly. You can then use + '%history -t' to see the translated history; this will give you the + input after removal of all the leading prompts and whitespace, which + can be pasted back into an editor. + + With these features, you can switch into this mode easily whenever you + need to do testing and changes to doctests, without having to leave + your existing IPython session. + """ + + # Shorthands + shell = self.shell + pm = shell.prompt_manager + meta = shell.meta + disp_formatter = self.shell.display_formatter + ptformatter = disp_formatter.formatters['text/plain'] + # dstore is a data store kept in the instance metadata bag to track any + # changes we make, so we can undo them later. + dstore = meta.setdefault('doctest_mode',Struct()) + save_dstore = dstore.setdefault + + # save a few values we'll need to recover later + mode = save_dstore('mode',False) + save_dstore('rc_pprint',ptformatter.pprint) + save_dstore('xmode',shell.InteractiveTB.mode) + save_dstore('rc_separate_out',shell.separate_out) + save_dstore('rc_separate_out2',shell.separate_out2) + save_dstore('rc_prompts_pad_left',pm.justify) + save_dstore('rc_separate_in',shell.separate_in) + save_dstore('rc_plain_text_only',disp_formatter.plain_text_only) + save_dstore('prompt_templates',(pm.in_template, pm.in2_template, pm.out_template)) + + if mode == False: + # turn on + pm.in_template = '>>> ' + pm.in2_template = '... ' + pm.out_template = '' + + # Prompt separators like plain python + shell.separate_in = '' + shell.separate_out = '' + shell.separate_out2 = '' + + pm.justify = False + + ptformatter.pprint = False + disp_formatter.plain_text_only = True + + shell.magic('xmode Plain') + else: + # turn off + pm.in_template, pm.in2_template, pm.out_template = dstore.prompt_templates + + shell.separate_in = dstore.rc_separate_in + + shell.separate_out = dstore.rc_separate_out + shell.separate_out2 = dstore.rc_separate_out2 + + pm.justify = dstore.rc_prompts_pad_left + + ptformatter.pprint = dstore.rc_pprint + disp_formatter.plain_text_only = dstore.rc_plain_text_only + + shell.magic('xmode ' + dstore.xmode) + + # Store new mode and inform + dstore.mode = bool(1-int(mode)) + mode_label = ['OFF','ON'][dstore.mode] + print('Doctest mode is:', mode_label) + + @line_magic + def gui(self, parameter_s=''): + """Enable or disable IPython GUI event loop integration. + + %gui [GUINAME] + + This magic replaces IPython's threaded shells that were activated + using the (pylab/wthread/etc.) command line flags. GUI toolkits + can now be enabled at runtime and keyboard + interrupts should work without any problems. The following toolkits + are supported: wxPython, PyQt4, PyGTK, Tk and Cocoa (OSX):: + + %gui wx # enable wxPython event loop integration + %gui qt4|qt # enable PyQt4 event loop integration + %gui gtk # enable PyGTK event loop integration + %gui gtk3 # enable Gtk3 event loop integration + %gui tk # enable Tk event loop integration + %gui OSX # enable Cocoa event loop integration + # (requires %matplotlib 1.1) + %gui # disable all event loop integration + + WARNING: after any of these has been called you can simply create + an application object, but DO NOT start the event loop yourself, as + we have already handled that. + """ + opts, arg = self.parse_options(parameter_s, '') + if arg=='': arg = None + try: + return self.enable_gui(arg) + except Exception as e: + # print simple error message, rather than traceback if we can't + # hook up the GUI + error(str(e)) + + @skip_doctest + @line_magic + def precision(self, s=''): + """Set floating point precision for pretty printing. + + Can set either integer precision or a format string. + + If numpy has been imported and precision is an int, + numpy display precision will also be set, via ``numpy.set_printoptions``. + + If no argument is given, defaults will be restored. + + Examples + -------- + :: + + In [1]: from math import pi + + In [2]: %precision 3 + Out[2]: u'%.3f' + + In [3]: pi + Out[3]: 3.142 + + In [4]: %precision %i + Out[4]: u'%i' + + In [5]: pi + Out[5]: 3 + + In [6]: %precision %e + Out[6]: u'%e' + + In [7]: pi**10 + Out[7]: 9.364805e+04 + + In [8]: %precision + Out[8]: u'%r' + + In [9]: pi**10 + Out[9]: 93648.047476082982 + """ + ptformatter = self.shell.display_formatter.formatters['text/plain'] + ptformatter.float_precision = s + return ptformatter.float_format + + @magic_arguments.magic_arguments() + @magic_arguments.argument( + '-e', '--export', action='store_true', default=False, + help='Export IPython history as a notebook. The filename argument ' + 'is used to specify the notebook name and format. For example ' + 'a filename of notebook.ipynb will result in a notebook name ' + 'of "notebook" and a format of "xml". Likewise using a ".json" ' + 'or ".py" file extension will write the notebook in the json ' + 'or py formats.' + ) + @magic_arguments.argument( + '-f', '--format', + help='Convert an existing IPython notebook to a new format. This option ' + 'specifies the new format and can have the values: xml, json, py. ' + 'The target filename is chosen automatically based on the new ' + 'format. The filename argument gives the name of the source file.' + ) + @magic_arguments.argument( + 'filename', type=unicode, + help='Notebook name or filename' + ) + @line_magic + def notebook(self, s): + """Export and convert IPython notebooks. + + This function can export the current IPython history to a notebook file + or can convert an existing notebook file into a different format. For + example, to export the history to "foo.ipynb" do "%notebook -e foo.ipynb". + To export the history to "foo.py" do "%notebook -e foo.py". To convert + "foo.ipynb" to "foo.json" do "%notebook -f json foo.ipynb". Possible + formats include (json/ipynb, py). + """ + args = magic_arguments.parse_argstring(self.notebook, s) + + from IPython.nbformat import current + args.filename = unquote_filename(args.filename) + if args.export: + fname, name, format = current.parse_filename(args.filename) + cells = [] + hist = list(self.shell.history_manager.get_range()) + for session, prompt_number, input in hist[:-1]: + cells.append(current.new_code_cell(prompt_number=prompt_number, + input=input)) + worksheet = current.new_worksheet(cells=cells) + nb = current.new_notebook(name=name,worksheets=[worksheet]) + with io.open(fname, 'w', encoding='utf-8') as f: + current.write(nb, f, format); + elif args.format is not None: + old_fname, old_name, old_format = current.parse_filename(args.filename) + new_format = args.format + if new_format == u'xml': + raise ValueError('Notebooks cannot be written as xml.') + elif new_format == u'ipynb' or new_format == u'json': + new_fname = old_name + u'.ipynb' + new_format = u'json' + elif new_format == u'py': + new_fname = old_name + u'.py' + else: + raise ValueError('Invalid notebook format: %s' % new_format) + with io.open(old_fname, 'r', encoding='utf-8') as f: + nb = current.read(f, old_format) + with io.open(new_fname, 'w', encoding='utf-8') as f: + current.write(nb, f, new_format) From 21bd60192fb1e2d5100cbc12282e186433c6209b Mon Sep 17 00:00:00 2001 From: Fernando Perez Date: Wed, 23 May 2012 13:47:32 -0700 Subject: [PATCH 055/103] Create core.magics.code according to new API. --- IPython/core/interactiveshell.py | 2 +- IPython/core/magic_functions.py | 449 +---------------------------- IPython/core/magics/__init__.py | 5 +- IPython/core/magics/code.py | 478 +++++++++++++++++++++++++++++++ 4 files changed, 487 insertions(+), 447 deletions(-) create mode 100644 IPython/core/magics/code.py diff --git a/IPython/core/interactiveshell.py b/IPython/core/interactiveshell.py index aabf0352d45..41a6e4518fc 100644 --- a/IPython/core/interactiveshell.py +++ b/IPython/core/interactiveshell.py @@ -2005,7 +2005,7 @@ def init_magics(self): self.register_magic_function = self.magics_manager.register_function self.define_magic = self.magics_manager.define_magic - self.register_magics(m.BasicMagics, mf.CodeMagics, mf.ConfigMagics, + self.register_magics(m.BasicMagics, m.CodeMagics, mf.ConfigMagics, mf.ExecutionMagics, mf.NamespaceMagics, mf.AutoMagics, mf.OSMagics, mf.LoggingMagics, mf.ExtensionsMagics, mf.PylabMagics, m.HistoryMagics, mf.DeprecatedMagics) diff --git a/IPython/core/magic_functions.py b/IPython/core/magic_functions.py index f1f2c5e76c3..4c85f657095 100644 --- a/IPython/core/magic_functions.py +++ b/IPython/core/magic_functions.py @@ -12,7 +12,7 @@ #----------------------------------------------------------------------------- # Imports #----------------------------------------------------------------------------- - +# Stdlib import __builtin__ as builtin_mod import bdb import gc @@ -38,6 +38,7 @@ except ImportError: profile = pstats = None +# Our own packages from IPython.config.application import Application from IPython.core import debugger, oinspect from IPython.core import page @@ -59,449 +60,9 @@ from IPython.utils.timing import clock, clock2 from IPython.utils.warn import warn, error - -# Used for exception handling in magic_edit -class MacroToEdit(ValueError): pass - - -@register_magics -class CodeMagics(Magics): - """Magics related to code management (loading, saving, editing, ...).""" - - @line_magic - def save(self, parameter_s=''): - """Save a set of lines or a macro to a given filename. - - Usage:\\ - %save [options] filename n1-n2 n3-n4 ... n5 .. n6 ... - - Options: - - -r: use 'raw' input. By default, the 'processed' history is used, - so that magics are loaded in their transformed version to valid - Python. If this option is given, the raw input as typed as the - command line is used instead. - - This function uses the same syntax as %history for input ranges, - then saves the lines to the filename you specify. - - It adds a '.py' extension to the file if you don't do so yourself, and - it asks for confirmation before overwriting existing files.""" - - opts,args = self.parse_options(parameter_s,'r',mode='list') - fname, codefrom = unquote_filename(args[0]), " ".join(args[1:]) - if not fname.endswith('.py'): - fname += '.py' - if os.path.isfile(fname): - ans = raw_input('File `%s` exists. Overwrite (y/[N])? ' % fname) - if ans.lower() not in ['y','yes']: - print 'Operation cancelled.' - return - try: - cmds = self.shell.find_user_code(codefrom, 'r' in opts) - except (TypeError, ValueError) as e: - print e.args[0] - return - with io.open(fname,'w', encoding="utf-8") as f: - f.write(u"# coding: utf-8\n") - f.write(py3compat.cast_unicode(cmds)) - print 'The following commands were written to file `%s`:' % fname - print cmds - - @line_magic - def pastebin(self, parameter_s=''): - """Upload code to Github's Gist paste bin, returning the URL. - - Usage:\\ - %pastebin [-d "Custom description"] 1-7 - - The argument can be an input history range, a filename, or the name of a - string or macro. - - Options: - - -d: Pass a custom description for the gist. The default will say - "Pasted from IPython". - """ - opts, args = self.parse_options(parameter_s, 'd:') - - try: - code = self.shell.find_user_code(args) - except (ValueError, TypeError) as e: - print e.args[0] - return - - post_data = json.dumps({ - "description": opts.get('d', "Pasted from IPython"), - "public": True, - "files": { - "file1.py": { - "content": code - } - } - }).encode('utf-8') - - response = urlopen("https://api.github.com/gists", post_data) - response_data = json.loads(response.read().decode('utf-8')) - return response_data['html_url'] - - @line_magic - def loadpy(self, arg_s): - """Load a .py python script into the GUI console. - - This magic command can either take a local filename or a url:: - - %loadpy myscript.py - %loadpy http://www.example.com/myscript.py - """ - arg_s = unquote_filename(arg_s) - remote_url = arg_s.startswith(('http://', 'https://')) - local_url = not remote_url - if local_url and not arg_s.endswith('.py'): - # Local files must be .py; for remote URLs it's possible that the - # fetch URL doesn't have a .py in it (many servers have an opaque - # URL, such as scipy-central.org). - raise ValueError('%%loadpy only works with .py files: %s' % arg_s) - - # openpy takes care of finding the source encoding (per PEP 263) - if remote_url: - contents = openpy.read_py_url(arg_s, skip_encoding_cookie=True) - else: - contents = openpy.read_py_file(arg_s, skip_encoding_cookie=True) - - self.shell.set_next_input(contents) - - def _find_edit_target(self, args, opts, last_call): - """Utility method used by magic_edit to find what to edit.""" - - def make_filename(arg): - "Make a filename from the given args" - arg = unquote_filename(arg) - try: - filename = get_py_filename(arg) - except IOError: - # If it ends with .py but doesn't already exist, assume we want - # a new file. - if arg.endswith('.py'): - filename = arg - else: - filename = None - return filename - - # Set a few locals from the options for convenience: - opts_prev = 'p' in opts - opts_raw = 'r' in opts - - # custom exceptions - class DataIsObject(Exception): pass - - # Default line number value - lineno = opts.get('n',None) - - if opts_prev: - args = '_%s' % last_call[0] - if not self.shell.user_ns.has_key(args): - args = last_call[1] - - # use last_call to remember the state of the previous call, but don't - # let it be clobbered by successive '-p' calls. - try: - last_call[0] = self.shell.displayhook.prompt_count - if not opts_prev: - last_call[1] = args - except: - pass - - # by default this is done with temp files, except when the given - # arg is a filename - use_temp = True - - data = '' - - # First, see if the arguments should be a filename. - filename = make_filename(args) - if filename: - use_temp = False - elif args: - # Mode where user specifies ranges of lines, like in %macro. - data = self.shell.extract_input_lines(args, opts_raw) - if not data: - try: - # Load the parameter given as a variable. If not a string, - # process it as an object instead (below) - - #print '*** args',args,'type',type(args) # dbg - data = eval(args, self.shell.user_ns) - if not isinstance(data, basestring): - raise DataIsObject - - except (NameError,SyntaxError): - # given argument is not a variable, try as a filename - filename = make_filename(args) - if filename is None: - warn("Argument given (%s) can't be found as a variable " - "or as a filename." % args) - return - use_temp = False - - except DataIsObject: - # macros have a special edit function - if isinstance(data, Macro): - raise MacroToEdit(data) - - # For objects, try to edit the file where they are defined - try: - filename = inspect.getabsfile(data) - if 'fakemodule' in filename.lower() and \ - inspect.isclass(data): - # class created by %edit? Try to find source - # by looking for method definitions instead, the - # __module__ in those classes is FakeModule. - attrs = [getattr(data, aname) for aname in dir(data)] - for attr in attrs: - if not inspect.ismethod(attr): - continue - filename = inspect.getabsfile(attr) - if filename and \ - 'fakemodule' not in filename.lower(): - # change the attribute to be the edit - # target instead - data = attr - break - - datafile = 1 - except TypeError: - filename = make_filename(args) - datafile = 1 - warn('Could not find file where `%s` is defined.\n' - 'Opening a file named `%s`' % (args,filename)) - # Now, make sure we can actually read the source (if it was - # in a temp file it's gone by now). - if datafile: - try: - if lineno is None: - lineno = inspect.getsourcelines(data)[1] - except IOError: - filename = make_filename(args) - if filename is None: - warn('The file `%s` where `%s` was defined cannot ' - 'be read.' % (filename,data)) - return - use_temp = False - - if use_temp: - filename = self.shell.mktempfile(data) - print 'IPython will make a temporary file named:',filename - - return filename, lineno, use_temp - - def _edit_macro(self,mname,macro): - """open an editor with the macro data in a file""" - filename = self.shell.mktempfile(macro.value) - self.shell.hooks.editor(filename) - - # and make a new macro object, to replace the old one - mfile = open(filename) - mvalue = mfile.read() - mfile.close() - self.shell.user_ns[mname] = Macro(mvalue) - - @line_magic - def ed(self, parameter_s=''): - """Alias to %edit.""" - return self.edit(parameter_s) - - @skip_doctest - @line_magic - def edit(self, parameter_s='',last_call=['','']): - """Bring up an editor and execute the resulting code. - - Usage: - %edit [options] [args] - - %edit runs IPython's editor hook. The default version of this hook is - set to call the editor specified by your $EDITOR environment variable. - If this isn't found, it will default to vi under Linux/Unix and to - notepad under Windows. See the end of this docstring for how to change - the editor hook. - - You can also set the value of this editor via the - ``TerminalInteractiveShell.editor`` option in your configuration file. - This is useful if you wish to use a different editor from your typical - default with IPython (and for Windows users who typically don't set - environment variables). - - This command allows you to conveniently edit multi-line code right in - your IPython session. - - If called without arguments, %edit opens up an empty editor with a - temporary file and will execute the contents of this file when you - close it (don't forget to save it!). - - - Options: - - -n : open the editor at a specified line number. By default, - the IPython editor hook uses the unix syntax 'editor +N filename', but - you can configure this by providing your own modified hook if your - favorite editor supports line-number specifications with a different - syntax. - - -p: this will call the editor with the same data as the previous time - it was used, regardless of how long ago (in your current session) it - was. - - -r: use 'raw' input. This option only applies to input taken from the - user's history. By default, the 'processed' history is used, so that - magics are loaded in their transformed version to valid Python. If - this option is given, the raw input as typed as the command line is - used instead. When you exit the editor, it will be executed by - IPython's own processor. - - -x: do not execute the edited code immediately upon exit. This is - mainly useful if you are editing programs which need to be called with - command line arguments, which you can then do using %run. - - - Arguments: - - If arguments are given, the following possibilities exist: - - - If the argument is a filename, IPython will load that into the - editor. It will execute its contents with execfile() when you exit, - loading any code in the file into your interactive namespace. - - - The arguments are ranges of input history, e.g. "7 ~1/4-6". - The syntax is the same as in the %history magic. - - - If the argument is a string variable, its contents are loaded - into the editor. You can thus edit any string which contains - python code (including the result of previous edits). - - - If the argument is the name of an object (other than a string), - IPython will try to locate the file where it was defined and open the - editor at the point where it is defined. You can use `%edit function` - to load an editor exactly at the point where 'function' is defined, - edit it and have the file be executed automatically. - - - If the object is a macro (see %macro for details), this opens up your - specified editor with a temporary file containing the macro's data. - Upon exit, the macro is reloaded with the contents of the file. - - Note: opening at an exact line is only supported under Unix, and some - editors (like kedit and gedit up to Gnome 2.8) do not understand the - '+NUMBER' parameter necessary for this feature. Good editors like - (X)Emacs, vi, jed, pico and joe all do. - - After executing your code, %edit will return as output the code you - typed in the editor (except when it was an existing file). This way - you can reload the code in further invocations of %edit as a variable, - via _ or Out[], where is the prompt number of - the output. - - Note that %edit is also available through the alias %ed. - - This is an example of creating a simple function inside the editor and - then modifying it. First, start up the editor:: - - In [1]: ed - Editing... done. Executing edited code... - Out[1]: 'def foo():\\n print "foo() was defined in an editing - session"\\n' - - We can then call the function foo():: - - In [2]: foo() - foo() was defined in an editing session - - Now we edit foo. IPython automatically loads the editor with the - (temporary) file where foo() was previously defined:: - - In [3]: ed foo - Editing... done. Executing edited code... - - And if we call foo() again we get the modified version:: - - In [4]: foo() - foo() has now been changed! - - Here is an example of how to edit a code snippet successive - times. First we call the editor:: - - In [5]: ed - Editing... done. Executing edited code... - hello - Out[5]: "print 'hello'\\n" - - Now we call it again with the previous output (stored in _):: - - In [6]: ed _ - Editing... done. Executing edited code... - hello world - Out[6]: "print 'hello world'\\n" - - Now we call it with the output #8 (stored in _8, also as Out[8]):: - - In [7]: ed _8 - Editing... done. Executing edited code... - hello again - Out[7]: "print 'hello again'\\n" - - - Changing the default editor hook: - - If you wish to write your own editor hook, you can put it in a - configuration file which you load at startup time. The default hook - is defined in the IPython.core.hooks module, and you can use that as a - starting example for further modifications. That file also has - general instructions on how to set a new hook for use once you've - defined it.""" - opts,args = self.parse_options(parameter_s,'prxn:') - - try: - filename, lineno, is_temp = self._find_edit_target(args, opts, last_call) - except MacroToEdit as e: - self._edit_macro(args, e.args[0]) - return - - # do actual editing here - print 'Editing...', - sys.stdout.flush() - try: - # Quote filenames that may have spaces in them - if ' ' in filename: - filename = "'%s'" % filename - self.shell.hooks.editor(filename,lineno) - except TryNext: - warn('Could not open editor') - return - - # XXX TODO: should this be generalized for all string vars? - # For now, this is special-cased to blocks created by cpaste - if args.strip() == 'pasted_block': - self.shell.user_ns['pasted_block'] = file_read(filename) - - if 'x' in opts: # -x prevents actual execution - print - else: - print 'done. Executing edited code...' - if 'r' in opts: # Untranslated IPython code - self.shell.run_cell(file_read(filename), - store_history=False) - else: - self.shell.safe_execfile(filename, self.shell.user_ns, - self.shell.user_ns) - - if is_temp: - try: - return open(filename).read() - except IOError,msg: - if msg.filename == filename: - warn('File not found. Did you forget to save?') - return - else: - self.shell.showtraceback() - +#----------------------------------------------------------------------------- +# Magic implementation classes +#----------------------------------------------------------------------------- @register_magics class ConfigMagics(Magics): diff --git a/IPython/core/magics/__init__.py b/IPython/core/magics/__init__.py index d51657eca9f..528a1e9e0e2 100644 --- a/IPython/core/magics/__init__.py +++ b/IPython/core/magics/__init__.py @@ -13,8 +13,9 @@ #----------------------------------------------------------------------------- from IPython.core.magic import Magics, register_magics -from basic import BasicMagics -from history import HistoryMagics +from .basic import BasicMagics +from .code import CodeMagics, MacroToEdit +from .history import HistoryMagics #----------------------------------------------------------------------------- # Magic implementation classes diff --git a/IPython/core/magics/code.py b/IPython/core/magics/code.py new file mode 100644 index 00000000000..767211c8162 --- /dev/null +++ b/IPython/core/magics/code.py @@ -0,0 +1,478 @@ +"""Implementation of basic magic functions. +""" +#----------------------------------------------------------------------------- +# Copyright (c) 2012 The IPython Development Team. +# +# Distributed under the terms of the Modified BSD License. +# +# The full license is in the file COPYING.txt, distributed with this software. +#----------------------------------------------------------------------------- + +#----------------------------------------------------------------------------- +# Imports +#----------------------------------------------------------------------------- + +# Stdlib +import inspect +import io +import json +import os +import sys +from urllib2 import urlopen + +# Our own packages +from IPython.core.error import TryNext +from IPython.core.macro import Macro +from IPython.core.magic import Magics, register_magics, line_magic +from IPython.testing.skipdoctest import skip_doctest +from IPython.utils import openpy +from IPython.utils import py3compat +from IPython.utils.io import file_read +from IPython.utils.path import get_py_filename, unquote_filename +from IPython.utils.warn import warn + +#----------------------------------------------------------------------------- +# Magic implementation classes +#----------------------------------------------------------------------------- + +# Used for exception handling in magic_edit +class MacroToEdit(ValueError): pass + + +@register_magics +class CodeMagics(Magics): + """Magics related to code management (loading, saving, editing, ...).""" + + @line_magic + def save(self, parameter_s=''): + """Save a set of lines or a macro to a given filename. + + Usage:\\ + %save [options] filename n1-n2 n3-n4 ... n5 .. n6 ... + + Options: + + -r: use 'raw' input. By default, the 'processed' history is used, + so that magics are loaded in their transformed version to valid + Python. If this option is given, the raw input as typed as the + command line is used instead. + + This function uses the same syntax as %history for input ranges, + then saves the lines to the filename you specify. + + It adds a '.py' extension to the file if you don't do so yourself, and + it asks for confirmation before overwriting existing files.""" + + opts,args = self.parse_options(parameter_s,'r',mode='list') + fname, codefrom = unquote_filename(args[0]), " ".join(args[1:]) + if not fname.endswith('.py'): + fname += '.py' + if os.path.isfile(fname): + ans = raw_input('File `%s` exists. Overwrite (y/[N])? ' % fname) + if ans.lower() not in ['y','yes']: + print 'Operation cancelled.' + return + try: + cmds = self.shell.find_user_code(codefrom, 'r' in opts) + except (TypeError, ValueError) as e: + print e.args[0] + return + with io.open(fname,'w', encoding="utf-8") as f: + f.write(u"# coding: utf-8\n") + f.write(py3compat.cast_unicode(cmds)) + print 'The following commands were written to file `%s`:' % fname + print cmds + + @line_magic + def pastebin(self, parameter_s=''): + """Upload code to Github's Gist paste bin, returning the URL. + + Usage:\\ + %pastebin [-d "Custom description"] 1-7 + + The argument can be an input history range, a filename, or the name of a + string or macro. + + Options: + + -d: Pass a custom description for the gist. The default will say + "Pasted from IPython". + """ + opts, args = self.parse_options(parameter_s, 'd:') + + try: + code = self.shell.find_user_code(args) + except (ValueError, TypeError) as e: + print e.args[0] + return + + post_data = json.dumps({ + "description": opts.get('d', "Pasted from IPython"), + "public": True, + "files": { + "file1.py": { + "content": code + } + } + }).encode('utf-8') + + response = urlopen("https://api.github.com/gists", post_data) + response_data = json.loads(response.read().decode('utf-8')) + return response_data['html_url'] + + @line_magic + def loadpy(self, arg_s): + """Load a .py python script into the GUI console. + + This magic command can either take a local filename or a url:: + + %loadpy myscript.py + %loadpy http://www.example.com/myscript.py + """ + arg_s = unquote_filename(arg_s) + remote_url = arg_s.startswith(('http://', 'https://')) + local_url = not remote_url + if local_url and not arg_s.endswith('.py'): + # Local files must be .py; for remote URLs it's possible that the + # fetch URL doesn't have a .py in it (many servers have an opaque + # URL, such as scipy-central.org). + raise ValueError('%%loadpy only works with .py files: %s' % arg_s) + + # openpy takes care of finding the source encoding (per PEP 263) + if remote_url: + contents = openpy.read_py_url(arg_s, skip_encoding_cookie=True) + else: + contents = openpy.read_py_file(arg_s, skip_encoding_cookie=True) + + self.shell.set_next_input(contents) + + def _find_edit_target(self, args, opts, last_call): + """Utility method used by magic_edit to find what to edit.""" + + def make_filename(arg): + "Make a filename from the given args" + arg = unquote_filename(arg) + try: + filename = get_py_filename(arg) + except IOError: + # If it ends with .py but doesn't already exist, assume we want + # a new file. + if arg.endswith('.py'): + filename = arg + else: + filename = None + return filename + + # Set a few locals from the options for convenience: + opts_prev = 'p' in opts + opts_raw = 'r' in opts + + # custom exceptions + class DataIsObject(Exception): pass + + # Default line number value + lineno = opts.get('n',None) + + if opts_prev: + args = '_%s' % last_call[0] + if not self.shell.user_ns.has_key(args): + args = last_call[1] + + # use last_call to remember the state of the previous call, but don't + # let it be clobbered by successive '-p' calls. + try: + last_call[0] = self.shell.displayhook.prompt_count + if not opts_prev: + last_call[1] = args + except: + pass + + # by default this is done with temp files, except when the given + # arg is a filename + use_temp = True + + data = '' + + # First, see if the arguments should be a filename. + filename = make_filename(args) + if filename: + use_temp = False + elif args: + # Mode where user specifies ranges of lines, like in %macro. + data = self.shell.extract_input_lines(args, opts_raw) + if not data: + try: + # Load the parameter given as a variable. If not a string, + # process it as an object instead (below) + + #print '*** args',args,'type',type(args) # dbg + data = eval(args, self.shell.user_ns) + if not isinstance(data, basestring): + raise DataIsObject + + except (NameError,SyntaxError): + # given argument is not a variable, try as a filename + filename = make_filename(args) + if filename is None: + warn("Argument given (%s) can't be found as a variable " + "or as a filename." % args) + return + use_temp = False + + except DataIsObject: + # macros have a special edit function + if isinstance(data, Macro): + raise MacroToEdit(data) + + # For objects, try to edit the file where they are defined + try: + filename = inspect.getabsfile(data) + if 'fakemodule' in filename.lower() and \ + inspect.isclass(data): + # class created by %edit? Try to find source + # by looking for method definitions instead, the + # __module__ in those classes is FakeModule. + attrs = [getattr(data, aname) for aname in dir(data)] + for attr in attrs: + if not inspect.ismethod(attr): + continue + filename = inspect.getabsfile(attr) + if filename and \ + 'fakemodule' not in filename.lower(): + # change the attribute to be the edit + # target instead + data = attr + break + + datafile = 1 + except TypeError: + filename = make_filename(args) + datafile = 1 + warn('Could not find file where `%s` is defined.\n' + 'Opening a file named `%s`' % (args,filename)) + # Now, make sure we can actually read the source (if it was + # in a temp file it's gone by now). + if datafile: + try: + if lineno is None: + lineno = inspect.getsourcelines(data)[1] + except IOError: + filename = make_filename(args) + if filename is None: + warn('The file `%s` where `%s` was defined cannot ' + 'be read.' % (filename,data)) + return + use_temp = False + + if use_temp: + filename = self.shell.mktempfile(data) + print 'IPython will make a temporary file named:',filename + + return filename, lineno, use_temp + + def _edit_macro(self,mname,macro): + """open an editor with the macro data in a file""" + filename = self.shell.mktempfile(macro.value) + self.shell.hooks.editor(filename) + + # and make a new macro object, to replace the old one + mfile = open(filename) + mvalue = mfile.read() + mfile.close() + self.shell.user_ns[mname] = Macro(mvalue) + + @line_magic + def ed(self, parameter_s=''): + """Alias to %edit.""" + return self.edit(parameter_s) + + @skip_doctest + @line_magic + def edit(self, parameter_s='',last_call=['','']): + """Bring up an editor and execute the resulting code. + + Usage: + %edit [options] [args] + + %edit runs IPython's editor hook. The default version of this hook is + set to call the editor specified by your $EDITOR environment variable. + If this isn't found, it will default to vi under Linux/Unix and to + notepad under Windows. See the end of this docstring for how to change + the editor hook. + + You can also set the value of this editor via the + ``TerminalInteractiveShell.editor`` option in your configuration file. + This is useful if you wish to use a different editor from your typical + default with IPython (and for Windows users who typically don't set + environment variables). + + This command allows you to conveniently edit multi-line code right in + your IPython session. + + If called without arguments, %edit opens up an empty editor with a + temporary file and will execute the contents of this file when you + close it (don't forget to save it!). + + + Options: + + -n : open the editor at a specified line number. By default, + the IPython editor hook uses the unix syntax 'editor +N filename', but + you can configure this by providing your own modified hook if your + favorite editor supports line-number specifications with a different + syntax. + + -p: this will call the editor with the same data as the previous time + it was used, regardless of how long ago (in your current session) it + was. + + -r: use 'raw' input. This option only applies to input taken from the + user's history. By default, the 'processed' history is used, so that + magics are loaded in their transformed version to valid Python. If + this option is given, the raw input as typed as the command line is + used instead. When you exit the editor, it will be executed by + IPython's own processor. + + -x: do not execute the edited code immediately upon exit. This is + mainly useful if you are editing programs which need to be called with + command line arguments, which you can then do using %run. + + + Arguments: + + If arguments are given, the following possibilities exist: + + - If the argument is a filename, IPython will load that into the + editor. It will execute its contents with execfile() when you exit, + loading any code in the file into your interactive namespace. + + - The arguments are ranges of input history, e.g. "7 ~1/4-6". + The syntax is the same as in the %history magic. + + - If the argument is a string variable, its contents are loaded + into the editor. You can thus edit any string which contains + python code (including the result of previous edits). + + - If the argument is the name of an object (other than a string), + IPython will try to locate the file where it was defined and open the + editor at the point where it is defined. You can use `%edit function` + to load an editor exactly at the point where 'function' is defined, + edit it and have the file be executed automatically. + + - If the object is a macro (see %macro for details), this opens up your + specified editor with a temporary file containing the macro's data. + Upon exit, the macro is reloaded with the contents of the file. + + Note: opening at an exact line is only supported under Unix, and some + editors (like kedit and gedit up to Gnome 2.8) do not understand the + '+NUMBER' parameter necessary for this feature. Good editors like + (X)Emacs, vi, jed, pico and joe all do. + + After executing your code, %edit will return as output the code you + typed in the editor (except when it was an existing file). This way + you can reload the code in further invocations of %edit as a variable, + via _ or Out[], where is the prompt number of + the output. + + Note that %edit is also available through the alias %ed. + + This is an example of creating a simple function inside the editor and + then modifying it. First, start up the editor:: + + In [1]: ed + Editing... done. Executing edited code... + Out[1]: 'def foo():\\n print "foo() was defined in an editing + session"\\n' + + We can then call the function foo():: + + In [2]: foo() + foo() was defined in an editing session + + Now we edit foo. IPython automatically loads the editor with the + (temporary) file where foo() was previously defined:: + + In [3]: ed foo + Editing... done. Executing edited code... + + And if we call foo() again we get the modified version:: + + In [4]: foo() + foo() has now been changed! + + Here is an example of how to edit a code snippet successive + times. First we call the editor:: + + In [5]: ed + Editing... done. Executing edited code... + hello + Out[5]: "print 'hello'\\n" + + Now we call it again with the previous output (stored in _):: + + In [6]: ed _ + Editing... done. Executing edited code... + hello world + Out[6]: "print 'hello world'\\n" + + Now we call it with the output #8 (stored in _8, also as Out[8]):: + + In [7]: ed _8 + Editing... done. Executing edited code... + hello again + Out[7]: "print 'hello again'\\n" + + + Changing the default editor hook: + + If you wish to write your own editor hook, you can put it in a + configuration file which you load at startup time. The default hook + is defined in the IPython.core.hooks module, and you can use that as a + starting example for further modifications. That file also has + general instructions on how to set a new hook for use once you've + defined it.""" + opts,args = self.parse_options(parameter_s,'prxn:') + + try: + filename, lineno, is_temp = self._find_edit_target(args, opts, last_call) + except MacroToEdit as e: + self._edit_macro(args, e.args[0]) + return + + # do actual editing here + print 'Editing...', + sys.stdout.flush() + try: + # Quote filenames that may have spaces in them + if ' ' in filename: + filename = "'%s'" % filename + self.shell.hooks.editor(filename,lineno) + except TryNext: + warn('Could not open editor') + return + + # XXX TODO: should this be generalized for all string vars? + # For now, this is special-cased to blocks created by cpaste + if args.strip() == 'pasted_block': + self.shell.user_ns['pasted_block'] = file_read(filename) + + if 'x' in opts: # -x prevents actual execution + print + else: + print 'done. Executing edited code...' + if 'r' in opts: # Untranslated IPython code + self.shell.run_cell(file_read(filename), + store_history=False) + else: + self.shell.safe_execfile(filename, self.shell.user_ns, + self.shell.user_ns) + + if is_temp: + try: + return open(filename).read() + except IOError,msg: + if msg.filename == filename: + warn('File not found. Did you forget to save?') + return + else: + self.shell.showtraceback() From bb38a61a140f44078cc234ed95ce85784fa7c894 Mon Sep 17 00:00:00 2001 From: Fernando Perez Date: Wed, 23 May 2012 13:52:07 -0700 Subject: [PATCH 056/103] Create core.magics.config according to new API. --- IPython/core/interactiveshell.py | 2 +- IPython/core/magic_functions.py | 125 +------------------------- IPython/core/magics/__init__.py | 1 + IPython/core/magics/code.py | 2 +- IPython/core/magics/config.py | 146 +++++++++++++++++++++++++++++++ 5 files changed, 151 insertions(+), 125 deletions(-) create mode 100644 IPython/core/magics/config.py diff --git a/IPython/core/interactiveshell.py b/IPython/core/interactiveshell.py index 41a6e4518fc..c9ee397e817 100644 --- a/IPython/core/interactiveshell.py +++ b/IPython/core/interactiveshell.py @@ -2005,7 +2005,7 @@ def init_magics(self): self.register_magic_function = self.magics_manager.register_function self.define_magic = self.magics_manager.define_magic - self.register_magics(m.BasicMagics, m.CodeMagics, mf.ConfigMagics, + self.register_magics(m.BasicMagics, m.CodeMagics, m.ConfigMagics, mf.ExecutionMagics, mf.NamespaceMagics, mf.AutoMagics, mf.OSMagics, mf.LoggingMagics, mf.ExtensionsMagics, mf.PylabMagics, m.HistoryMagics, mf.DeprecatedMagics) diff --git a/IPython/core/magic_functions.py b/IPython/core/magic_functions.py index 4c85f657095..36a06568bac 100644 --- a/IPython/core/magic_functions.py +++ b/IPython/core/magic_functions.py @@ -9,9 +9,11 @@ # Distributed under the terms of the BSD License. The full license is in # the file COPYING, distributed as part of this software. #----------------------------------------------------------------------------- + #----------------------------------------------------------------------------- # Imports #----------------------------------------------------------------------------- + # Stdlib import __builtin__ as builtin_mod import bdb @@ -63,129 +65,6 @@ #----------------------------------------------------------------------------- # Magic implementation classes #----------------------------------------------------------------------------- - -@register_magics -class ConfigMagics(Magics): - - def __init__(self, shell): - super(ConfigMagics, self).__init__(shell) - self.configurables = [] - - @line_magic - def config(self, s): - """configure IPython - - %config Class[.trait=value] - - This magic exposes most of the IPython config system. Any - Configurable class should be able to be configured with the simple - line:: - - %config Class.trait=value - - Where `value` will be resolved in the user's namespace, if it is an - expression or variable name. - - Examples - -------- - - To see what classes are available for config, pass no arguments:: - - In [1]: %config - Available objects for config: - TerminalInteractiveShell - HistoryManager - PrefilterManager - AliasManager - IPCompleter - PromptManager - DisplayFormatter - - To view what is configurable on a given class, just pass the class - name:: - - In [2]: %config IPCompleter - IPCompleter options - ----------------- - IPCompleter.omit__names= - Current: 2 - Choices: (0, 1, 2) - Instruct the completer to omit private method names - Specifically, when completing on ``object.``. - When 2 [default]: all names that start with '_' will be excluded. - When 1: all 'magic' names (``__foo__``) will be excluded. - When 0: nothing will be excluded. - IPCompleter.merge_completions= - Current: True - Whether to merge completion results into a single list - If False, only the completion results from the first non-empty - completer will be returned. - IPCompleter.limit_to__all__= - Current: False - Instruct the completer to use __all__ for the completion - Specifically, when completing on ``object.``. - When True: only those names in obj.__all__ will be included. - When False [default]: the __all__ attribute is ignored - IPCompleter.greedy= - Current: False - Activate greedy completion - This will enable completion on elements of lists, results of - function calls, etc., but can be unsafe because the code is - actually evaluated on TAB. - - but the real use is in setting values:: - - In [3]: %config IPCompleter.greedy = True - - and these values are read from the user_ns if they are variables:: - - In [4]: feeling_greedy=False - - In [5]: %config IPCompleter.greedy = feeling_greedy - - """ - from IPython.config.loader import Config - # some IPython objects are Configurable, but do not yet have - # any configurable traits. Exclude them from the effects of - # this magic, as their presence is just noise: - configurables = [ c for c in self.shell.configurables - if c.__class__.class_traits(config=True) ] - classnames = [ c.__class__.__name__ for c in configurables ] - - line = s.strip() - if not line: - # print available configurable names - print "Available objects for config:" - for name in classnames: - print " ", name - return - elif line in classnames: - # `%config TerminalInteractiveShell` will print trait info for - # TerminalInteractiveShell - c = configurables[classnames.index(line)] - cls = c.__class__ - help = cls.class_get_help(c) - # strip leading '--' from cl-args: - help = re.sub(re.compile(r'^--', re.MULTILINE), '', help) - print help - return - elif '=' not in line: - raise UsageError("Invalid config statement: %r, " - "should be Class.trait = value" % line) - - # otherwise, assume we are setting configurables. - # leave quotes on args when splitting, because we want - # unquoted args to eval in user_ns - cfg = Config() - exec "cfg."+line in locals(), self.shell.user_ns - - for configurable in configurables: - try: - configurable.update_config(cfg) - except Exception as e: - error(e) - - @register_magics class NamespaceMagics(Magics): """Magics to manage various aspects of the user's namespace. diff --git a/IPython/core/magics/__init__.py b/IPython/core/magics/__init__.py index 528a1e9e0e2..f8f80959837 100644 --- a/IPython/core/magics/__init__.py +++ b/IPython/core/magics/__init__.py @@ -15,6 +15,7 @@ from IPython.core.magic import Magics, register_magics from .basic import BasicMagics from .code import CodeMagics, MacroToEdit +from .config import ConfigMagics from .history import HistoryMagics #----------------------------------------------------------------------------- diff --git a/IPython/core/magics/code.py b/IPython/core/magics/code.py index 767211c8162..7052d58b14f 100644 --- a/IPython/core/magics/code.py +++ b/IPython/core/magics/code.py @@ -1,4 +1,4 @@ -"""Implementation of basic magic functions. +"""Implementation of code management magic functions. """ #----------------------------------------------------------------------------- # Copyright (c) 2012 The IPython Development Team. diff --git a/IPython/core/magics/config.py b/IPython/core/magics/config.py new file mode 100644 index 00000000000..204e8811a34 --- /dev/null +++ b/IPython/core/magics/config.py @@ -0,0 +1,146 @@ +"""Implementation of configuration-related magic functions. +""" +#----------------------------------------------------------------------------- +# Copyright (c) 2012 The IPython Development Team. +# +# Distributed under the terms of the Modified BSD License. +# +# The full license is in the file COPYING.txt, distributed with this software. +#----------------------------------------------------------------------------- + +#----------------------------------------------------------------------------- +# Imports +#----------------------------------------------------------------------------- + +# Stdlib +import re + +# Our own packages +from IPython.core.error import UsageError +from IPython.core.magic import Magics, register_magics, line_magic +from IPython.utils.warn import error + +#----------------------------------------------------------------------------- +# Magic implementation classes +#----------------------------------------------------------------------------- + +@register_magics +class ConfigMagics(Magics): + + def __init__(self, shell): + super(ConfigMagics, self).__init__(shell) + self.configurables = [] + + @line_magic + def config(self, s): + """configure IPython + + %config Class[.trait=value] + + This magic exposes most of the IPython config system. Any + Configurable class should be able to be configured with the simple + line:: + + %config Class.trait=value + + Where `value` will be resolved in the user's namespace, if it is an + expression or variable name. + + Examples + -------- + + To see what classes are available for config, pass no arguments:: + + In [1]: %config + Available objects for config: + TerminalInteractiveShell + HistoryManager + PrefilterManager + AliasManager + IPCompleter + PromptManager + DisplayFormatter + + To view what is configurable on a given class, just pass the class + name:: + + In [2]: %config IPCompleter + IPCompleter options + ----------------- + IPCompleter.omit__names= + Current: 2 + Choices: (0, 1, 2) + Instruct the completer to omit private method names + Specifically, when completing on ``object.``. + When 2 [default]: all names that start with '_' will be excluded. + When 1: all 'magic' names (``__foo__``) will be excluded. + When 0: nothing will be excluded. + IPCompleter.merge_completions= + Current: True + Whether to merge completion results into a single list + If False, only the completion results from the first non-empty + completer will be returned. + IPCompleter.limit_to__all__= + Current: False + Instruct the completer to use __all__ for the completion + Specifically, when completing on ``object.``. + When True: only those names in obj.__all__ will be included. + When False [default]: the __all__ attribute is ignored + IPCompleter.greedy= + Current: False + Activate greedy completion + This will enable completion on elements of lists, results of + function calls, etc., but can be unsafe because the code is + actually evaluated on TAB. + + but the real use is in setting values:: + + In [3]: %config IPCompleter.greedy = True + + and these values are read from the user_ns if they are variables:: + + In [4]: feeling_greedy=False + + In [5]: %config IPCompleter.greedy = feeling_greedy + + """ + from IPython.config.loader import Config + # some IPython objects are Configurable, but do not yet have + # any configurable traits. Exclude them from the effects of + # this magic, as their presence is just noise: + configurables = [ c for c in self.shell.configurables + if c.__class__.class_traits(config=True) ] + classnames = [ c.__class__.__name__ for c in configurables ] + + line = s.strip() + if not line: + # print available configurable names + print "Available objects for config:" + for name in classnames: + print " ", name + return + elif line in classnames: + # `%config TerminalInteractiveShell` will print trait info for + # TerminalInteractiveShell + c = configurables[classnames.index(line)] + cls = c.__class__ + help = cls.class_get_help(c) + # strip leading '--' from cl-args: + help = re.sub(re.compile(r'^--', re.MULTILINE), '', help) + print help + return + elif '=' not in line: + raise UsageError("Invalid config statement: %r, " + "should be Class.trait = value" % line) + + # otherwise, assume we are setting configurables. + # leave quotes on args when splitting, because we want + # unquoted args to eval in user_ns + cfg = Config() + exec "cfg."+line in locals(), self.shell.user_ns + + for configurable in configurables: + try: + configurable.update_config(cfg) + except Exception as e: + error(e) From 020e4ff761a6bbeddf80e615d8e3d6c89c42ccab Mon Sep 17 00:00:00 2001 From: Fernando Perez Date: Wed, 23 May 2012 14:24:17 -0700 Subject: [PATCH 057/103] Create core.magics.namespace according to new API. --- IPython/core/interactiveshell.py | 2 +- IPython/core/magic_functions.py | 672 ----------------------------- IPython/core/magics/__init__.py | 1 + IPython/core/magics/namespace.py | 702 +++++++++++++++++++++++++++++++ 4 files changed, 704 insertions(+), 673 deletions(-) create mode 100644 IPython/core/magics/namespace.py diff --git a/IPython/core/interactiveshell.py b/IPython/core/interactiveshell.py index c9ee397e817..5b6c7f2911a 100644 --- a/IPython/core/interactiveshell.py +++ b/IPython/core/interactiveshell.py @@ -2006,7 +2006,7 @@ def init_magics(self): self.define_magic = self.magics_manager.define_magic self.register_magics(m.BasicMagics, m.CodeMagics, m.ConfigMagics, - mf.ExecutionMagics, mf.NamespaceMagics, mf.AutoMagics, + mf.ExecutionMagics, m.NamespaceMagics, mf.AutoMagics, mf.OSMagics, mf.LoggingMagics, mf.ExtensionsMagics, mf.PylabMagics, m.HistoryMagics, mf.DeprecatedMagics) diff --git a/IPython/core/magic_functions.py b/IPython/core/magic_functions.py index 36a06568bac..f57b2789bf1 100644 --- a/IPython/core/magic_functions.py +++ b/IPython/core/magic_functions.py @@ -65,678 +65,6 @@ #----------------------------------------------------------------------------- # Magic implementation classes #----------------------------------------------------------------------------- -@register_magics -class NamespaceMagics(Magics): - """Magics to manage various aspects of the user's namespace. - - These include listing variables, introspecting into them, etc. - """ - - @line_magic - def pinfo(self, parameter_s='', namespaces=None): - """Provide detailed information about an object. - - '%pinfo object' is just a synonym for object? or ?object.""" - - #print 'pinfo par: <%s>' % parameter_s # dbg - - - # detail_level: 0 -> obj? , 1 -> obj?? - detail_level = 0 - # We need to detect if we got called as 'pinfo pinfo foo', which can - # happen if the user types 'pinfo foo?' at the cmd line. - pinfo,qmark1,oname,qmark2 = \ - re.match('(pinfo )?(\?*)(.*?)(\??$)',parameter_s).groups() - if pinfo or qmark1 or qmark2: - detail_level = 1 - if "*" in oname: - self.psearch(oname) - else: - self.shell._inspect('pinfo', oname, detail_level=detail_level, - namespaces=namespaces) - - @line_magic - def pinfo2(self, parameter_s='', namespaces=None): - """Provide extra detailed information about an object. - - '%pinfo2 object' is just a synonym for object?? or ??object.""" - self.shell._inspect('pinfo', parameter_s, detail_level=1, - namespaces=namespaces) - - @skip_doctest - @line_magic - def pdef(self, parameter_s='', namespaces=None): - """Print the definition header for any callable object. - - If the object is a class, print the constructor information. - - Examples - -------- - :: - - In [3]: %pdef urllib.urlopen - urllib.urlopen(url, data=None, proxies=None) - """ - self._inspect('pdef',parameter_s, namespaces) - - @line_magic - def pdoc(self, parameter_s='', namespaces=None): - """Print the docstring for an object. - - If the given object is a class, it will print both the class and the - constructor docstrings.""" - self._inspect('pdoc',parameter_s, namespaces) - - @line_magic - def psource(self, parameter_s='', namespaces=None): - """Print (or run through pager) the source code for an object.""" - self._inspect('psource',parameter_s, namespaces) - - @line_magic - def pfile(self, parameter_s=''): - """Print (or run through pager) the file where an object is defined. - - The file opens at the line where the object definition begins. IPython - will honor the environment variable PAGER if set, and otherwise will - do its best to print the file in a convenient form. - - If the given argument is not an object currently defined, IPython will - try to interpret it as a filename (automatically adding a .py extension - if needed). You can thus use %pfile as a syntax highlighting code - viewer.""" - - # first interpret argument as an object name - out = self._inspect('pfile',parameter_s) - # if not, try the input as a filename - if out == 'not found': - try: - filename = get_py_filename(parameter_s) - except IOError,msg: - print msg - return - page.page(self.shell.inspector.format(open(filename).read())) - - @line_magic - def psearch(self, parameter_s=''): - """Search for object in namespaces by wildcard. - - %psearch [options] PATTERN [OBJECT TYPE] - - Note: ? can be used as a synonym for %psearch, at the beginning or at - the end: both a*? and ?a* are equivalent to '%psearch a*'. Still, the - rest of the command line must be unchanged (options come first), so - for example the following forms are equivalent - - %psearch -i a* function - -i a* function? - ?-i a* function - - Arguments: - - PATTERN - - where PATTERN is a string containing * as a wildcard similar to its - use in a shell. The pattern is matched in all namespaces on the - search path. By default objects starting with a single _ are not - matched, many IPython generated objects have a single - underscore. The default is case insensitive matching. Matching is - also done on the attributes of objects and not only on the objects - in a module. - - [OBJECT TYPE] - - Is the name of a python type from the types module. The name is - given in lowercase without the ending type, ex. StringType is - written string. By adding a type here only objects matching the - given type are matched. Using all here makes the pattern match all - types (this is the default). - - Options: - - -a: makes the pattern match even objects whose names start with a - single underscore. These names are normally omitted from the - search. - - -i/-c: make the pattern case insensitive/sensitive. If neither of - these options are given, the default is read from your configuration - file, with the option ``InteractiveShell.wildcards_case_sensitive``. - If this option is not specified in your configuration file, IPython's - internal default is to do a case sensitive search. - - -e/-s NAMESPACE: exclude/search a given namespace. The pattern you - specify can be searched in any of the following namespaces: - 'builtin', 'user', 'user_global','internal', 'alias', where - 'builtin' and 'user' are the search defaults. Note that you should - not use quotes when specifying namespaces. - - 'Builtin' contains the python module builtin, 'user' contains all - user data, 'alias' only contain the shell aliases and no python - objects, 'internal' contains objects used by IPython. The - 'user_global' namespace is only used by embedded IPython instances, - and it contains module-level globals. You can add namespaces to the - search with -s or exclude them with -e (these options can be given - more than once). - - Examples - -------- - :: - - %psearch a* -> objects beginning with an a - %psearch -e builtin a* -> objects NOT in the builtin space starting in a - %psearch a* function -> all functions beginning with an a - %psearch re.e* -> objects beginning with an e in module re - %psearch r*.e* -> objects that start with e in modules starting in r - %psearch r*.* string -> all strings in modules beginning with r - - Case sensitive search:: - - %psearch -c a* list all object beginning with lower case a - - Show objects beginning with a single _:: - - %psearch -a _* list objects beginning with a single underscore - """ - try: - parameter_s.encode('ascii') - except UnicodeEncodeError: - print 'Python identifiers can only contain ascii characters.' - return - - # default namespaces to be searched - def_search = ['user_local', 'user_global', 'builtin'] - - # Process options/args - opts,args = self.parse_options(parameter_s,'cias:e:',list_all=True) - opt = opts.get - shell = self.shell - psearch = shell.inspector.psearch - - # select case options - if opts.has_key('i'): - ignore_case = True - elif opts.has_key('c'): - ignore_case = False - else: - ignore_case = not shell.wildcards_case_sensitive - - # Build list of namespaces to search from user options - def_search.extend(opt('s',[])) - ns_exclude = ns_exclude=opt('e',[]) - ns_search = [nm for nm in def_search if nm not in ns_exclude] - - # Call the actual search - try: - psearch(args,shell.ns_table,ns_search, - show_all=opt('a'),ignore_case=ignore_case) - except: - shell.showtraceback() - - @skip_doctest - @line_magic - def who_ls(self, parameter_s=''): - """Return a sorted list of all interactive variables. - - If arguments are given, only variables of types matching these - arguments are returned. - - Examples - -------- - - Define two variables and list them with who_ls:: - - In [1]: alpha = 123 - - In [2]: beta = 'test' - - In [3]: %who_ls - Out[3]: ['alpha', 'beta'] - - In [4]: %who_ls int - Out[4]: ['alpha'] - - In [5]: %who_ls str - Out[5]: ['beta'] - """ - - user_ns = self.shell.user_ns - user_ns_hidden = self.shell.user_ns_hidden - out = [ i for i in user_ns - if not i.startswith('_') \ - and not i in user_ns_hidden ] - - typelist = parameter_s.split() - if typelist: - typeset = set(typelist) - out = [i for i in out if type(user_ns[i]).__name__ in typeset] - - out.sort() - return out - - @skip_doctest - @line_magic - def who(self, parameter_s=''): - """Print all interactive variables, with some minimal formatting. - - If any arguments are given, only variables whose type matches one of - these are printed. For example:: - - %who function str - - will only list functions and strings, excluding all other types of - variables. To find the proper type names, simply use type(var) at a - command line to see how python prints type names. For example: - - :: - - In [1]: type('hello')\\ - Out[1]: - - indicates that the type name for strings is 'str'. - - ``%who`` always excludes executed names loaded through your configuration - file and things which are internal to IPython. - - This is deliberate, as typically you may load many modules and the - purpose of %who is to show you only what you've manually defined. - - Examples - -------- - - Define two variables and list them with who:: - - In [1]: alpha = 123 - - In [2]: beta = 'test' - - In [3]: %who - alpha beta - - In [4]: %who int - alpha - - In [5]: %who str - beta - """ - - varlist = self.who_ls(parameter_s) - if not varlist: - if parameter_s: - print 'No variables match your requested type.' - else: - print 'Interactive namespace is empty.' - return - - # if we have variables, move on... - count = 0 - for i in varlist: - print i+'\t', - count += 1 - if count > 8: - count = 0 - print - print - - @skip_doctest - @line_magic - def whos(self, parameter_s=''): - """Like %who, but gives some extra information about each variable. - - The same type filtering of %who can be applied here. - - For all variables, the type is printed. Additionally it prints: - - - For {},[],(): their length. - - - For numpy arrays, a summary with shape, number of - elements, typecode and size in memory. - - - Everything else: a string representation, snipping their middle if - too long. - - Examples - -------- - - Define two variables and list them with whos:: - - In [1]: alpha = 123 - - In [2]: beta = 'test' - - In [3]: %whos - Variable Type Data/Info - -------------------------------- - alpha int 123 - beta str test - """ - - varnames = self.who_ls(parameter_s) - if not varnames: - if parameter_s: - print 'No variables match your requested type.' - else: - print 'Interactive namespace is empty.' - return - - # if we have variables, move on... - - # for these types, show len() instead of data: - seq_types = ['dict', 'list', 'tuple'] - - # for numpy arrays, display summary info - ndarray_type = None - if 'numpy' in sys.modules: - try: - from numpy import ndarray - except ImportError: - pass - else: - ndarray_type = ndarray.__name__ - - # Find all variable names and types so we can figure out column sizes - def get_vars(i): - return self.shell.user_ns[i] - - # some types are well known and can be shorter - abbrevs = {'IPython.core.macro.Macro' : 'Macro'} - def type_name(v): - tn = type(v).__name__ - return abbrevs.get(tn,tn) - - varlist = map(get_vars,varnames) - - typelist = [] - for vv in varlist: - tt = type_name(vv) - - if tt=='instance': - typelist.append( abbrevs.get(str(vv.__class__), - str(vv.__class__))) - else: - typelist.append(tt) - - # column labels and # of spaces as separator - varlabel = 'Variable' - typelabel = 'Type' - datalabel = 'Data/Info' - colsep = 3 - # variable format strings - vformat = "{0:<{varwidth}}{1:<{typewidth}}" - aformat = "%s: %s elems, type `%s`, %s bytes" - # find the size of the columns to format the output nicely - varwidth = max(max(map(len,varnames)), len(varlabel)) + colsep - typewidth = max(max(map(len,typelist)), len(typelabel)) + colsep - # table header - print varlabel.ljust(varwidth) + typelabel.ljust(typewidth) + \ - ' '+datalabel+'\n' + '-'*(varwidth+typewidth+len(datalabel)+1) - # and the table itself - kb = 1024 - Mb = 1048576 # kb**2 - for vname,var,vtype in zip(varnames,varlist,typelist): - print vformat.format(vname, vtype, varwidth=varwidth, typewidth=typewidth), - if vtype in seq_types: - print "n="+str(len(var)) - elif vtype == ndarray_type: - vshape = str(var.shape).replace(',','').replace(' ','x')[1:-1] - if vtype==ndarray_type: - # numpy - vsize = var.size - vbytes = vsize*var.itemsize - vdtype = var.dtype - - if vbytes < 100000: - print aformat % (vshape,vsize,vdtype,vbytes) - else: - print aformat % (vshape,vsize,vdtype,vbytes), - if vbytes < Mb: - print '(%s kb)' % (vbytes/kb,) - else: - print '(%s Mb)' % (vbytes/Mb,) - else: - try: - vstr = str(var) - except UnicodeEncodeError: - vstr = unicode(var).encode(DEFAULT_ENCODING, - 'backslashreplace') - except: - vstr = "" % id(var) - vstr = vstr.replace('\n','\\n') - if len(vstr) < 50: - print vstr - else: - print vstr[:25] + "<...>" + vstr[-25:] - - @line_magic - def reset(self, parameter_s=''): - """Resets the namespace by removing all names defined by the user, if - called without arguments, or by removing some types of objects, such - as everything currently in IPython's In[] and Out[] containers (see - the parameters for details). - - Parameters - ---------- - -f : force reset without asking for confirmation. - - -s : 'Soft' reset: Only clears your namespace, leaving history intact. - References to objects may be kept. By default (without this option), - we do a 'hard' reset, giving you a new session and removing all - references to objects from the current session. - - in : reset input history - - out : reset output history - - dhist : reset directory history - - array : reset only variables that are NumPy arrays - - See Also - -------- - magic_reset_selective : invoked as ``%reset_selective`` - - Examples - -------- - :: - - In [6]: a = 1 - - In [7]: a - Out[7]: 1 - - In [8]: 'a' in _ip.user_ns - Out[8]: True - - In [9]: %reset -f - - In [1]: 'a' in _ip.user_ns - Out[1]: False - - In [2]: %reset -f in - Flushing input history - - In [3]: %reset -f dhist in - Flushing directory history - Flushing input history - - Notes - ----- - Calling this magic from clients that do not implement standard input, - such as the ipython notebook interface, will reset the namespace - without confirmation. - """ - opts, args = self.parse_options(parameter_s,'sf', mode='list') - if 'f' in opts: - ans = True - else: - try: - ans = self.shell.ask_yes_no( - "Once deleted, variables cannot be recovered. Proceed (y/[n])?", - default='n') - except StdinNotImplementedError: - ans = True - if not ans: - print 'Nothing done.' - return - - if 's' in opts: # Soft reset - user_ns = self.shell.user_ns - for i in self.who_ls(): - del(user_ns[i]) - elif len(args) == 0: # Hard reset - self.shell.reset(new_session = False) - - # reset in/out/dhist/array: previously extensinions/clearcmd.py - ip = self.shell - user_ns = self.shell.user_ns # local lookup, heavily used - - for target in args: - target = target.lower() # make matches case insensitive - if target == 'out': - print "Flushing output cache (%d entries)" % len(user_ns['_oh']) - self.shell.displayhook.flush() - - elif target == 'in': - print "Flushing input history" - pc = self.shell.displayhook.prompt_count + 1 - for n in range(1, pc): - key = '_i'+repr(n) - user_ns.pop(key,None) - user_ns.update(dict(_i=u'',_ii=u'',_iii=u'')) - hm = ip.history_manager - # don't delete these, as %save and %macro depending on the - # length of these lists to be preserved - hm.input_hist_parsed[:] = [''] * pc - hm.input_hist_raw[:] = [''] * pc - # hm has internal machinery for _i,_ii,_iii, clear it out - hm._i = hm._ii = hm._iii = hm._i00 = u'' - - elif target == 'array': - # Support cleaning up numpy arrays - try: - from numpy import ndarray - # This must be done with items and not iteritems because - # we're going to modify the dict in-place. - for x,val in user_ns.items(): - if isinstance(val,ndarray): - del user_ns[x] - except ImportError: - print "reset array only works if Numpy is available." - - elif target == 'dhist': - print "Flushing directory history" - del user_ns['_dh'][:] - - else: - print "Don't know how to reset ", - print target + ", please run `%reset?` for details" - - gc.collect() - - @line_magic - def reset_selective(self, parameter_s=''): - """Resets the namespace by removing names defined by the user. - - Input/Output history are left around in case you need them. - - %reset_selective [-f] regex - - No action is taken if regex is not included - - Options - -f : force reset without asking for confirmation. - - See Also - -------- - magic_reset : invoked as ``%reset`` - - Examples - -------- - - We first fully reset the namespace so your output looks identical to - this example for pedagogical reasons; in practice you do not need a - full reset:: - - In [1]: %reset -f - - Now, with a clean namespace we can make a few variables and use - ``%reset_selective`` to only delete names that match our regexp:: - - In [2]: a=1; b=2; c=3; b1m=4; b2m=5; b3m=6; b4m=7; b2s=8 - - In [3]: who_ls - Out[3]: ['a', 'b', 'b1m', 'b2m', 'b2s', 'b3m', 'b4m', 'c'] - - In [4]: %reset_selective -f b[2-3]m - - In [5]: who_ls - Out[5]: ['a', 'b', 'b1m', 'b2s', 'b4m', 'c'] - - In [6]: %reset_selective -f d - - In [7]: who_ls - Out[7]: ['a', 'b', 'b1m', 'b2s', 'b4m', 'c'] - - In [8]: %reset_selective -f c - - In [9]: who_ls - Out[9]: ['a', 'b', 'b1m', 'b2s', 'b4m'] - - In [10]: %reset_selective -f b - - In [11]: who_ls - Out[11]: ['a'] - - Notes - ----- - Calling this magic from clients that do not implement standard input, - such as the ipython notebook interface, will reset the namespace - without confirmation. - """ - - opts, regex = self.parse_options(parameter_s,'f') - - if opts.has_key('f'): - ans = True - else: - try: - ans = self.shell.ask_yes_no( - "Once deleted, variables cannot be recovered. Proceed (y/[n])? ", - default='n') - except StdinNotImplementedError: - ans = True - if not ans: - print 'Nothing done.' - return - user_ns = self.shell.user_ns - if not regex: - print 'No regex pattern specified. Nothing done.' - return - else: - try: - m = re.compile(regex) - except TypeError: - raise TypeError('regex must be a string or compiled pattern') - for i in self.who_ls(): - if m.search(i): - del(user_ns[i]) - - @line_magic - def xdel(self, parameter_s=''): - """Delete a variable, trying to clear it from anywhere that - IPython's machinery has references to it. By default, this uses - the identity of the named object in the user namespace to remove - references held under other names. The object is also removed - from the output history. - - Options - -n : Delete the specified name from all namespaces, without - checking their identity. - """ - opts, varname = self.parse_options(parameter_s,'n') - try: - self.shell.del_var(varname, ('n' in opts)) - except (NameError, ValueError) as e: - print type(e).__name__ +": "+ str(e) - @register_magics class ExecutionMagics(Magics): diff --git a/IPython/core/magics/__init__.py b/IPython/core/magics/__init__.py index f8f80959837..3ba9c33567c 100644 --- a/IPython/core/magics/__init__.py +++ b/IPython/core/magics/__init__.py @@ -17,6 +17,7 @@ from .code import CodeMagics, MacroToEdit from .config import ConfigMagics from .history import HistoryMagics +from .namespace import NamespaceMagics #----------------------------------------------------------------------------- # Magic implementation classes diff --git a/IPython/core/magics/namespace.py b/IPython/core/magics/namespace.py new file mode 100644 index 00000000000..e20d0de6ce1 --- /dev/null +++ b/IPython/core/magics/namespace.py @@ -0,0 +1,702 @@ +"""Implementation of namespace-related magic functions. +""" +#----------------------------------------------------------------------------- +# Copyright (c) 2012 The IPython Development Team. +# +# Distributed under the terms of the Modified BSD License. +# +# The full license is in the file COPYING.txt, distributed with this software. +#----------------------------------------------------------------------------- + +#----------------------------------------------------------------------------- +# Imports +#----------------------------------------------------------------------------- + +# Stdlib +import gc +import re +import sys + +# Our own packages +from IPython.core import page +from IPython.core.error import StdinNotImplementedError +from IPython.core.magic import Magics, register_magics, line_magic +from IPython.testing.skipdoctest import skip_doctest +from IPython.utils.encoding import DEFAULT_ENCODING +from IPython.utils.path import get_py_filename + +#----------------------------------------------------------------------------- +# Magic implementation classes +#----------------------------------------------------------------------------- + +@register_magics +class NamespaceMagics(Magics): + """Magics to manage various aspects of the user's namespace. + + These include listing variables, introspecting into them, etc. + """ + + @line_magic + def pinfo(self, parameter_s='', namespaces=None): + """Provide detailed information about an object. + + '%pinfo object' is just a synonym for object? or ?object.""" + + #print 'pinfo par: <%s>' % parameter_s # dbg + + + # detail_level: 0 -> obj? , 1 -> obj?? + detail_level = 0 + # We need to detect if we got called as 'pinfo pinfo foo', which can + # happen if the user types 'pinfo foo?' at the cmd line. + pinfo,qmark1,oname,qmark2 = \ + re.match('(pinfo )?(\?*)(.*?)(\??$)',parameter_s).groups() + if pinfo or qmark1 or qmark2: + detail_level = 1 + if "*" in oname: + self.psearch(oname) + else: + self.shell._inspect('pinfo', oname, detail_level=detail_level, + namespaces=namespaces) + + @line_magic + def pinfo2(self, parameter_s='', namespaces=None): + """Provide extra detailed information about an object. + + '%pinfo2 object' is just a synonym for object?? or ??object.""" + self.shell._inspect('pinfo', parameter_s, detail_level=1, + namespaces=namespaces) + + @skip_doctest + @line_magic + def pdef(self, parameter_s='', namespaces=None): + """Print the definition header for any callable object. + + If the object is a class, print the constructor information. + + Examples + -------- + :: + + In [3]: %pdef urllib.urlopen + urllib.urlopen(url, data=None, proxies=None) + """ + self._inspect('pdef',parameter_s, namespaces) + + @line_magic + def pdoc(self, parameter_s='', namespaces=None): + """Print the docstring for an object. + + If the given object is a class, it will print both the class and the + constructor docstrings.""" + self._inspect('pdoc',parameter_s, namespaces) + + @line_magic + def psource(self, parameter_s='', namespaces=None): + """Print (or run through pager) the source code for an object.""" + self._inspect('psource',parameter_s, namespaces) + + @line_magic + def pfile(self, parameter_s=''): + """Print (or run through pager) the file where an object is defined. + + The file opens at the line where the object definition begins. IPython + will honor the environment variable PAGER if set, and otherwise will + do its best to print the file in a convenient form. + + If the given argument is not an object currently defined, IPython will + try to interpret it as a filename (automatically adding a .py extension + if needed). You can thus use %pfile as a syntax highlighting code + viewer.""" + + # first interpret argument as an object name + out = self._inspect('pfile',parameter_s) + # if not, try the input as a filename + if out == 'not found': + try: + filename = get_py_filename(parameter_s) + except IOError,msg: + print msg + return + page.page(self.shell.inspector.format(open(filename).read())) + + @line_magic + def psearch(self, parameter_s=''): + """Search for object in namespaces by wildcard. + + %psearch [options] PATTERN [OBJECT TYPE] + + Note: ? can be used as a synonym for %psearch, at the beginning or at + the end: both a*? and ?a* are equivalent to '%psearch a*'. Still, the + rest of the command line must be unchanged (options come first), so + for example the following forms are equivalent + + %psearch -i a* function + -i a* function? + ?-i a* function + + Arguments: + + PATTERN + + where PATTERN is a string containing * as a wildcard similar to its + use in a shell. The pattern is matched in all namespaces on the + search path. By default objects starting with a single _ are not + matched, many IPython generated objects have a single + underscore. The default is case insensitive matching. Matching is + also done on the attributes of objects and not only on the objects + in a module. + + [OBJECT TYPE] + + Is the name of a python type from the types module. The name is + given in lowercase without the ending type, ex. StringType is + written string. By adding a type here only objects matching the + given type are matched. Using all here makes the pattern match all + types (this is the default). + + Options: + + -a: makes the pattern match even objects whose names start with a + single underscore. These names are normally omitted from the + search. + + -i/-c: make the pattern case insensitive/sensitive. If neither of + these options are given, the default is read from your configuration + file, with the option ``InteractiveShell.wildcards_case_sensitive``. + If this option is not specified in your configuration file, IPython's + internal default is to do a case sensitive search. + + -e/-s NAMESPACE: exclude/search a given namespace. The pattern you + specify can be searched in any of the following namespaces: + 'builtin', 'user', 'user_global','internal', 'alias', where + 'builtin' and 'user' are the search defaults. Note that you should + not use quotes when specifying namespaces. + + 'Builtin' contains the python module builtin, 'user' contains all + user data, 'alias' only contain the shell aliases and no python + objects, 'internal' contains objects used by IPython. The + 'user_global' namespace is only used by embedded IPython instances, + and it contains module-level globals. You can add namespaces to the + search with -s or exclude them with -e (these options can be given + more than once). + + Examples + -------- + :: + + %psearch a* -> objects beginning with an a + %psearch -e builtin a* -> objects NOT in the builtin space starting in a + %psearch a* function -> all functions beginning with an a + %psearch re.e* -> objects beginning with an e in module re + %psearch r*.e* -> objects that start with e in modules starting in r + %psearch r*.* string -> all strings in modules beginning with r + + Case sensitive search:: + + %psearch -c a* list all object beginning with lower case a + + Show objects beginning with a single _:: + + %psearch -a _* list objects beginning with a single underscore + """ + try: + parameter_s.encode('ascii') + except UnicodeEncodeError: + print 'Python identifiers can only contain ascii characters.' + return + + # default namespaces to be searched + def_search = ['user_local', 'user_global', 'builtin'] + + # Process options/args + opts,args = self.parse_options(parameter_s,'cias:e:',list_all=True) + opt = opts.get + shell = self.shell + psearch = shell.inspector.psearch + + # select case options + if opts.has_key('i'): + ignore_case = True + elif opts.has_key('c'): + ignore_case = False + else: + ignore_case = not shell.wildcards_case_sensitive + + # Build list of namespaces to search from user options + def_search.extend(opt('s',[])) + ns_exclude = ns_exclude=opt('e',[]) + ns_search = [nm for nm in def_search if nm not in ns_exclude] + + # Call the actual search + try: + psearch(args,shell.ns_table,ns_search, + show_all=opt('a'),ignore_case=ignore_case) + except: + shell.showtraceback() + + @skip_doctest + @line_magic + def who_ls(self, parameter_s=''): + """Return a sorted list of all interactive variables. + + If arguments are given, only variables of types matching these + arguments are returned. + + Examples + -------- + + Define two variables and list them with who_ls:: + + In [1]: alpha = 123 + + In [2]: beta = 'test' + + In [3]: %who_ls + Out[3]: ['alpha', 'beta'] + + In [4]: %who_ls int + Out[4]: ['alpha'] + + In [5]: %who_ls str + Out[5]: ['beta'] + """ + + user_ns = self.shell.user_ns + user_ns_hidden = self.shell.user_ns_hidden + out = [ i for i in user_ns + if not i.startswith('_') \ + and not i in user_ns_hidden ] + + typelist = parameter_s.split() + if typelist: + typeset = set(typelist) + out = [i for i in out if type(user_ns[i]).__name__ in typeset] + + out.sort() + return out + + @skip_doctest + @line_magic + def who(self, parameter_s=''): + """Print all interactive variables, with some minimal formatting. + + If any arguments are given, only variables whose type matches one of + these are printed. For example:: + + %who function str + + will only list functions and strings, excluding all other types of + variables. To find the proper type names, simply use type(var) at a + command line to see how python prints type names. For example: + + :: + + In [1]: type('hello')\\ + Out[1]: + + indicates that the type name for strings is 'str'. + + ``%who`` always excludes executed names loaded through your configuration + file and things which are internal to IPython. + + This is deliberate, as typically you may load many modules and the + purpose of %who is to show you only what you've manually defined. + + Examples + -------- + + Define two variables and list them with who:: + + In [1]: alpha = 123 + + In [2]: beta = 'test' + + In [3]: %who + alpha beta + + In [4]: %who int + alpha + + In [5]: %who str + beta + """ + + varlist = self.who_ls(parameter_s) + if not varlist: + if parameter_s: + print 'No variables match your requested type.' + else: + print 'Interactive namespace is empty.' + return + + # if we have variables, move on... + count = 0 + for i in varlist: + print i+'\t', + count += 1 + if count > 8: + count = 0 + print + print + + @skip_doctest + @line_magic + def whos(self, parameter_s=''): + """Like %who, but gives some extra information about each variable. + + The same type filtering of %who can be applied here. + + For all variables, the type is printed. Additionally it prints: + + - For {},[],(): their length. + + - For numpy arrays, a summary with shape, number of + elements, typecode and size in memory. + + - Everything else: a string representation, snipping their middle if + too long. + + Examples + -------- + + Define two variables and list them with whos:: + + In [1]: alpha = 123 + + In [2]: beta = 'test' + + In [3]: %whos + Variable Type Data/Info + -------------------------------- + alpha int 123 + beta str test + """ + + varnames = self.who_ls(parameter_s) + if not varnames: + if parameter_s: + print 'No variables match your requested type.' + else: + print 'Interactive namespace is empty.' + return + + # if we have variables, move on... + + # for these types, show len() instead of data: + seq_types = ['dict', 'list', 'tuple'] + + # for numpy arrays, display summary info + ndarray_type = None + if 'numpy' in sys.modules: + try: + from numpy import ndarray + except ImportError: + pass + else: + ndarray_type = ndarray.__name__ + + # Find all variable names and types so we can figure out column sizes + def get_vars(i): + return self.shell.user_ns[i] + + # some types are well known and can be shorter + abbrevs = {'IPython.core.macro.Macro' : 'Macro'} + def type_name(v): + tn = type(v).__name__ + return abbrevs.get(tn,tn) + + varlist = map(get_vars,varnames) + + typelist = [] + for vv in varlist: + tt = type_name(vv) + + if tt=='instance': + typelist.append( abbrevs.get(str(vv.__class__), + str(vv.__class__))) + else: + typelist.append(tt) + + # column labels and # of spaces as separator + varlabel = 'Variable' + typelabel = 'Type' + datalabel = 'Data/Info' + colsep = 3 + # variable format strings + vformat = "{0:<{varwidth}}{1:<{typewidth}}" + aformat = "%s: %s elems, type `%s`, %s bytes" + # find the size of the columns to format the output nicely + varwidth = max(max(map(len,varnames)), len(varlabel)) + colsep + typewidth = max(max(map(len,typelist)), len(typelabel)) + colsep + # table header + print varlabel.ljust(varwidth) + typelabel.ljust(typewidth) + \ + ' '+datalabel+'\n' + '-'*(varwidth+typewidth+len(datalabel)+1) + # and the table itself + kb = 1024 + Mb = 1048576 # kb**2 + for vname,var,vtype in zip(varnames,varlist,typelist): + print vformat.format(vname, vtype, varwidth=varwidth, typewidth=typewidth), + if vtype in seq_types: + print "n="+str(len(var)) + elif vtype == ndarray_type: + vshape = str(var.shape).replace(',','').replace(' ','x')[1:-1] + if vtype==ndarray_type: + # numpy + vsize = var.size + vbytes = vsize*var.itemsize + vdtype = var.dtype + + if vbytes < 100000: + print aformat % (vshape,vsize,vdtype,vbytes) + else: + print aformat % (vshape,vsize,vdtype,vbytes), + if vbytes < Mb: + print '(%s kb)' % (vbytes/kb,) + else: + print '(%s Mb)' % (vbytes/Mb,) + else: + try: + vstr = str(var) + except UnicodeEncodeError: + vstr = unicode(var).encode(DEFAULT_ENCODING, + 'backslashreplace') + except: + vstr = "" % id(var) + vstr = vstr.replace('\n','\\n') + if len(vstr) < 50: + print vstr + else: + print vstr[:25] + "<...>" + vstr[-25:] + + @line_magic + def reset(self, parameter_s=''): + """Resets the namespace by removing all names defined by the user, if + called without arguments, or by removing some types of objects, such + as everything currently in IPython's In[] and Out[] containers (see + the parameters for details). + + Parameters + ---------- + -f : force reset without asking for confirmation. + + -s : 'Soft' reset: Only clears your namespace, leaving history intact. + References to objects may be kept. By default (without this option), + we do a 'hard' reset, giving you a new session and removing all + references to objects from the current session. + + in : reset input history + + out : reset output history + + dhist : reset directory history + + array : reset only variables that are NumPy arrays + + See Also + -------- + magic_reset_selective : invoked as ``%reset_selective`` + + Examples + -------- + :: + + In [6]: a = 1 + + In [7]: a + Out[7]: 1 + + In [8]: 'a' in _ip.user_ns + Out[8]: True + + In [9]: %reset -f + + In [1]: 'a' in _ip.user_ns + Out[1]: False + + In [2]: %reset -f in + Flushing input history + + In [3]: %reset -f dhist in + Flushing directory history + Flushing input history + + Notes + ----- + Calling this magic from clients that do not implement standard input, + such as the ipython notebook interface, will reset the namespace + without confirmation. + """ + opts, args = self.parse_options(parameter_s,'sf', mode='list') + if 'f' in opts: + ans = True + else: + try: + ans = self.shell.ask_yes_no( + "Once deleted, variables cannot be recovered. Proceed (y/[n])?", + default='n') + except StdinNotImplementedError: + ans = True + if not ans: + print 'Nothing done.' + return + + if 's' in opts: # Soft reset + user_ns = self.shell.user_ns + for i in self.who_ls(): + del(user_ns[i]) + elif len(args) == 0: # Hard reset + self.shell.reset(new_session = False) + + # reset in/out/dhist/array: previously extensinions/clearcmd.py + ip = self.shell + user_ns = self.shell.user_ns # local lookup, heavily used + + for target in args: + target = target.lower() # make matches case insensitive + if target == 'out': + print "Flushing output cache (%d entries)" % len(user_ns['_oh']) + self.shell.displayhook.flush() + + elif target == 'in': + print "Flushing input history" + pc = self.shell.displayhook.prompt_count + 1 + for n in range(1, pc): + key = '_i'+repr(n) + user_ns.pop(key,None) + user_ns.update(dict(_i=u'',_ii=u'',_iii=u'')) + hm = ip.history_manager + # don't delete these, as %save and %macro depending on the + # length of these lists to be preserved + hm.input_hist_parsed[:] = [''] * pc + hm.input_hist_raw[:] = [''] * pc + # hm has internal machinery for _i,_ii,_iii, clear it out + hm._i = hm._ii = hm._iii = hm._i00 = u'' + + elif target == 'array': + # Support cleaning up numpy arrays + try: + from numpy import ndarray + # This must be done with items and not iteritems because + # we're going to modify the dict in-place. + for x,val in user_ns.items(): + if isinstance(val,ndarray): + del user_ns[x] + except ImportError: + print "reset array only works if Numpy is available." + + elif target == 'dhist': + print "Flushing directory history" + del user_ns['_dh'][:] + + else: + print "Don't know how to reset ", + print target + ", please run `%reset?` for details" + + gc.collect() + + @line_magic + def reset_selective(self, parameter_s=''): + """Resets the namespace by removing names defined by the user. + + Input/Output history are left around in case you need them. + + %reset_selective [-f] regex + + No action is taken if regex is not included + + Options + -f : force reset without asking for confirmation. + + See Also + -------- + magic_reset : invoked as ``%reset`` + + Examples + -------- + + We first fully reset the namespace so your output looks identical to + this example for pedagogical reasons; in practice you do not need a + full reset:: + + In [1]: %reset -f + + Now, with a clean namespace we can make a few variables and use + ``%reset_selective`` to only delete names that match our regexp:: + + In [2]: a=1; b=2; c=3; b1m=4; b2m=5; b3m=6; b4m=7; b2s=8 + + In [3]: who_ls + Out[3]: ['a', 'b', 'b1m', 'b2m', 'b2s', 'b3m', 'b4m', 'c'] + + In [4]: %reset_selective -f b[2-3]m + + In [5]: who_ls + Out[5]: ['a', 'b', 'b1m', 'b2s', 'b4m', 'c'] + + In [6]: %reset_selective -f d + + In [7]: who_ls + Out[7]: ['a', 'b', 'b1m', 'b2s', 'b4m', 'c'] + + In [8]: %reset_selective -f c + + In [9]: who_ls + Out[9]: ['a', 'b', 'b1m', 'b2s', 'b4m'] + + In [10]: %reset_selective -f b + + In [11]: who_ls + Out[11]: ['a'] + + Notes + ----- + Calling this magic from clients that do not implement standard input, + such as the ipython notebook interface, will reset the namespace + without confirmation. + """ + + opts, regex = self.parse_options(parameter_s,'f') + + if opts.has_key('f'): + ans = True + else: + try: + ans = self.shell.ask_yes_no( + "Once deleted, variables cannot be recovered. Proceed (y/[n])? ", + default='n') + except StdinNotImplementedError: + ans = True + if not ans: + print 'Nothing done.' + return + user_ns = self.shell.user_ns + if not regex: + print 'No regex pattern specified. Nothing done.' + return + else: + try: + m = re.compile(regex) + except TypeError: + raise TypeError('regex must be a string or compiled pattern') + for i in self.who_ls(): + if m.search(i): + del(user_ns[i]) + + @line_magic + def xdel(self, parameter_s=''): + """Delete a variable, trying to clear it from anywhere that + IPython's machinery has references to it. By default, this uses + the identity of the named object in the user namespace to remove + references held under other names. The object is also removed + from the output history. + + Options + -n : Delete the specified name from all namespaces, without + checking their identity. + """ + opts, varname = self.parse_options(parameter_s,'n') + try: + self.shell.del_var(varname, ('n' in opts)) + except (NameError, ValueError) as e: + print type(e).__name__ +": "+ str(e) From 2d703c47041d426fc2815fc7a5761c8b93b867dc Mon Sep 17 00:00:00 2001 From: Fernando Perez Date: Wed, 23 May 2012 14:38:03 -0700 Subject: [PATCH 058/103] Create core.magics.execution according to new API. --- IPython/core/interactiveshell.py | 8 +- IPython/core/magic_functions.py | 905 ----------------------------- IPython/core/magics/__init__.py | 1 + IPython/core/magics/execution.py | 955 +++++++++++++++++++++++++++++++ 4 files changed, 960 insertions(+), 909 deletions(-) create mode 100644 IPython/core/magics/execution.py diff --git a/IPython/core/interactiveshell.py b/IPython/core/interactiveshell.py index 5b6c7f2911a..d9bc9f6932a 100644 --- a/IPython/core/interactiveshell.py +++ b/IPython/core/interactiveshell.py @@ -2005,10 +2005,10 @@ def init_magics(self): self.register_magic_function = self.magics_manager.register_function self.define_magic = self.magics_manager.define_magic - self.register_magics(m.BasicMagics, m.CodeMagics, m.ConfigMagics, - mf.ExecutionMagics, m.NamespaceMagics, mf.AutoMagics, - mf.OSMagics, mf.LoggingMagics, mf.ExtensionsMagics, - mf.PylabMagics, m.HistoryMagics, mf.DeprecatedMagics) + self.register_magics(mf.AutoMagics, m.BasicMagics, m.CodeMagics, + m.ConfigMagics, mf.DeprecatedMagics, m.ExecutionMagics, + mf.ExtensionsMagics, m.HistoryMagics, mf.LoggingMagics, + m.NamespaceMagics, mf.OSMagics, mf.PylabMagics ) # FIXME: Move the color initialization to the DisplayHook, which # should be split into a prompt manager and displayhook. We probably diff --git a/IPython/core/magic_functions.py b/IPython/core/magic_functions.py index f57b2789bf1..8ba6ab42cf9 100644 --- a/IPython/core/magic_functions.py +++ b/IPython/core/magic_functions.py @@ -66,911 +66,6 @@ # Magic implementation classes #----------------------------------------------------------------------------- -@register_magics -class ExecutionMagics(Magics): - """Magics related to code execution, debugging, profiling, etc. - - """ - - def __init__(self, shell): - super(ExecutionMagics, self).__init__(shell) - if profile is None: - self.prun = self.profile_missing_notice - # Default execution function used to actually run user code. - self.default_runner = None - - def profile_missing_notice(self, *args, **kwargs): - error("""\ -The profile module could not be found. It has been removed from the standard -python packages because of its non-free license. To use profiling, install the -python-profiler package from non-free.""") - - @skip_doctest - @line_magic - def prun(self, parameter_s='',user_mode=1, - opts=None,arg_lst=None,prog_ns=None): - - """Run a statement through the python code profiler. - - Usage: - %prun [options] statement - - The given statement (which doesn't require quote marks) is run via the - python profiler in a manner similar to the profile.run() function. - Namespaces are internally managed to work correctly; profile.run - cannot be used in IPython because it makes certain assumptions about - namespaces which do not hold under IPython. - - Options: - - -l : you can place restrictions on what or how much of the - profile gets printed. The limit value can be: - - * A string: only information for function names containing this string - is printed. - - * An integer: only these many lines are printed. - - * A float (between 0 and 1): this fraction of the report is printed - (for example, use a limit of 0.4 to see the topmost 40% only). - - You can combine several limits with repeated use of the option. For - example, '-l __init__ -l 5' will print only the topmost 5 lines of - information about class constructors. - - -r: return the pstats.Stats object generated by the profiling. This - object has all the information about the profile in it, and you can - later use it for further analysis or in other functions. - - -s : sort profile by given key. You can provide more than one key - by using the option several times: '-s key1 -s key2 -s key3...'. The - default sorting key is 'time'. - - The following is copied verbatim from the profile documentation - referenced below: - - When more than one key is provided, additional keys are used as - secondary criteria when the there is equality in all keys selected - before them. - - Abbreviations can be used for any key names, as long as the - abbreviation is unambiguous. The following are the keys currently - defined: - - Valid Arg Meaning - "calls" call count - "cumulative" cumulative time - "file" file name - "module" file name - "pcalls" primitive call count - "line" line number - "name" function name - "nfl" name/file/line - "stdname" standard name - "time" internal time - - Note that all sorts on statistics are in descending order (placing - most time consuming items first), where as name, file, and line number - searches are in ascending order (i.e., alphabetical). The subtle - distinction between "nfl" and "stdname" is that the standard name is a - sort of the name as printed, which means that the embedded line - numbers get compared in an odd way. For example, lines 3, 20, and 40 - would (if the file names were the same) appear in the string order - "20" "3" and "40". In contrast, "nfl" does a numeric compare of the - line numbers. In fact, sort_stats("nfl") is the same as - sort_stats("name", "file", "line"). - - -T : save profile results as shown on screen to a text - file. The profile is still shown on screen. - - -D : save (via dump_stats) profile statistics to given - filename. This data is in a format understood by the pstats module, and - is generated by a call to the dump_stats() method of profile - objects. The profile is still shown on screen. - - -q: suppress output to the pager. Best used with -T and/or -D above. - - If you want to run complete programs under the profiler's control, use - '%run -p [prof_opts] filename.py [args to program]' where prof_opts - contains profiler specific options as described here. - - You can read the complete documentation for the profile module with:: - - In [1]: import profile; profile.help() - """ - - opts_def = Struct(D=[''],l=[],s=['time'],T=['']) - - if user_mode: # regular user call - opts,arg_str = self.parse_options(parameter_s,'D:l:rs:T:q', - list_all=1, posix=False) - namespace = self.shell.user_ns - else: # called to run a program by %run -p - try: - filename = get_py_filename(arg_lst[0]) - except IOError as e: - try: - msg = str(e) - except UnicodeError: - msg = e.message - error(msg) - return - - arg_str = 'execfile(filename,prog_ns)' - namespace = { - 'execfile': self.shell.safe_execfile, - 'prog_ns': prog_ns, - 'filename': filename - } - - opts.merge(opts_def) - - prof = profile.Profile() - try: - prof = prof.runctx(arg_str,namespace,namespace) - sys_exit = '' - except SystemExit: - sys_exit = """*** SystemExit exception caught in code being profiled.""" - - stats = pstats.Stats(prof).strip_dirs().sort_stats(*opts.s) - - lims = opts.l - if lims: - lims = [] # rebuild lims with ints/floats/strings - for lim in opts.l: - try: - lims.append(int(lim)) - except ValueError: - try: - lims.append(float(lim)) - except ValueError: - lims.append(lim) - - # Trap output. - stdout_trap = StringIO() - - if hasattr(stats,'stream'): - # In newer versions of python, the stats object has a 'stream' - # attribute to write into. - stats.stream = stdout_trap - stats.print_stats(*lims) - else: - # For older versions, we manually redirect stdout during printing - sys_stdout = sys.stdout - try: - sys.stdout = stdout_trap - stats.print_stats(*lims) - finally: - sys.stdout = sys_stdout - - output = stdout_trap.getvalue() - output = output.rstrip() - - if 'q' not in opts: - page.page(output) - print sys_exit, - - dump_file = opts.D[0] - text_file = opts.T[0] - if dump_file: - dump_file = unquote_filename(dump_file) - prof.dump_stats(dump_file) - print '\n*** Profile stats marshalled to file',\ - `dump_file`+'.',sys_exit - if text_file: - text_file = unquote_filename(text_file) - pfile = open(text_file,'w') - pfile.write(output) - pfile.close() - print '\n*** Profile printout saved to text file',\ - `text_file`+'.',sys_exit - - if opts.has_key('r'): - return stats - else: - return None - - @line_magic - def pdb(self, parameter_s=''): - """Control the automatic calling of the pdb interactive debugger. - - Call as '%pdb on', '%pdb 1', '%pdb off' or '%pdb 0'. If called without - argument it works as a toggle. - - When an exception is triggered, IPython can optionally call the - interactive pdb debugger after the traceback printout. %pdb toggles - this feature on and off. - - The initial state of this feature is set in your configuration - file (the option is ``InteractiveShell.pdb``). - - If you want to just activate the debugger AFTER an exception has fired, - without having to type '%pdb on' and rerunning your code, you can use - the %debug magic.""" - - par = parameter_s.strip().lower() - - if par: - try: - new_pdb = {'off':0,'0':0,'on':1,'1':1}[par] - except KeyError: - print ('Incorrect argument. Use on/1, off/0, ' - 'or nothing for a toggle.') - return - else: - # toggle - new_pdb = not self.shell.call_pdb - - # set on the shell - self.shell.call_pdb = new_pdb - print 'Automatic pdb calling has been turned',on_off(new_pdb) - - @line_magic - def debug(self, parameter_s=''): - """Activate the interactive debugger in post-mortem mode. - - If an exception has just occurred, this lets you inspect its stack - frames interactively. Note that this will always work only on the last - traceback that occurred, so you must call this quickly after an - exception that you wish to inspect has fired, because if another one - occurs, it clobbers the previous one. - - If you want IPython to automatically do this on every exception, see - the %pdb magic for more details. - """ - self.shell.debugger(force=True) - - @line_magic - def tb(self, s): - """Print the last traceback with the currently active exception mode. - - See %xmode for changing exception reporting modes.""" - self.shell.showtraceback() - - @skip_doctest - @line_magic - def run(self, parameter_s='', runner=None, - file_finder=get_py_filename): - """Run the named file inside IPython as a program. - - Usage:\\ - %run [-n -i -t [-N] -d [-b] -p [profile options]] file [args] - - Parameters after the filename are passed as command-line arguments to - the program (put in sys.argv). Then, control returns to IPython's - prompt. - - This is similar to running at a system prompt:\\ - $ python file args\\ - but with the advantage of giving you IPython's tracebacks, and of - loading all variables into your interactive namespace for further use - (unless -p is used, see below). - - The file is executed in a namespace initially consisting only of - __name__=='__main__' and sys.argv constructed as indicated. It thus - sees its environment as if it were being run as a stand-alone program - (except for sharing global objects such as previously imported - modules). But after execution, the IPython interactive namespace gets - updated with all variables defined in the program (except for __name__ - and sys.argv). This allows for very convenient loading of code for - interactive work, while giving each program a 'clean sheet' to run in. - - Options: - - -n: __name__ is NOT set to '__main__', but to the running file's name - without extension (as python does under import). This allows running - scripts and reloading the definitions in them without calling code - protected by an ' if __name__ == "__main__" ' clause. - - -i: run the file in IPython's namespace instead of an empty one. This - is useful if you are experimenting with code written in a text editor - which depends on variables defined interactively. - - -e: ignore sys.exit() calls or SystemExit exceptions in the script - being run. This is particularly useful if IPython is being used to - run unittests, which always exit with a sys.exit() call. In such - cases you are interested in the output of the test results, not in - seeing a traceback of the unittest module. - - -t: print timing information at the end of the run. IPython will give - you an estimated CPU time consumption for your script, which under - Unix uses the resource module to avoid the wraparound problems of - time.clock(). Under Unix, an estimate of time spent on system tasks - is also given (for Windows platforms this is reported as 0.0). - - If -t is given, an additional -N option can be given, where - must be an integer indicating how many times you want the script to - run. The final timing report will include total and per run results. - - For example (testing the script uniq_stable.py):: - - In [1]: run -t uniq_stable - - IPython CPU timings (estimated):\\ - User : 0.19597 s.\\ - System: 0.0 s.\\ - - In [2]: run -t -N5 uniq_stable - - IPython CPU timings (estimated):\\ - Total runs performed: 5\\ - Times : Total Per run\\ - User : 0.910862 s, 0.1821724 s.\\ - System: 0.0 s, 0.0 s. - - -d: run your program under the control of pdb, the Python debugger. - This allows you to execute your program step by step, watch variables, - etc. Internally, what IPython does is similar to calling: - - pdb.run('execfile("YOURFILENAME")') - - with a breakpoint set on line 1 of your file. You can change the line - number for this automatic breakpoint to be by using the -bN option - (where N must be an integer). For example:: - - %run -d -b40 myscript - - will set the first breakpoint at line 40 in myscript.py. Note that - the first breakpoint must be set on a line which actually does - something (not a comment or docstring) for it to stop execution. - - When the pdb debugger starts, you will see a (Pdb) prompt. You must - first enter 'c' (without quotes) to start execution up to the first - breakpoint. - - Entering 'help' gives information about the use of the debugger. You - can easily see pdb's full documentation with "import pdb;pdb.help()" - at a prompt. - - -p: run program under the control of the Python profiler module (which - prints a detailed report of execution times, function calls, etc). - - You can pass other options after -p which affect the behavior of the - profiler itself. See the docs for %prun for details. - - In this mode, the program's variables do NOT propagate back to the - IPython interactive namespace (because they remain in the namespace - where the profiler executes them). - - Internally this triggers a call to %prun, see its documentation for - details on the options available specifically for profiling. - - There is one special usage for which the text above doesn't apply: - if the filename ends with .ipy, the file is run as ipython script, - just as if the commands were written on IPython prompt. - - -m: specify module name to load instead of script path. Similar to - the -m option for the python interpreter. Use this option last if you - want to combine with other %run options. Unlike the python interpreter - only source modules are allowed no .pyc or .pyo files. - For example:: - - %run -m example - - will run the example module. - - """ - - # get arguments and set sys.argv for program to be run. - opts, arg_lst = self.parse_options(parameter_s, 'nidtN:b:pD:l:rs:T:em:', - mode='list', list_all=1) - if "m" in opts: - modulename = opts["m"][0] - modpath = find_mod(modulename) - if modpath is None: - warn('%r is not a valid modulename on sys.path'%modulename) - return - arg_lst = [modpath] + arg_lst - try: - filename = file_finder(arg_lst[0]) - except IndexError: - warn('you must provide at least a filename.') - print '\n%run:\n', oinspect.getdoc(self.run) - return - except IOError as e: - try: - msg = str(e) - except UnicodeError: - msg = e.message - error(msg) - return - - if filename.lower().endswith('.ipy'): - self.shell.safe_execfile_ipy(filename) - return - - # Control the response to exit() calls made by the script being run - exit_ignore = 'e' in opts - - # Make sure that the running script gets a proper sys.argv as if it - # were run from a system shell. - save_argv = sys.argv # save it for later restoring - - # simulate shell expansion on arguments, at least tilde expansion - args = [ os.path.expanduser(a) for a in arg_lst[1:] ] - - sys.argv = [filename] + args # put in the proper filename - # protect sys.argv from potential unicode strings on Python 2: - if not py3compat.PY3: - sys.argv = [ py3compat.cast_bytes(a) for a in sys.argv ] - - if 'i' in opts: - # Run in user's interactive namespace - prog_ns = self.shell.user_ns - __name__save = self.shell.user_ns['__name__'] - prog_ns['__name__'] = '__main__' - main_mod = self.shell.new_main_mod(prog_ns) - else: - # Run in a fresh, empty namespace - if 'n' in opts: - name = os.path.splitext(os.path.basename(filename))[0] - else: - name = '__main__' - - main_mod = self.shell.new_main_mod() - prog_ns = main_mod.__dict__ - prog_ns['__name__'] = name - - # Since '%run foo' emulates 'python foo.py' at the cmd line, we must - # set the __file__ global in the script's namespace - prog_ns['__file__'] = filename - - # pickle fix. See interactiveshell for an explanation. But we need to - # make sure that, if we overwrite __main__, we replace it at the end - main_mod_name = prog_ns['__name__'] - - if main_mod_name == '__main__': - restore_main = sys.modules['__main__'] - else: - restore_main = False - - # This needs to be undone at the end to prevent holding references to - # every single object ever created. - sys.modules[main_mod_name] = main_mod - - try: - stats = None - with self.shell.readline_no_record: - if 'p' in opts: - stats = self.prun('', 0, opts, arg_lst, prog_ns) - else: - if 'd' in opts: - deb = debugger.Pdb(self.shell.colors) - # reset Breakpoint state, which is moronically kept - # in a class - bdb.Breakpoint.next = 1 - bdb.Breakpoint.bplist = {} - bdb.Breakpoint.bpbynumber = [None] - # Set an initial breakpoint to stop execution - maxtries = 10 - bp = int(opts.get('b', [1])[0]) - checkline = deb.checkline(filename, bp) - if not checkline: - for bp in range(bp + 1, bp + maxtries + 1): - if deb.checkline(filename, bp): - break - else: - msg = ("\nI failed to find a valid line to set " - "a breakpoint\n" - "after trying up to line: %s.\n" - "Please set a valid breakpoint manually " - "with the -b option." % bp) - error(msg) - return - # if we find a good linenumber, set the breakpoint - deb.do_break('%s:%s' % (filename, bp)) - # Start file run - print "NOTE: Enter 'c' at the", - print "%s prompt to start your script." % deb.prompt - ns = {'execfile': py3compat.execfile, 'prog_ns': prog_ns} - try: - deb.run('execfile("%s", prog_ns)' % filename, ns) - - except: - etype, value, tb = sys.exc_info() - # Skip three frames in the traceback: the %run one, - # one inside bdb.py, and the command-line typed by the - # user (run by exec in pdb itself). - self.shell.InteractiveTB(etype, value, tb, tb_offset=3) - else: - if runner is None: - runner = self.default_runner - if runner is None: - runner = self.shell.safe_execfile - if 't' in opts: - # timed execution - try: - nruns = int(opts['N'][0]) - if nruns < 1: - error('Number of runs must be >=1') - return - except (KeyError): - nruns = 1 - twall0 = time.time() - if nruns == 1: - t0 = clock2() - runner(filename, prog_ns, prog_ns, - exit_ignore=exit_ignore) - t1 = clock2() - t_usr = t1[0] - t0[0] - t_sys = t1[1] - t0[1] - print "\nIPython CPU timings (estimated):" - print " User : %10.2f s." % t_usr - print " System : %10.2f s." % t_sys - else: - runs = range(nruns) - t0 = clock2() - for nr in runs: - runner(filename, prog_ns, prog_ns, - exit_ignore=exit_ignore) - t1 = clock2() - t_usr = t1[0] - t0[0] - t_sys = t1[1] - t0[1] - print "\nIPython CPU timings (estimated):" - print "Total runs performed:", nruns - print " Times : %10.2f %10.2f" % ('Total', 'Per run') - print " User : %10.2f s, %10.2f s." % (t_usr, t_usr / nruns) - print " System : %10.2f s, %10.2f s." % (t_sys, t_sys / nruns) - twall1 = time.time() - print "Wall time: %10.2f s." % (twall1 - twall0) - - else: - # regular execution - runner(filename, prog_ns, prog_ns, exit_ignore=exit_ignore) - - if 'i' in opts: - self.shell.user_ns['__name__'] = __name__save - else: - # The shell MUST hold a reference to prog_ns so after %run - # exits, the python deletion mechanism doesn't zero it out - # (leaving dangling references). - self.shell.cache_main_mod(prog_ns, filename) - # update IPython interactive namespace - - # Some forms of read errors on the file may mean the - # __name__ key was never set; using pop we don't have to - # worry about a possible KeyError. - prog_ns.pop('__name__', None) - - self.shell.user_ns.update(prog_ns) - finally: - # It's a bit of a mystery why, but __builtins__ can change from - # being a module to becoming a dict missing some key data after - # %run. As best I can see, this is NOT something IPython is doing - # at all, and similar problems have been reported before: - # http://coding.derkeiler.com/Archive/Python/comp.lang.python/2004-10/0188.html - # Since this seems to be done by the interpreter itself, the best - # we can do is to at least restore __builtins__ for the user on - # exit. - self.shell.user_ns['__builtins__'] = builtin_mod - - # Ensure key global structures are restored - sys.argv = save_argv - if restore_main: - sys.modules['__main__'] = restore_main - else: - # Remove from sys.modules the reference to main_mod we'd - # added. Otherwise it will trap references to objects - # contained therein. - del sys.modules[main_mod_name] - - return stats - - @skip_doctest - @line_magic - def timeit(self, parameter_s=''): - """Time execution of a Python statement or expression - - Usage:\\ - %timeit [-n -r [-t|-c]] statement - - Time execution of a Python statement or expression using the timeit - module. - - Options: - -n: execute the given statement times in a loop. If this value - is not given, a fitting value is chosen. - - -r: repeat the loop iteration times and take the best result. - Default: 3 - - -t: use time.time to measure the time, which is the default on Unix. - This function measures wall time. - - -c: use time.clock to measure the time, which is the default on - Windows and measures wall time. On Unix, resource.getrusage is used - instead and returns the CPU user time. - - -p

: use a precision of

digits to display the timing result. - Default: 3 - - - Examples - -------- - :: - - In [1]: %timeit pass - 10000000 loops, best of 3: 53.3 ns per loop - - In [2]: u = None - - In [3]: %timeit u is None - 10000000 loops, best of 3: 184 ns per loop - - In [4]: %timeit -r 4 u == None - 1000000 loops, best of 4: 242 ns per loop - - In [5]: import time - - In [6]: %timeit -n1 time.sleep(2) - 1 loops, best of 3: 2 s per loop - - - The times reported by %timeit will be slightly higher than those - reported by the timeit.py script when variables are accessed. This is - due to the fact that %timeit executes the statement in the namespace - of the shell, compared with timeit.py, which uses a single setup - statement to import function or create variables. Generally, the bias - does not matter as long as results from timeit.py are not mixed with - those from %timeit.""" - - import timeit - import math - - # XXX: Unfortunately the unicode 'micro' symbol can cause problems in - # certain terminals. Until we figure out a robust way of - # auto-detecting if the terminal can deal with it, use plain 'us' for - # microseconds. I am really NOT happy about disabling the proper - # 'micro' prefix, but crashing is worse... If anyone knows what the - # right solution for this is, I'm all ears... - # - # Note: using - # - # s = u'\xb5' - # s.encode(sys.getdefaultencoding()) - # - # is not sufficient, as I've seen terminals where that fails but - # print s - # - # succeeds - # - # See bug: https://bugs.launchpad.net/ipython/+bug/348466 - - #units = [u"s", u"ms",u'\xb5',"ns"] - units = [u"s", u"ms",u'us',"ns"] - - scaling = [1, 1e3, 1e6, 1e9] - - opts, stmt = self.parse_options(parameter_s,'n:r:tcp:', - posix=False, strict=False) - if stmt == "": - return - timefunc = timeit.default_timer - number = int(getattr(opts, "n", 0)) - repeat = int(getattr(opts, "r", timeit.default_repeat)) - precision = int(getattr(opts, "p", 3)) - if hasattr(opts, "t"): - timefunc = time.time - if hasattr(opts, "c"): - timefunc = clock - - timer = timeit.Timer(timer=timefunc) - # this code has tight coupling to the inner workings of timeit.Timer, - # but is there a better way to achieve that the code stmt has access - # to the shell namespace? - - src = timeit.template % {'stmt': timeit.reindent(stmt, 8), - 'setup': "pass"} - # Track compilation time so it can be reported if too long - # Minimum time above which compilation time will be reported - tc_min = 0.1 - - t0 = clock() - code = compile(src, "", "exec") - tc = clock()-t0 - - ns = {} - exec code in self.shell.user_ns, ns - timer.inner = ns["inner"] - - if number == 0: - # determine number so that 0.2 <= total time < 2.0 - number = 1 - for i in range(1, 10): - if timer.timeit(number) >= 0.2: - break - number *= 10 - - best = min(timer.repeat(repeat, number)) / number - - if best > 0.0 and best < 1000.0: - order = min(-int(math.floor(math.log10(best)) // 3), 3) - elif best >= 1000.0: - order = 0 - else: - order = 3 - print u"%d loops, best of %d: %.*g %s per loop" % (number, repeat, - precision, - best * scaling[order], - units[order]) - if tc > tc_min: - print "Compiler time: %.2f s" % tc - - @skip_doctest - @needs_local_scope - @line_magic - def time(self,parameter_s, user_locals): - """Time execution of a Python statement or expression. - - The CPU and wall clock times are printed, and the value of the - expression (if any) is returned. Note that under Win32, system time - is always reported as 0, since it can not be measured. - - This function provides very basic timing functionality. In Python - 2.3, the timeit module offers more control and sophistication, so this - could be rewritten to use it (patches welcome). - - Examples - -------- - :: - - In [1]: time 2**128 - CPU times: user 0.00 s, sys: 0.00 s, total: 0.00 s - Wall time: 0.00 - Out[1]: 340282366920938463463374607431768211456L - - In [2]: n = 1000000 - - In [3]: time sum(range(n)) - CPU times: user 1.20 s, sys: 0.05 s, total: 1.25 s - Wall time: 1.37 - Out[3]: 499999500000L - - In [4]: time print 'hello world' - hello world - CPU times: user 0.00 s, sys: 0.00 s, total: 0.00 s - Wall time: 0.00 - - Note that the time needed by Python to compile the given expression - will be reported if it is more than 0.1s. In this example, the - actual exponentiation is done by Python at compilation time, so while - the expression can take a noticeable amount of time to compute, that - time is purely due to the compilation: - - In [5]: time 3**9999; - CPU times: user 0.00 s, sys: 0.00 s, total: 0.00 s - Wall time: 0.00 s - - In [6]: time 3**999999; - CPU times: user 0.00 s, sys: 0.00 s, total: 0.00 s - Wall time: 0.00 s - Compiler : 0.78 s - """ - - # fail immediately if the given expression can't be compiled - - expr = self.shell.prefilter(parameter_s,False) - - # Minimum time above which compilation time will be reported - tc_min = 0.1 - - try: - mode = 'eval' - t0 = clock() - code = compile(expr,'',mode) - tc = clock()-t0 - except SyntaxError: - mode = 'exec' - t0 = clock() - code = compile(expr,'',mode) - tc = clock()-t0 - # skew measurement as little as possible - glob = self.shell.user_ns - wtime = time.time - # time execution - wall_st = wtime() - if mode=='eval': - st = clock2() - out = eval(code, glob, user_locals) - end = clock2() - else: - st = clock2() - exec code in glob, user_locals - end = clock2() - out = None - wall_end = wtime() - # Compute actual times and report - wall_time = wall_end-wall_st - cpu_user = end[0]-st[0] - cpu_sys = end[1]-st[1] - cpu_tot = cpu_user+cpu_sys - print "CPU times: user %.2f s, sys: %.2f s, total: %.2f s" % \ - (cpu_user,cpu_sys,cpu_tot) - print "Wall time: %.2f s" % wall_time - if tc > tc_min: - print "Compiler : %.2f s" % tc - return out - - @skip_doctest - @line_magic - def macro(self, parameter_s=''): - """Define a macro for future re-execution. It accepts ranges of history, - filenames or string objects. - - Usage:\\ - %macro [options] name n1-n2 n3-n4 ... n5 .. n6 ... - - Options: - - -r: use 'raw' input. By default, the 'processed' history is used, - so that magics are loaded in their transformed version to valid - Python. If this option is given, the raw input as typed as the - command line is used instead. - - This will define a global variable called `name` which is a string - made of joining the slices and lines you specify (n1,n2,... numbers - above) from your input history into a single string. This variable - acts like an automatic function which re-executes those lines as if - you had typed them. You just type 'name' at the prompt and the code - executes. - - The syntax for indicating input ranges is described in %history. - - Note: as a 'hidden' feature, you can also use traditional python slice - notation, where N:M means numbers N through M-1. - - For example, if your history contains (%hist prints it):: - - 44: x=1 - 45: y=3 - 46: z=x+y - 47: print x - 48: a=5 - 49: print 'x',x,'y',y - - you can create a macro with lines 44 through 47 (included) and line 49 - called my_macro with:: - - In [55]: %macro my_macro 44-47 49 - - Now, typing `my_macro` (without quotes) will re-execute all this code - in one pass. - - You don't need to give the line-numbers in order, and any given line - number can appear multiple times. You can assemble macros with any - lines from your input history in any order. - - The macro is a simple object which holds its value in an attribute, - but IPython's display system checks for macros and executes them as - code instead of printing them when you type their name. - - You can view a macro's contents by explicitly printing it with:: - - print macro_name - - """ - opts,args = self.parse_options(parameter_s,'r',mode='list') - if not args: # List existing macros - return sorted(k for k,v in self.shell.user_ns.iteritems() if\ - isinstance(v, Macro)) - if len(args) == 1: - raise UsageError( - "%macro insufficient args; usage '%macro name n1-n2 n3-4...") - name, codefrom = args[0], " ".join(args[1:]) - - #print 'rng',ranges # dbg - try: - lines = self.shell.find_user_code(codefrom, 'r' in opts) - except (ValueError, TypeError) as e: - print e.args[0] - return - macro = Macro(lines) - self.shell.define_macro(name, macro) - print 'Macro `%s` created. To execute, type its name (without quotes).' % name - print '=== Macro contents: ===' - print macro, - - @register_magics class AutoMagics(Magics): """Magics that control various autoX behaviors.""" diff --git a/IPython/core/magics/__init__.py b/IPython/core/magics/__init__.py index 3ba9c33567c..34753505035 100644 --- a/IPython/core/magics/__init__.py +++ b/IPython/core/magics/__init__.py @@ -16,6 +16,7 @@ from .basic import BasicMagics from .code import CodeMagics, MacroToEdit from .config import ConfigMagics +from .execution import ExecutionMagics from .history import HistoryMagics from .namespace import NamespaceMagics diff --git a/IPython/core/magics/execution.py b/IPython/core/magics/execution.py new file mode 100644 index 00000000000..30a1a8f3afc --- /dev/null +++ b/IPython/core/magics/execution.py @@ -0,0 +1,955 @@ +"""Implementation of execution-related magic functions. +""" +#----------------------------------------------------------------------------- +# Copyright (c) 2012 The IPython Development Team. +# +# Distributed under the terms of the Modified BSD License. +# +# The full license is in the file COPYING.txt, distributed with this software. +#----------------------------------------------------------------------------- + +#----------------------------------------------------------------------------- +# Imports +#----------------------------------------------------------------------------- + +# Stdlib +import __builtin__ as builtin_mod +import bdb +import os +import sys +import time +from StringIO import StringIO + +# cProfile was added in Python2.5 +try: + import cProfile as profile + import pstats +except ImportError: + # profile isn't bundled by default in Debian for license reasons + try: + import profile, pstats + except ImportError: + profile = pstats = None + +# Our own packages +from IPython.core import debugger, oinspect +from IPython.core import page +from IPython.core.error import UsageError +from IPython.core.macro import Macro +from IPython.core.magic import (Magics, register_magics, line_magic, + on_off, needs_local_scope) +from IPython.testing.skipdoctest import skip_doctest +from IPython.utils import py3compat +from IPython.utils.ipstruct import Struct +from IPython.utils.module_paths import find_mod +from IPython.utils.path import get_py_filename, unquote_filename +from IPython.utils.timing import clock, clock2 +from IPython.utils.warn import warn, error + +#----------------------------------------------------------------------------- +# Magic implementation classes +#----------------------------------------------------------------------------- + +@register_magics +class ExecutionMagics(Magics): + """Magics related to code execution, debugging, profiling, etc. + + """ + + def __init__(self, shell): + super(ExecutionMagics, self).__init__(shell) + if profile is None: + self.prun = self.profile_missing_notice + # Default execution function used to actually run user code. + self.default_runner = None + + def profile_missing_notice(self, *args, **kwargs): + error("""\ +The profile module could not be found. It has been removed from the standard +python packages because of its non-free license. To use profiling, install the +python-profiler package from non-free.""") + + @skip_doctest + @line_magic + def prun(self, parameter_s='',user_mode=1, + opts=None,arg_lst=None,prog_ns=None): + + """Run a statement through the python code profiler. + + Usage: + %prun [options] statement + + The given statement (which doesn't require quote marks) is run via the + python profiler in a manner similar to the profile.run() function. + Namespaces are internally managed to work correctly; profile.run + cannot be used in IPython because it makes certain assumptions about + namespaces which do not hold under IPython. + + Options: + + -l : you can place restrictions on what or how much of the + profile gets printed. The limit value can be: + + * A string: only information for function names containing this string + is printed. + + * An integer: only these many lines are printed. + + * A float (between 0 and 1): this fraction of the report is printed + (for example, use a limit of 0.4 to see the topmost 40% only). + + You can combine several limits with repeated use of the option. For + example, '-l __init__ -l 5' will print only the topmost 5 lines of + information about class constructors. + + -r: return the pstats.Stats object generated by the profiling. This + object has all the information about the profile in it, and you can + later use it for further analysis or in other functions. + + -s : sort profile by given key. You can provide more than one key + by using the option several times: '-s key1 -s key2 -s key3...'. The + default sorting key is 'time'. + + The following is copied verbatim from the profile documentation + referenced below: + + When more than one key is provided, additional keys are used as + secondary criteria when the there is equality in all keys selected + before them. + + Abbreviations can be used for any key names, as long as the + abbreviation is unambiguous. The following are the keys currently + defined: + + Valid Arg Meaning + "calls" call count + "cumulative" cumulative time + "file" file name + "module" file name + "pcalls" primitive call count + "line" line number + "name" function name + "nfl" name/file/line + "stdname" standard name + "time" internal time + + Note that all sorts on statistics are in descending order (placing + most time consuming items first), where as name, file, and line number + searches are in ascending order (i.e., alphabetical). The subtle + distinction between "nfl" and "stdname" is that the standard name is a + sort of the name as printed, which means that the embedded line + numbers get compared in an odd way. For example, lines 3, 20, and 40 + would (if the file names were the same) appear in the string order + "20" "3" and "40". In contrast, "nfl" does a numeric compare of the + line numbers. In fact, sort_stats("nfl") is the same as + sort_stats("name", "file", "line"). + + -T : save profile results as shown on screen to a text + file. The profile is still shown on screen. + + -D : save (via dump_stats) profile statistics to given + filename. This data is in a format understood by the pstats module, and + is generated by a call to the dump_stats() method of profile + objects. The profile is still shown on screen. + + -q: suppress output to the pager. Best used with -T and/or -D above. + + If you want to run complete programs under the profiler's control, use + '%run -p [prof_opts] filename.py [args to program]' where prof_opts + contains profiler specific options as described here. + + You can read the complete documentation for the profile module with:: + + In [1]: import profile; profile.help() + """ + + opts_def = Struct(D=[''],l=[],s=['time'],T=['']) + + if user_mode: # regular user call + opts,arg_str = self.parse_options(parameter_s,'D:l:rs:T:q', + list_all=1, posix=False) + namespace = self.shell.user_ns + else: # called to run a program by %run -p + try: + filename = get_py_filename(arg_lst[0]) + except IOError as e: + try: + msg = str(e) + except UnicodeError: + msg = e.message + error(msg) + return + + arg_str = 'execfile(filename,prog_ns)' + namespace = { + 'execfile': self.shell.safe_execfile, + 'prog_ns': prog_ns, + 'filename': filename + } + + opts.merge(opts_def) + + prof = profile.Profile() + try: + prof = prof.runctx(arg_str,namespace,namespace) + sys_exit = '' + except SystemExit: + sys_exit = """*** SystemExit exception caught in code being profiled.""" + + stats = pstats.Stats(prof).strip_dirs().sort_stats(*opts.s) + + lims = opts.l + if lims: + lims = [] # rebuild lims with ints/floats/strings + for lim in opts.l: + try: + lims.append(int(lim)) + except ValueError: + try: + lims.append(float(lim)) + except ValueError: + lims.append(lim) + + # Trap output. + stdout_trap = StringIO() + + if hasattr(stats,'stream'): + # In newer versions of python, the stats object has a 'stream' + # attribute to write into. + stats.stream = stdout_trap + stats.print_stats(*lims) + else: + # For older versions, we manually redirect stdout during printing + sys_stdout = sys.stdout + try: + sys.stdout = stdout_trap + stats.print_stats(*lims) + finally: + sys.stdout = sys_stdout + + output = stdout_trap.getvalue() + output = output.rstrip() + + if 'q' not in opts: + page.page(output) + print sys_exit, + + dump_file = opts.D[0] + text_file = opts.T[0] + if dump_file: + dump_file = unquote_filename(dump_file) + prof.dump_stats(dump_file) + print '\n*** Profile stats marshalled to file',\ + `dump_file`+'.',sys_exit + if text_file: + text_file = unquote_filename(text_file) + pfile = open(text_file,'w') + pfile.write(output) + pfile.close() + print '\n*** Profile printout saved to text file',\ + `text_file`+'.',sys_exit + + if opts.has_key('r'): + return stats + else: + return None + + @line_magic + def pdb(self, parameter_s=''): + """Control the automatic calling of the pdb interactive debugger. + + Call as '%pdb on', '%pdb 1', '%pdb off' or '%pdb 0'. If called without + argument it works as a toggle. + + When an exception is triggered, IPython can optionally call the + interactive pdb debugger after the traceback printout. %pdb toggles + this feature on and off. + + The initial state of this feature is set in your configuration + file (the option is ``InteractiveShell.pdb``). + + If you want to just activate the debugger AFTER an exception has fired, + without having to type '%pdb on' and rerunning your code, you can use + the %debug magic.""" + + par = parameter_s.strip().lower() + + if par: + try: + new_pdb = {'off':0,'0':0,'on':1,'1':1}[par] + except KeyError: + print ('Incorrect argument. Use on/1, off/0, ' + 'or nothing for a toggle.') + return + else: + # toggle + new_pdb = not self.shell.call_pdb + + # set on the shell + self.shell.call_pdb = new_pdb + print 'Automatic pdb calling has been turned',on_off(new_pdb) + + @line_magic + def debug(self, parameter_s=''): + """Activate the interactive debugger in post-mortem mode. + + If an exception has just occurred, this lets you inspect its stack + frames interactively. Note that this will always work only on the last + traceback that occurred, so you must call this quickly after an + exception that you wish to inspect has fired, because if another one + occurs, it clobbers the previous one. + + If you want IPython to automatically do this on every exception, see + the %pdb magic for more details. + """ + self.shell.debugger(force=True) + + @line_magic + def tb(self, s): + """Print the last traceback with the currently active exception mode. + + See %xmode for changing exception reporting modes.""" + self.shell.showtraceback() + + @skip_doctest + @line_magic + def run(self, parameter_s='', runner=None, + file_finder=get_py_filename): + """Run the named file inside IPython as a program. + + Usage:\\ + %run [-n -i -t [-N] -d [-b] -p [profile options]] file [args] + + Parameters after the filename are passed as command-line arguments to + the program (put in sys.argv). Then, control returns to IPython's + prompt. + + This is similar to running at a system prompt:\\ + $ python file args\\ + but with the advantage of giving you IPython's tracebacks, and of + loading all variables into your interactive namespace for further use + (unless -p is used, see below). + + The file is executed in a namespace initially consisting only of + __name__=='__main__' and sys.argv constructed as indicated. It thus + sees its environment as if it were being run as a stand-alone program + (except for sharing global objects such as previously imported + modules). But after execution, the IPython interactive namespace gets + updated with all variables defined in the program (except for __name__ + and sys.argv). This allows for very convenient loading of code for + interactive work, while giving each program a 'clean sheet' to run in. + + Options: + + -n: __name__ is NOT set to '__main__', but to the running file's name + without extension (as python does under import). This allows running + scripts and reloading the definitions in them without calling code + protected by an ' if __name__ == "__main__" ' clause. + + -i: run the file in IPython's namespace instead of an empty one. This + is useful if you are experimenting with code written in a text editor + which depends on variables defined interactively. + + -e: ignore sys.exit() calls or SystemExit exceptions in the script + being run. This is particularly useful if IPython is being used to + run unittests, which always exit with a sys.exit() call. In such + cases you are interested in the output of the test results, not in + seeing a traceback of the unittest module. + + -t: print timing information at the end of the run. IPython will give + you an estimated CPU time consumption for your script, which under + Unix uses the resource module to avoid the wraparound problems of + time.clock(). Under Unix, an estimate of time spent on system tasks + is also given (for Windows platforms this is reported as 0.0). + + If -t is given, an additional -N option can be given, where + must be an integer indicating how many times you want the script to + run. The final timing report will include total and per run results. + + For example (testing the script uniq_stable.py):: + + In [1]: run -t uniq_stable + + IPython CPU timings (estimated):\\ + User : 0.19597 s.\\ + System: 0.0 s.\\ + + In [2]: run -t -N5 uniq_stable + + IPython CPU timings (estimated):\\ + Total runs performed: 5\\ + Times : Total Per run\\ + User : 0.910862 s, 0.1821724 s.\\ + System: 0.0 s, 0.0 s. + + -d: run your program under the control of pdb, the Python debugger. + This allows you to execute your program step by step, watch variables, + etc. Internally, what IPython does is similar to calling: + + pdb.run('execfile("YOURFILENAME")') + + with a breakpoint set on line 1 of your file. You can change the line + number for this automatic breakpoint to be by using the -bN option + (where N must be an integer). For example:: + + %run -d -b40 myscript + + will set the first breakpoint at line 40 in myscript.py. Note that + the first breakpoint must be set on a line which actually does + something (not a comment or docstring) for it to stop execution. + + When the pdb debugger starts, you will see a (Pdb) prompt. You must + first enter 'c' (without quotes) to start execution up to the first + breakpoint. + + Entering 'help' gives information about the use of the debugger. You + can easily see pdb's full documentation with "import pdb;pdb.help()" + at a prompt. + + -p: run program under the control of the Python profiler module (which + prints a detailed report of execution times, function calls, etc). + + You can pass other options after -p which affect the behavior of the + profiler itself. See the docs for %prun for details. + + In this mode, the program's variables do NOT propagate back to the + IPython interactive namespace (because they remain in the namespace + where the profiler executes them). + + Internally this triggers a call to %prun, see its documentation for + details on the options available specifically for profiling. + + There is one special usage for which the text above doesn't apply: + if the filename ends with .ipy, the file is run as ipython script, + just as if the commands were written on IPython prompt. + + -m: specify module name to load instead of script path. Similar to + the -m option for the python interpreter. Use this option last if you + want to combine with other %run options. Unlike the python interpreter + only source modules are allowed no .pyc or .pyo files. + For example:: + + %run -m example + + will run the example module. + + """ + + # get arguments and set sys.argv for program to be run. + opts, arg_lst = self.parse_options(parameter_s, 'nidtN:b:pD:l:rs:T:em:', + mode='list', list_all=1) + if "m" in opts: + modulename = opts["m"][0] + modpath = find_mod(modulename) + if modpath is None: + warn('%r is not a valid modulename on sys.path'%modulename) + return + arg_lst = [modpath] + arg_lst + try: + filename = file_finder(arg_lst[0]) + except IndexError: + warn('you must provide at least a filename.') + print '\n%run:\n', oinspect.getdoc(self.run) + return + except IOError as e: + try: + msg = str(e) + except UnicodeError: + msg = e.message + error(msg) + return + + if filename.lower().endswith('.ipy'): + self.shell.safe_execfile_ipy(filename) + return + + # Control the response to exit() calls made by the script being run + exit_ignore = 'e' in opts + + # Make sure that the running script gets a proper sys.argv as if it + # were run from a system shell. + save_argv = sys.argv # save it for later restoring + + # simulate shell expansion on arguments, at least tilde expansion + args = [ os.path.expanduser(a) for a in arg_lst[1:] ] + + sys.argv = [filename] + args # put in the proper filename + # protect sys.argv from potential unicode strings on Python 2: + if not py3compat.PY3: + sys.argv = [ py3compat.cast_bytes(a) for a in sys.argv ] + + if 'i' in opts: + # Run in user's interactive namespace + prog_ns = self.shell.user_ns + __name__save = self.shell.user_ns['__name__'] + prog_ns['__name__'] = '__main__' + main_mod = self.shell.new_main_mod(prog_ns) + else: + # Run in a fresh, empty namespace + if 'n' in opts: + name = os.path.splitext(os.path.basename(filename))[0] + else: + name = '__main__' + + main_mod = self.shell.new_main_mod() + prog_ns = main_mod.__dict__ + prog_ns['__name__'] = name + + # Since '%run foo' emulates 'python foo.py' at the cmd line, we must + # set the __file__ global in the script's namespace + prog_ns['__file__'] = filename + + # pickle fix. See interactiveshell for an explanation. But we need to + # make sure that, if we overwrite __main__, we replace it at the end + main_mod_name = prog_ns['__name__'] + + if main_mod_name == '__main__': + restore_main = sys.modules['__main__'] + else: + restore_main = False + + # This needs to be undone at the end to prevent holding references to + # every single object ever created. + sys.modules[main_mod_name] = main_mod + + try: + stats = None + with self.shell.readline_no_record: + if 'p' in opts: + stats = self.prun('', 0, opts, arg_lst, prog_ns) + else: + if 'd' in opts: + deb = debugger.Pdb(self.shell.colors) + # reset Breakpoint state, which is moronically kept + # in a class + bdb.Breakpoint.next = 1 + bdb.Breakpoint.bplist = {} + bdb.Breakpoint.bpbynumber = [None] + # Set an initial breakpoint to stop execution + maxtries = 10 + bp = int(opts.get('b', [1])[0]) + checkline = deb.checkline(filename, bp) + if not checkline: + for bp in range(bp + 1, bp + maxtries + 1): + if deb.checkline(filename, bp): + break + else: + msg = ("\nI failed to find a valid line to set " + "a breakpoint\n" + "after trying up to line: %s.\n" + "Please set a valid breakpoint manually " + "with the -b option." % bp) + error(msg) + return + # if we find a good linenumber, set the breakpoint + deb.do_break('%s:%s' % (filename, bp)) + # Start file run + print "NOTE: Enter 'c' at the", + print "%s prompt to start your script." % deb.prompt + ns = {'execfile': py3compat.execfile, 'prog_ns': prog_ns} + try: + deb.run('execfile("%s", prog_ns)' % filename, ns) + + except: + etype, value, tb = sys.exc_info() + # Skip three frames in the traceback: the %run one, + # one inside bdb.py, and the command-line typed by the + # user (run by exec in pdb itself). + self.shell.InteractiveTB(etype, value, tb, tb_offset=3) + else: + if runner is None: + runner = self.default_runner + if runner is None: + runner = self.shell.safe_execfile + if 't' in opts: + # timed execution + try: + nruns = int(opts['N'][0]) + if nruns < 1: + error('Number of runs must be >=1') + return + except (KeyError): + nruns = 1 + twall0 = time.time() + if nruns == 1: + t0 = clock2() + runner(filename, prog_ns, prog_ns, + exit_ignore=exit_ignore) + t1 = clock2() + t_usr = t1[0] - t0[0] + t_sys = t1[1] - t0[1] + print "\nIPython CPU timings (estimated):" + print " User : %10.2f s." % t_usr + print " System : %10.2f s." % t_sys + else: + runs = range(nruns) + t0 = clock2() + for nr in runs: + runner(filename, prog_ns, prog_ns, + exit_ignore=exit_ignore) + t1 = clock2() + t_usr = t1[0] - t0[0] + t_sys = t1[1] - t0[1] + print "\nIPython CPU timings (estimated):" + print "Total runs performed:", nruns + print " Times : %10.2f %10.2f" % ('Total', 'Per run') + print " User : %10.2f s, %10.2f s." % (t_usr, t_usr / nruns) + print " System : %10.2f s, %10.2f s." % (t_sys, t_sys / nruns) + twall1 = time.time() + print "Wall time: %10.2f s." % (twall1 - twall0) + + else: + # regular execution + runner(filename, prog_ns, prog_ns, exit_ignore=exit_ignore) + + if 'i' in opts: + self.shell.user_ns['__name__'] = __name__save + else: + # The shell MUST hold a reference to prog_ns so after %run + # exits, the python deletion mechanism doesn't zero it out + # (leaving dangling references). + self.shell.cache_main_mod(prog_ns, filename) + # update IPython interactive namespace + + # Some forms of read errors on the file may mean the + # __name__ key was never set; using pop we don't have to + # worry about a possible KeyError. + prog_ns.pop('__name__', None) + + self.shell.user_ns.update(prog_ns) + finally: + # It's a bit of a mystery why, but __builtins__ can change from + # being a module to becoming a dict missing some key data after + # %run. As best I can see, this is NOT something IPython is doing + # at all, and similar problems have been reported before: + # http://coding.derkeiler.com/Archive/Python/comp.lang.python/2004-10/0188.html + # Since this seems to be done by the interpreter itself, the best + # we can do is to at least restore __builtins__ for the user on + # exit. + self.shell.user_ns['__builtins__'] = builtin_mod + + # Ensure key global structures are restored + sys.argv = save_argv + if restore_main: + sys.modules['__main__'] = restore_main + else: + # Remove from sys.modules the reference to main_mod we'd + # added. Otherwise it will trap references to objects + # contained therein. + del sys.modules[main_mod_name] + + return stats + + @skip_doctest + @line_magic + def timeit(self, parameter_s=''): + """Time execution of a Python statement or expression + + Usage:\\ + %timeit [-n -r [-t|-c]] statement + + Time execution of a Python statement or expression using the timeit + module. + + Options: + -n: execute the given statement times in a loop. If this value + is not given, a fitting value is chosen. + + -r: repeat the loop iteration times and take the best result. + Default: 3 + + -t: use time.time to measure the time, which is the default on Unix. + This function measures wall time. + + -c: use time.clock to measure the time, which is the default on + Windows and measures wall time. On Unix, resource.getrusage is used + instead and returns the CPU user time. + + -p

: use a precision of

digits to display the timing result. + Default: 3 + + + Examples + -------- + :: + + In [1]: %timeit pass + 10000000 loops, best of 3: 53.3 ns per loop + + In [2]: u = None + + In [3]: %timeit u is None + 10000000 loops, best of 3: 184 ns per loop + + In [4]: %timeit -r 4 u == None + 1000000 loops, best of 4: 242 ns per loop + + In [5]: import time + + In [6]: %timeit -n1 time.sleep(2) + 1 loops, best of 3: 2 s per loop + + + The times reported by %timeit will be slightly higher than those + reported by the timeit.py script when variables are accessed. This is + due to the fact that %timeit executes the statement in the namespace + of the shell, compared with timeit.py, which uses a single setup + statement to import function or create variables. Generally, the bias + does not matter as long as results from timeit.py are not mixed with + those from %timeit.""" + + import timeit + import math + + # XXX: Unfortunately the unicode 'micro' symbol can cause problems in + # certain terminals. Until we figure out a robust way of + # auto-detecting if the terminal can deal with it, use plain 'us' for + # microseconds. I am really NOT happy about disabling the proper + # 'micro' prefix, but crashing is worse... If anyone knows what the + # right solution for this is, I'm all ears... + # + # Note: using + # + # s = u'\xb5' + # s.encode(sys.getdefaultencoding()) + # + # is not sufficient, as I've seen terminals where that fails but + # print s + # + # succeeds + # + # See bug: https://bugs.launchpad.net/ipython/+bug/348466 + + #units = [u"s", u"ms",u'\xb5',"ns"] + units = [u"s", u"ms",u'us',"ns"] + + scaling = [1, 1e3, 1e6, 1e9] + + opts, stmt = self.parse_options(parameter_s,'n:r:tcp:', + posix=False, strict=False) + if stmt == "": + return + timefunc = timeit.default_timer + number = int(getattr(opts, "n", 0)) + repeat = int(getattr(opts, "r", timeit.default_repeat)) + precision = int(getattr(opts, "p", 3)) + if hasattr(opts, "t"): + timefunc = time.time + if hasattr(opts, "c"): + timefunc = clock + + timer = timeit.Timer(timer=timefunc) + # this code has tight coupling to the inner workings of timeit.Timer, + # but is there a better way to achieve that the code stmt has access + # to the shell namespace? + + src = timeit.template % {'stmt': timeit.reindent(stmt, 8), + 'setup': "pass"} + # Track compilation time so it can be reported if too long + # Minimum time above which compilation time will be reported + tc_min = 0.1 + + t0 = clock() + code = compile(src, "", "exec") + tc = clock()-t0 + + ns = {} + exec code in self.shell.user_ns, ns + timer.inner = ns["inner"] + + if number == 0: + # determine number so that 0.2 <= total time < 2.0 + number = 1 + for i in range(1, 10): + if timer.timeit(number) >= 0.2: + break + number *= 10 + + best = min(timer.repeat(repeat, number)) / number + + if best > 0.0 and best < 1000.0: + order = min(-int(math.floor(math.log10(best)) // 3), 3) + elif best >= 1000.0: + order = 0 + else: + order = 3 + print u"%d loops, best of %d: %.*g %s per loop" % (number, repeat, + precision, + best * scaling[order], + units[order]) + if tc > tc_min: + print "Compiler time: %.2f s" % tc + + @skip_doctest + @needs_local_scope + @line_magic + def time(self,parameter_s, user_locals): + """Time execution of a Python statement or expression. + + The CPU and wall clock times are printed, and the value of the + expression (if any) is returned. Note that under Win32, system time + is always reported as 0, since it can not be measured. + + This function provides very basic timing functionality. In Python + 2.3, the timeit module offers more control and sophistication, so this + could be rewritten to use it (patches welcome). + + Examples + -------- + :: + + In [1]: time 2**128 + CPU times: user 0.00 s, sys: 0.00 s, total: 0.00 s + Wall time: 0.00 + Out[1]: 340282366920938463463374607431768211456L + + In [2]: n = 1000000 + + In [3]: time sum(range(n)) + CPU times: user 1.20 s, sys: 0.05 s, total: 1.25 s + Wall time: 1.37 + Out[3]: 499999500000L + + In [4]: time print 'hello world' + hello world + CPU times: user 0.00 s, sys: 0.00 s, total: 0.00 s + Wall time: 0.00 + + Note that the time needed by Python to compile the given expression + will be reported if it is more than 0.1s. In this example, the + actual exponentiation is done by Python at compilation time, so while + the expression can take a noticeable amount of time to compute, that + time is purely due to the compilation: + + In [5]: time 3**9999; + CPU times: user 0.00 s, sys: 0.00 s, total: 0.00 s + Wall time: 0.00 s + + In [6]: time 3**999999; + CPU times: user 0.00 s, sys: 0.00 s, total: 0.00 s + Wall time: 0.00 s + Compiler : 0.78 s + """ + + # fail immediately if the given expression can't be compiled + + expr = self.shell.prefilter(parameter_s,False) + + # Minimum time above which compilation time will be reported + tc_min = 0.1 + + try: + mode = 'eval' + t0 = clock() + code = compile(expr,'',mode) + tc = clock()-t0 + except SyntaxError: + mode = 'exec' + t0 = clock() + code = compile(expr,'',mode) + tc = clock()-t0 + # skew measurement as little as possible + glob = self.shell.user_ns + wtime = time.time + # time execution + wall_st = wtime() + if mode=='eval': + st = clock2() + out = eval(code, glob, user_locals) + end = clock2() + else: + st = clock2() + exec code in glob, user_locals + end = clock2() + out = None + wall_end = wtime() + # Compute actual times and report + wall_time = wall_end-wall_st + cpu_user = end[0]-st[0] + cpu_sys = end[1]-st[1] + cpu_tot = cpu_user+cpu_sys + print "CPU times: user %.2f s, sys: %.2f s, total: %.2f s" % \ + (cpu_user,cpu_sys,cpu_tot) + print "Wall time: %.2f s" % wall_time + if tc > tc_min: + print "Compiler : %.2f s" % tc + return out + + @skip_doctest + @line_magic + def macro(self, parameter_s=''): + """Define a macro for future re-execution. It accepts ranges of history, + filenames or string objects. + + Usage:\\ + %macro [options] name n1-n2 n3-n4 ... n5 .. n6 ... + + Options: + + -r: use 'raw' input. By default, the 'processed' history is used, + so that magics are loaded in their transformed version to valid + Python. If this option is given, the raw input as typed as the + command line is used instead. + + This will define a global variable called `name` which is a string + made of joining the slices and lines you specify (n1,n2,... numbers + above) from your input history into a single string. This variable + acts like an automatic function which re-executes those lines as if + you had typed them. You just type 'name' at the prompt and the code + executes. + + The syntax for indicating input ranges is described in %history. + + Note: as a 'hidden' feature, you can also use traditional python slice + notation, where N:M means numbers N through M-1. + + For example, if your history contains (%hist prints it):: + + 44: x=1 + 45: y=3 + 46: z=x+y + 47: print x + 48: a=5 + 49: print 'x',x,'y',y + + you can create a macro with lines 44 through 47 (included) and line 49 + called my_macro with:: + + In [55]: %macro my_macro 44-47 49 + + Now, typing `my_macro` (without quotes) will re-execute all this code + in one pass. + + You don't need to give the line-numbers in order, and any given line + number can appear multiple times. You can assemble macros with any + lines from your input history in any order. + + The macro is a simple object which holds its value in an attribute, + but IPython's display system checks for macros and executes them as + code instead of printing them when you type their name. + + You can view a macro's contents by explicitly printing it with:: + + print macro_name + + """ + opts,args = self.parse_options(parameter_s,'r',mode='list') + if not args: # List existing macros + return sorted(k for k,v in self.shell.user_ns.iteritems() if\ + isinstance(v, Macro)) + if len(args) == 1: + raise UsageError( + "%macro insufficient args; usage '%macro name n1-n2 n3-4...") + name, codefrom = args[0], " ".join(args[1:]) + + #print 'rng',ranges # dbg + try: + lines = self.shell.find_user_code(codefrom, 'r' in opts) + except (ValueError, TypeError) as e: + print e.args[0] + return + macro = Macro(lines) + self.shell.define_macro(name, macro) + print 'Macro `%s` created. To execute, type its name (without quotes).' % name + print '=== Macro contents: ===' + print macro, From d53e149768dc81ae75243e822dd3e0ef11952d25 Mon Sep 17 00:00:00 2001 From: Fernando Perez Date: Wed, 23 May 2012 14:48:03 -0700 Subject: [PATCH 059/103] Create core.magics.auto according to new API. --- IPython/core/interactiveshell.py | 2 +- IPython/core/magic_functions.py | 107 -------------------------- IPython/core/magics/__init__.py | 1 + IPython/core/magics/auto.py | 128 +++++++++++++++++++++++++++++++ 4 files changed, 130 insertions(+), 108 deletions(-) create mode 100644 IPython/core/magics/auto.py diff --git a/IPython/core/interactiveshell.py b/IPython/core/interactiveshell.py index d9bc9f6932a..da2a268672b 100644 --- a/IPython/core/interactiveshell.py +++ b/IPython/core/interactiveshell.py @@ -2005,7 +2005,7 @@ def init_magics(self): self.register_magic_function = self.magics_manager.register_function self.define_magic = self.magics_manager.define_magic - self.register_magics(mf.AutoMagics, m.BasicMagics, m.CodeMagics, + self.register_magics(m.AutoMagics, m.BasicMagics, m.CodeMagics, m.ConfigMagics, mf.DeprecatedMagics, m.ExecutionMagics, mf.ExtensionsMagics, m.HistoryMagics, mf.LoggingMagics, m.NamespaceMagics, mf.OSMagics, mf.PylabMagics ) diff --git a/IPython/core/magic_functions.py b/IPython/core/magic_functions.py index 8ba6ab42cf9..1eb9dc7c9c2 100644 --- a/IPython/core/magic_functions.py +++ b/IPython/core/magic_functions.py @@ -66,113 +66,6 @@ # Magic implementation classes #----------------------------------------------------------------------------- -@register_magics -class AutoMagics(Magics): - """Magics that control various autoX behaviors.""" - - def __init__(self, shell): - super(AutoMagics, self).__init__(shell) - # namespace for holding state we may need - self._magic_state = Bunch() - - @line_magic - def automagic(self, parameter_s=''): - """Make magic functions callable without having to type the initial %. - - Without argumentsl toggles on/off (when off, you must call it as - %automagic, of course). With arguments it sets the value, and you can - use any of (case insensitive): - - - on, 1, True: to activate - - - off, 0, False: to deactivate. - - Note that magic functions have lowest priority, so if there's a - variable whose name collides with that of a magic fn, automagic won't - work for that function (you get the variable instead). However, if you - delete the variable (del var), the previously shadowed magic function - becomes visible to automagic again.""" - - arg = parameter_s.lower() - mman = self.shell.magics_manager - if arg in ('on', '1', 'true'): - val = True - elif arg in ('off', '0', 'false'): - val = False - else: - val = not mman.auto_magic - mman.auto_magic = val - print '\n' + self.shell.magics_manager.auto_status() - - @skip_doctest - @line_magic - def autocall(self, parameter_s=''): - """Make functions callable without having to type parentheses. - - Usage: - - %autocall [mode] - - The mode can be one of: 0->Off, 1->Smart, 2->Full. If not given, the - value is toggled on and off (remembering the previous state). - - In more detail, these values mean: - - 0 -> fully disabled - - 1 -> active, but do not apply if there are no arguments on the line. - - In this mode, you get:: - - In [1]: callable - Out[1]: - - In [2]: callable 'hello' - ------> callable('hello') - Out[2]: False - - 2 -> Active always. Even if no arguments are present, the callable - object is called:: - - In [2]: float - ------> float() - Out[2]: 0.0 - - Note that even with autocall off, you can still use '/' at the start of - a line to treat the first argument on the command line as a function - and add parentheses to it:: - - In [8]: /str 43 - ------> str(43) - Out[8]: '43' - - # all-random (note for auto-testing) - """ - - if parameter_s: - arg = int(parameter_s) - else: - arg = 'toggle' - - if not arg in (0, 1, 2,'toggle'): - error('Valid modes: (0->Off, 1->Smart, 2->Full') - return - - if arg in (0, 1, 2): - self.shell.autocall = arg - else: # toggle - if self.shell.autocall: - self._magic_state.autocall_save = self.shell.autocall - self.shell.autocall = 0 - else: - try: - self.shell.autocall = self._magic_state.autocall_save - except AttributeError: - self.shell.autocall = self._magic_state.autocall_save = 1 - - print "Automatic calling is:",['OFF','Smart','Full'][self.shell.autocall] - - @register_magics class OSMagics(Magics): """Magics to interact with the underlying OS (shell-type functionality). diff --git a/IPython/core/magics/__init__.py b/IPython/core/magics/__init__.py index 34753505035..52e51cb2df8 100644 --- a/IPython/core/magics/__init__.py +++ b/IPython/core/magics/__init__.py @@ -13,6 +13,7 @@ #----------------------------------------------------------------------------- from IPython.core.magic import Magics, register_magics +from .auto import AutoMagics from .basic import BasicMagics from .code import CodeMagics, MacroToEdit from .config import ConfigMagics diff --git a/IPython/core/magics/auto.py b/IPython/core/magics/auto.py new file mode 100644 index 00000000000..54767d4c0c0 --- /dev/null +++ b/IPython/core/magics/auto.py @@ -0,0 +1,128 @@ +"""Implementation of execution-related magic functions. +""" +#----------------------------------------------------------------------------- +# Copyright (c) 2012 The IPython Development Team. +# +# Distributed under the terms of the Modified BSD License. +# +# The full license is in the file COPYING.txt, distributed with this software. +#----------------------------------------------------------------------------- + +#----------------------------------------------------------------------------- +# Imports +#----------------------------------------------------------------------------- + +# Our own packages +from IPython.core.magic import Bunch, Magics, register_magics, line_magic +from IPython.testing.skipdoctest import skip_doctest +from IPython.utils.warn import error + +#----------------------------------------------------------------------------- +# Magic implementation classes +#----------------------------------------------------------------------------- + +@register_magics +class AutoMagics(Magics): + """Magics that control various autoX behaviors.""" + + def __init__(self, shell): + super(AutoMagics, self).__init__(shell) + # namespace for holding state we may need + self._magic_state = Bunch() + + @line_magic + def automagic(self, parameter_s=''): + """Make magic functions callable without having to type the initial %. + + Without argumentsl toggles on/off (when off, you must call it as + %automagic, of course). With arguments it sets the value, and you can + use any of (case insensitive): + + - on, 1, True: to activate + + - off, 0, False: to deactivate. + + Note that magic functions have lowest priority, so if there's a + variable whose name collides with that of a magic fn, automagic won't + work for that function (you get the variable instead). However, if you + delete the variable (del var), the previously shadowed magic function + becomes visible to automagic again.""" + + arg = parameter_s.lower() + mman = self.shell.magics_manager + if arg in ('on', '1', 'true'): + val = True + elif arg in ('off', '0', 'false'): + val = False + else: + val = not mman.auto_magic + mman.auto_magic = val + print '\n' + self.shell.magics_manager.auto_status() + + @skip_doctest + @line_magic + def autocall(self, parameter_s=''): + """Make functions callable without having to type parentheses. + + Usage: + + %autocall [mode] + + The mode can be one of: 0->Off, 1->Smart, 2->Full. If not given, the + value is toggled on and off (remembering the previous state). + + In more detail, these values mean: + + 0 -> fully disabled + + 1 -> active, but do not apply if there are no arguments on the line. + + In this mode, you get:: + + In [1]: callable + Out[1]: + + In [2]: callable 'hello' + ------> callable('hello') + Out[2]: False + + 2 -> Active always. Even if no arguments are present, the callable + object is called:: + + In [2]: float + ------> float() + Out[2]: 0.0 + + Note that even with autocall off, you can still use '/' at the start of + a line to treat the first argument on the command line as a function + and add parentheses to it:: + + In [8]: /str 43 + ------> str(43) + Out[8]: '43' + + # all-random (note for auto-testing) + """ + + if parameter_s: + arg = int(parameter_s) + else: + arg = 'toggle' + + if not arg in (0, 1, 2,'toggle'): + error('Valid modes: (0->Off, 1->Smart, 2->Full') + return + + if arg in (0, 1, 2): + self.shell.autocall = arg + else: # toggle + if self.shell.autocall: + self._magic_state.autocall_save = self.shell.autocall + self.shell.autocall = 0 + else: + try: + self.shell.autocall = self._magic_state.autocall_save + except AttributeError: + self.shell.autocall = self._magic_state.autocall_save = 1 + + print "Automatic calling is:",['OFF','Smart','Full'][self.shell.autocall] From e5bbe651d6cf2c1f04d811cebe61439ff2c3aea8 Mon Sep 17 00:00:00 2001 From: Fernando Perez Date: Wed, 23 May 2012 14:56:51 -0700 Subject: [PATCH 060/103] Create core.magics.osm according to new API. --- IPython/core/interactiveshell.py | 2 +- IPython/core/magic_functions.py | 679 +------------------------------ IPython/core/magics/__init__.py | 1 + IPython/core/magics/auto.py | 2 +- IPython/core/magics/osm.py | 676 ++++++++++++++++++++++++++++++ 5 files changed, 684 insertions(+), 676 deletions(-) create mode 100644 IPython/core/magics/osm.py diff --git a/IPython/core/interactiveshell.py b/IPython/core/interactiveshell.py index da2a268672b..1dccf72ff8e 100644 --- a/IPython/core/interactiveshell.py +++ b/IPython/core/interactiveshell.py @@ -2008,7 +2008,7 @@ def init_magics(self): self.register_magics(m.AutoMagics, m.BasicMagics, m.CodeMagics, m.ConfigMagics, mf.DeprecatedMagics, m.ExecutionMagics, mf.ExtensionsMagics, m.HistoryMagics, mf.LoggingMagics, - m.NamespaceMagics, mf.OSMagics, mf.PylabMagics ) + m.NamespaceMagics, m.OSMagics, mf.PylabMagics ) # FIXME: Move the color initialization to the DisplayHook, which # should be split into a prompt manager and displayhook. We probably diff --git a/IPython/core/magic_functions.py b/IPython/core/magic_functions.py index 1eb9dc7c9c2..9e372d1a13a 100644 --- a/IPython/core/magic_functions.py +++ b/IPython/core/magic_functions.py @@ -15,698 +15,29 @@ #----------------------------------------------------------------------------- # Stdlib -import __builtin__ as builtin_mod -import bdb -import gc -import inspect -import io -import json import os import re import sys -import time -from StringIO import StringIO from pprint import pformat -from urllib2 import urlopen - -# cProfile was added in Python2.5 -try: - import cProfile as profile - import pstats -except ImportError: - # profile isn't bundled by default in Debian for license reasons - try: - import profile, pstats - except ImportError: - profile = pstats = None # Our own packages from IPython.config.application import Application -from IPython.core import debugger, oinspect +from IPython.core import oinspect from IPython.core import page -from IPython.core.error import UsageError, StdinNotImplementedError, TryNext -from IPython.core.macro import Macro -from IPython.core.magic import (Bunch, Magics, compress_dhist, - on_off, needs_local_scope, - register_magics, line_magic, cell_magic) +from IPython.core.error import UsageError +from IPython.core.magic import (Magics, compress_dhist, + register_magics, line_magic) from IPython.testing.skipdoctest import skip_doctest -from IPython.utils import openpy -from IPython.utils import py3compat -from IPython.utils.encoding import DEFAULT_ENCODING from IPython.utils.io import file_read, nlprint -from IPython.utils.ipstruct import Struct -from IPython.utils.module_paths import find_mod from IPython.utils.path import get_py_filename, unquote_filename from IPython.utils.process import abbrev_cwd from IPython.utils.terminal import set_term_title -from IPython.utils.timing import clock, clock2 -from IPython.utils.warn import warn, error +from IPython.utils.warn import warn #----------------------------------------------------------------------------- # Magic implementation classes #----------------------------------------------------------------------------- -@register_magics -class OSMagics(Magics): - """Magics to interact with the underlying OS (shell-type functionality). - """ - - @skip_doctest - @line_magic - def alias(self, parameter_s=''): - """Define an alias for a system command. - - '%alias alias_name cmd' defines 'alias_name' as an alias for 'cmd' - - Then, typing 'alias_name params' will execute the system command 'cmd - params' (from your underlying operating system). - - Aliases have lower precedence than magic functions and Python normal - variables, so if 'foo' is both a Python variable and an alias, the - alias can not be executed until 'del foo' removes the Python variable. - - You can use the %l specifier in an alias definition to represent the - whole line when the alias is called. For example:: - - In [2]: alias bracket echo "Input in brackets: <%l>" - In [3]: bracket hello world - Input in brackets: - - You can also define aliases with parameters using %s specifiers (one - per parameter):: - - In [1]: alias parts echo first %s second %s - In [2]: %parts A B - first A second B - In [3]: %parts A - Incorrect number of arguments: 2 expected. - parts is an alias to: 'echo first %s second %s' - - Note that %l and %s are mutually exclusive. You can only use one or - the other in your aliases. - - Aliases expand Python variables just like system calls using ! or !! - do: all expressions prefixed with '$' get expanded. For details of - the semantic rules, see PEP-215: - http://www.python.org/peps/pep-0215.html. This is the library used by - IPython for variable expansion. If you want to access a true shell - variable, an extra $ is necessary to prevent its expansion by - IPython:: - - In [6]: alias show echo - In [7]: PATH='A Python string' - In [8]: show $PATH - A Python string - In [9]: show $$PATH - /usr/local/lf9560/bin:/usr/local/intel/compiler70/ia32/bin:... - - You can use the alias facility to acess all of $PATH. See the %rehash - and %rehashx functions, which automatically create aliases for the - contents of your $PATH. - - If called with no parameters, %alias prints the current alias table.""" - - par = parameter_s.strip() - if not par: - aliases = sorted(self.shell.alias_manager.aliases) - # stored = self.shell.db.get('stored_aliases', {} ) - # for k, v in stored: - # atab.append(k, v[0]) - - print "Total number of aliases:", len(aliases) - sys.stdout.flush() - return aliases - - # Now try to define a new one - try: - alias,cmd = par.split(None, 1) - except: - print oinspect.getdoc(self.alias) - else: - self.shell.alias_manager.soft_define_alias(alias, cmd) - # end magic_alias - - @line_magic - def unalias(self, parameter_s=''): - """Remove an alias""" - - aname = parameter_s.strip() - self.shell.alias_manager.undefine_alias(aname) - stored = self.shell.db.get('stored_aliases', {} ) - if aname in stored: - print "Removing %stored alias",aname - del stored[aname] - self.shell.db['stored_aliases'] = stored - - @line_magic - def rehashx(self, parameter_s=''): - """Update the alias table with all executable files in $PATH. - - This version explicitly checks that every entry in $PATH is a file - with execute access (os.X_OK), so it is much slower than %rehash. - - Under Windows, it checks executability as a match against a - '|'-separated string of extensions, stored in the IPython config - variable win_exec_ext. This defaults to 'exe|com|bat'. - - This function also resets the root module cache of module completer, - used on slow filesystems. - """ - from IPython.core.alias import InvalidAliasError - - # for the benefit of module completer in ipy_completers.py - del self.shell.db['rootmodules'] - - path = [os.path.abspath(os.path.expanduser(p)) for p in - os.environ.get('PATH','').split(os.pathsep)] - path = filter(os.path.isdir,path) - - syscmdlist = [] - # Now define isexec in a cross platform manner. - if os.name == 'posix': - isexec = lambda fname:os.path.isfile(fname) and \ - os.access(fname,os.X_OK) - else: - try: - winext = os.environ['pathext'].replace(';','|').replace('.','') - except KeyError: - winext = 'exe|com|bat|py' - if 'py' not in winext: - winext += '|py' - execre = re.compile(r'(.*)\.(%s)$' % winext,re.IGNORECASE) - isexec = lambda fname:os.path.isfile(fname) and execre.match(fname) - savedir = os.getcwdu() - - # Now walk the paths looking for executables to alias. - try: - # write the whole loop for posix/Windows so we don't have an if in - # the innermost part - if os.name == 'posix': - for pdir in path: - os.chdir(pdir) - for ff in os.listdir(pdir): - if isexec(ff): - try: - # Removes dots from the name since ipython - # will assume names with dots to be python. - self.shell.alias_manager.define_alias( - ff.replace('.',''), ff) - except InvalidAliasError: - pass - else: - syscmdlist.append(ff) - else: - no_alias = self.shell.alias_manager.no_alias - for pdir in path: - os.chdir(pdir) - for ff in os.listdir(pdir): - base, ext = os.path.splitext(ff) - if isexec(ff) and base.lower() not in no_alias: - if ext.lower() == '.exe': - ff = base - try: - # Removes dots from the name since ipython - # will assume names with dots to be python. - self.shell.alias_manager.define_alias( - base.lower().replace('.',''), ff) - except InvalidAliasError: - pass - syscmdlist.append(ff) - self.shell.db['syscmdlist'] = syscmdlist - finally: - os.chdir(savedir) - - @skip_doctest - @line_magic - def pwd(self, parameter_s=''): - """Return the current working directory path. - - Examples - -------- - :: - - In [9]: pwd - Out[9]: '/home/tsuser/sprint/ipython' - """ - return os.getcwdu() - - @skip_doctest - @line_magic - def cd(self, parameter_s=''): - """Change the current working directory. - - This command automatically maintains an internal list of directories - you visit during your IPython session, in the variable _dh. The - command %dhist shows this history nicely formatted. You can also - do 'cd -' to see directory history conveniently. - - Usage: - - cd 'dir': changes to directory 'dir'. - - cd -: changes to the last visited directory. - - cd -: changes to the n-th directory in the directory history. - - cd --foo: change to directory that matches 'foo' in history - - cd -b : jump to a bookmark set by %bookmark - (note: cd is enough if there is no - directory , but a bookmark with the name exists.) - 'cd -b ' allows you to tab-complete bookmark names. - - Options: - - -q: quiet. Do not print the working directory after the cd command is - executed. By default IPython's cd command does print this directory, - since the default prompts do not display path information. - - Note that !cd doesn't work for this purpose because the shell where - !command runs is immediately discarded after executing 'command'. - - Examples - -------- - :: - - In [10]: cd parent/child - /home/tsuser/parent/child - """ - - #bkms = self.shell.persist.get("bookmarks",{}) - - oldcwd = os.getcwdu() - numcd = re.match(r'(-)(\d+)$',parameter_s) - # jump in directory history by number - if numcd: - nn = int(numcd.group(2)) - try: - ps = self.shell.user_ns['_dh'][nn] - except IndexError: - print 'The requested directory does not exist in history.' - return - else: - opts = {} - elif parameter_s.startswith('--'): - ps = None - fallback = None - pat = parameter_s[2:] - dh = self.shell.user_ns['_dh'] - # first search only by basename (last component) - for ent in reversed(dh): - if pat in os.path.basename(ent) and os.path.isdir(ent): - ps = ent - break - - if fallback is None and pat in ent and os.path.isdir(ent): - fallback = ent - - # if we have no last part match, pick the first full path match - if ps is None: - ps = fallback - - if ps is None: - print "No matching entry in directory history" - return - else: - opts = {} - - - else: - #turn all non-space-escaping backslashes to slashes, - # for c:\windows\directory\names\ - parameter_s = re.sub(r'\\(?! )','/', parameter_s) - opts,ps = self.parse_options(parameter_s,'qb',mode='string') - # jump to previous - if ps == '-': - try: - ps = self.shell.user_ns['_dh'][-2] - except IndexError: - raise UsageError('%cd -: No previous directory to change to.') - # jump to bookmark if needed - else: - if not os.path.isdir(ps) or opts.has_key('b'): - bkms = self.shell.db.get('bookmarks', {}) - - if bkms.has_key(ps): - target = bkms[ps] - print '(bookmark:%s) -> %s' % (ps,target) - ps = target - else: - if opts.has_key('b'): - raise UsageError("Bookmark '%s' not found. " - "Use '%%bookmark -l' to see your bookmarks." % ps) - - # strip extra quotes on Windows, because os.chdir doesn't like them - ps = unquote_filename(ps) - # at this point ps should point to the target dir - if ps: - try: - os.chdir(os.path.expanduser(ps)) - if hasattr(self.shell, 'term_title') and self.shell.term_title: - set_term_title('IPython: ' + abbrev_cwd()) - except OSError: - print sys.exc_info()[1] - else: - cwd = os.getcwdu() - dhist = self.shell.user_ns['_dh'] - if oldcwd != cwd: - dhist.append(cwd) - self.shell.db['dhist'] = compress_dhist(dhist)[-100:] - - else: - os.chdir(self.shell.home_dir) - if hasattr(self.shell, 'term_title') and self.shell.term_title: - set_term_title('IPython: ' + '~') - cwd = os.getcwdu() - dhist = self.shell.user_ns['_dh'] - - if oldcwd != cwd: - dhist.append(cwd) - self.shell.db['dhist'] = compress_dhist(dhist)[-100:] - if not 'q' in opts and self.shell.user_ns['_dh']: - print self.shell.user_ns['_dh'][-1] - - - @line_magic - def env(self, parameter_s=''): - """List environment variables.""" - - return dict(os.environ) - - @line_magic - def pushd(self, parameter_s=''): - """Place the current dir on stack and change directory. - - Usage:\\ - %pushd ['dirname'] - """ - - dir_s = self.shell.dir_stack - tgt = os.path.expanduser(unquote_filename(parameter_s)) - cwd = os.getcwdu().replace(self.shell.home_dir,'~') - if tgt: - self.cd(parameter_s) - dir_s.insert(0,cwd) - return self.shell.magic('dirs') - - @line_magic - def popd(self, parameter_s=''): - """Change to directory popped off the top of the stack. - """ - if not self.shell.dir_stack: - raise UsageError("%popd on empty stack") - top = self.shell.dir_stack.pop(0) - self.cd(top) - print "popd ->",top - - @line_magic - def dirs(self, parameter_s=''): - """Return the current directory stack.""" - - return self.shell.dir_stack - - @line_magic - def dhist(self, parameter_s=''): - """Print your history of visited directories. - - %dhist -> print full history\\ - %dhist n -> print last n entries only\\ - %dhist n1 n2 -> print entries between n1 and n2 (n1 not included)\\ - - This history is automatically maintained by the %cd command, and - always available as the global list variable _dh. You can use %cd - - to go to directory number . - - Note that most of time, you should view directory history by entering - cd -. - - """ - - dh = self.shell.user_ns['_dh'] - if parameter_s: - try: - args = map(int,parameter_s.split()) - except: - self.arg_err(self.dhist) - return - if len(args) == 1: - ini,fin = max(len(dh)-(args[0]),0),len(dh) - elif len(args) == 2: - ini,fin = args - else: - self.arg_err(self.dhist) - return - else: - ini,fin = 0,len(dh) - nlprint(dh, - header = 'Directory history (kept in _dh)', - start=ini,stop=fin) - - @skip_doctest - @line_magic - def sc(self, parameter_s=''): - """Shell capture - execute a shell command and capture its output. - - DEPRECATED. Suboptimal, retained for backwards compatibility. - - You should use the form 'var = !command' instead. Example: - - "%sc -l myfiles = ls ~" should now be written as - - "myfiles = !ls ~" - - myfiles.s, myfiles.l and myfiles.n still apply as documented - below. - - -- - %sc [options] varname=command - - IPython will run the given command using commands.getoutput(), and - will then update the user's interactive namespace with a variable - called varname, containing the value of the call. Your command can - contain shell wildcards, pipes, etc. - - The '=' sign in the syntax is mandatory, and the variable name you - supply must follow Python's standard conventions for valid names. - - (A special format without variable name exists for internal use) - - Options: - - -l: list output. Split the output on newlines into a list before - assigning it to the given variable. By default the output is stored - as a single string. - - -v: verbose. Print the contents of the variable. - - In most cases you should not need to split as a list, because the - returned value is a special type of string which can automatically - provide its contents either as a list (split on newlines) or as a - space-separated string. These are convenient, respectively, either - for sequential processing or to be passed to a shell command. - - For example:: - - # Capture into variable a - In [1]: sc a=ls *py - - # a is a string with embedded newlines - In [2]: a - Out[2]: 'setup.py\\nwin32_manual_post_install.py' - - # which can be seen as a list: - In [3]: a.l - Out[3]: ['setup.py', 'win32_manual_post_install.py'] - - # or as a whitespace-separated string: - In [4]: a.s - Out[4]: 'setup.py win32_manual_post_install.py' - - # a.s is useful to pass as a single command line: - In [5]: !wc -l $a.s - 146 setup.py - 130 win32_manual_post_install.py - 276 total - - # while the list form is useful to loop over: - In [6]: for f in a.l: - ...: !wc -l $f - ...: - 146 setup.py - 130 win32_manual_post_install.py - - Similarly, the lists returned by the -l option are also special, in - the sense that you can equally invoke the .s attribute on them to - automatically get a whitespace-separated string from their contents:: - - In [7]: sc -l b=ls *py - - In [8]: b - Out[8]: ['setup.py', 'win32_manual_post_install.py'] - - In [9]: b.s - Out[9]: 'setup.py win32_manual_post_install.py' - - In summary, both the lists and strings used for output capture have - the following special attributes:: - - .l (or .list) : value as list. - .n (or .nlstr): value as newline-separated string. - .s (or .spstr): value as space-separated string. - """ - - opts,args = self.parse_options(parameter_s,'lv') - # Try to get a variable name and command to run - try: - # the variable name must be obtained from the parse_options - # output, which uses shlex.split to strip options out. - var,_ = args.split('=',1) - var = var.strip() - # But the command has to be extracted from the original input - # parameter_s, not on what parse_options returns, to avoid the - # quote stripping which shlex.split performs on it. - _,cmd = parameter_s.split('=',1) - except ValueError: - var,cmd = '','' - # If all looks ok, proceed - split = 'l' in opts - out = self.shell.getoutput(cmd, split=split) - if opts.has_key('v'): - print '%s ==\n%s' % (var,pformat(out)) - if var: - self.shell.user_ns.update({var:out}) - else: - return out - - @line_magic - def sx(self, parameter_s=''): - """Shell execute - run a shell command and capture its output. - - %sx command - - IPython will run the given command using commands.getoutput(), and - return the result formatted as a list (split on '\\n'). Since the - output is _returned_, it will be stored in ipython's regular output - cache Out[N] and in the '_N' automatic variables. - - Notes: - - 1) If an input line begins with '!!', then %sx is automatically - invoked. That is, while:: - - !ls - - causes ipython to simply issue system('ls'), typing:: - - !!ls - - is a shorthand equivalent to:: - - %sx ls - - 2) %sx differs from %sc in that %sx automatically splits into a list, - like '%sc -l'. The reason for this is to make it as easy as possible - to process line-oriented shell output via further python commands. - %sc is meant to provide much finer control, but requires more - typing. - - 3) Just like %sc -l, this is a list with special attributes: - :: - - .l (or .list) : value as list. - .n (or .nlstr): value as newline-separated string. - .s (or .spstr): value as whitespace-separated string. - - This is very useful when trying to use such lists as arguments to - system commands.""" - - if parameter_s: - return self.shell.getoutput(parameter_s) - - - @line_magic - def bookmark(self, parameter_s=''): - """Manage IPython's bookmark system. - - %bookmark - set bookmark to current dir - %bookmark

- set bookmark to - %bookmark -l - list all bookmarks - %bookmark -d - remove bookmark - %bookmark -r - remove all bookmarks - - You can later on access a bookmarked folder with:: - - %cd -b - - or simply '%cd ' if there is no directory called AND - there is such a bookmark defined. - - Your bookmarks persist through IPython sessions, but they are - associated with each profile.""" - - opts,args = self.parse_options(parameter_s,'drl',mode='list') - if len(args) > 2: - raise UsageError("%bookmark: too many arguments") - - bkms = self.shell.db.get('bookmarks',{}) - - if opts.has_key('d'): - try: - todel = args[0] - except IndexError: - raise UsageError( - "%bookmark -d: must provide a bookmark to delete") - else: - try: - del bkms[todel] - except KeyError: - raise UsageError( - "%%bookmark -d: Can't delete bookmark '%s'" % todel) - - elif opts.has_key('r'): - bkms = {} - elif opts.has_key('l'): - bks = bkms.keys() - bks.sort() - if bks: - size = max(map(len,bks)) - else: - size = 0 - fmt = '%-'+str(size)+'s -> %s' - print 'Current bookmarks:' - for bk in bks: - print fmt % (bk,bkms[bk]) - else: - if not args: - raise UsageError("%bookmark: You must specify the bookmark name") - elif len(args)==1: - bkms[args[0]] = os.getcwdu() - elif len(args)==2: - bkms[args[0]] = args[1] - self.shell.db['bookmarks'] = bkms - - @line_magic - def pycat(self, parameter_s=''): - """Show a syntax-highlighted file through a pager. - - This magic is similar to the cat utility, but it will assume the file - to be Python source and will show it with syntax highlighting. """ - - try: - filename = get_py_filename(parameter_s) - cont = file_read(filename) - except IOError: - try: - cont = eval(parameter_s, self.shell.user_ns) - except NameError: - cont = None - if cont is None: - print "Error: no such file or variable" - return - - page.page(self.shell.pycolorize(cont)) - - @register_magics class LoggingMagics(Magics): """Magics related to all logging machinery.""" diff --git a/IPython/core/magics/__init__.py b/IPython/core/magics/__init__.py index 52e51cb2df8..0af99fc303c 100644 --- a/IPython/core/magics/__init__.py +++ b/IPython/core/magics/__init__.py @@ -20,6 +20,7 @@ from .execution import ExecutionMagics from .history import HistoryMagics from .namespace import NamespaceMagics +from .osm import OSMagics #----------------------------------------------------------------------------- # Magic implementation classes diff --git a/IPython/core/magics/auto.py b/IPython/core/magics/auto.py index 54767d4c0c0..bfa5059de58 100644 --- a/IPython/core/magics/auto.py +++ b/IPython/core/magics/auto.py @@ -1,4 +1,4 @@ -"""Implementation of execution-related magic functions. +"""Implementation of magic functions that control various automatic behaviors. """ #----------------------------------------------------------------------------- # Copyright (c) 2012 The IPython Development Team. diff --git a/IPython/core/magics/osm.py b/IPython/core/magics/osm.py new file mode 100644 index 00000000000..f4eae8c4a8a --- /dev/null +++ b/IPython/core/magics/osm.py @@ -0,0 +1,676 @@ +"""Implementation of magic functions for interaction with the OS. + +Note: this module is named 'osm' instead of 'os' to avoid a collision with the +builtin. +""" +#----------------------------------------------------------------------------- +# Copyright (c) 2012 The IPython Development Team. +# +# Distributed under the terms of the Modified BSD License. +# +# The full license is in the file COPYING.txt, distributed with this software. +#----------------------------------------------------------------------------- + +#----------------------------------------------------------------------------- +# Imports +#----------------------------------------------------------------------------- + +# Stdlib +import os +import re +import sys +from pprint import pformat + +# Our own packages +from IPython.core import oinspect +from IPython.core import page +from IPython.core.error import UsageError +from IPython.core.magic import (Magics, compress_dhist, register_magics, + line_magic) +from IPython.testing.skipdoctest import skip_doctest +from IPython.utils.io import file_read, nlprint +from IPython.utils.path import get_py_filename, unquote_filename +from IPython.utils.process import abbrev_cwd +from IPython.utils.terminal import set_term_title +#----------------------------------------------------------------------------- +# Magic implementation classes +#----------------------------------------------------------------------------- +@register_magics +class OSMagics(Magics): + """Magics to interact with the underlying OS (shell-type functionality). + """ + + @skip_doctest + @line_magic + def alias(self, parameter_s=''): + """Define an alias for a system command. + + '%alias alias_name cmd' defines 'alias_name' as an alias for 'cmd' + + Then, typing 'alias_name params' will execute the system command 'cmd + params' (from your underlying operating system). + + Aliases have lower precedence than magic functions and Python normal + variables, so if 'foo' is both a Python variable and an alias, the + alias can not be executed until 'del foo' removes the Python variable. + + You can use the %l specifier in an alias definition to represent the + whole line when the alias is called. For example:: + + In [2]: alias bracket echo "Input in brackets: <%l>" + In [3]: bracket hello world + Input in brackets: + + You can also define aliases with parameters using %s specifiers (one + per parameter):: + + In [1]: alias parts echo first %s second %s + In [2]: %parts A B + first A second B + In [3]: %parts A + Incorrect number of arguments: 2 expected. + parts is an alias to: 'echo first %s second %s' + + Note that %l and %s are mutually exclusive. You can only use one or + the other in your aliases. + + Aliases expand Python variables just like system calls using ! or !! + do: all expressions prefixed with '$' get expanded. For details of + the semantic rules, see PEP-215: + http://www.python.org/peps/pep-0215.html. This is the library used by + IPython for variable expansion. If you want to access a true shell + variable, an extra $ is necessary to prevent its expansion by + IPython:: + + In [6]: alias show echo + In [7]: PATH='A Python string' + In [8]: show $PATH + A Python string + In [9]: show $$PATH + /usr/local/lf9560/bin:/usr/local/intel/compiler70/ia32/bin:... + + You can use the alias facility to acess all of $PATH. See the %rehash + and %rehashx functions, which automatically create aliases for the + contents of your $PATH. + + If called with no parameters, %alias prints the current alias table.""" + + par = parameter_s.strip() + if not par: + aliases = sorted(self.shell.alias_manager.aliases) + # stored = self.shell.db.get('stored_aliases', {} ) + # for k, v in stored: + # atab.append(k, v[0]) + + print "Total number of aliases:", len(aliases) + sys.stdout.flush() + return aliases + + # Now try to define a new one + try: + alias,cmd = par.split(None, 1) + except: + print oinspect.getdoc(self.alias) + else: + self.shell.alias_manager.soft_define_alias(alias, cmd) + # end magic_alias + + @line_magic + def unalias(self, parameter_s=''): + """Remove an alias""" + + aname = parameter_s.strip() + self.shell.alias_manager.undefine_alias(aname) + stored = self.shell.db.get('stored_aliases', {} ) + if aname in stored: + print "Removing %stored alias",aname + del stored[aname] + self.shell.db['stored_aliases'] = stored + + @line_magic + def rehashx(self, parameter_s=''): + """Update the alias table with all executable files in $PATH. + + This version explicitly checks that every entry in $PATH is a file + with execute access (os.X_OK), so it is much slower than %rehash. + + Under Windows, it checks executability as a match against a + '|'-separated string of extensions, stored in the IPython config + variable win_exec_ext. This defaults to 'exe|com|bat'. + + This function also resets the root module cache of module completer, + used on slow filesystems. + """ + from IPython.core.alias import InvalidAliasError + + # for the benefit of module completer in ipy_completers.py + del self.shell.db['rootmodules'] + + path = [os.path.abspath(os.path.expanduser(p)) for p in + os.environ.get('PATH','').split(os.pathsep)] + path = filter(os.path.isdir,path) + + syscmdlist = [] + # Now define isexec in a cross platform manner. + if os.name == 'posix': + isexec = lambda fname:os.path.isfile(fname) and \ + os.access(fname,os.X_OK) + else: + try: + winext = os.environ['pathext'].replace(';','|').replace('.','') + except KeyError: + winext = 'exe|com|bat|py' + if 'py' not in winext: + winext += '|py' + execre = re.compile(r'(.*)\.(%s)$' % winext,re.IGNORECASE) + isexec = lambda fname:os.path.isfile(fname) and execre.match(fname) + savedir = os.getcwdu() + + # Now walk the paths looking for executables to alias. + try: + # write the whole loop for posix/Windows so we don't have an if in + # the innermost part + if os.name == 'posix': + for pdir in path: + os.chdir(pdir) + for ff in os.listdir(pdir): + if isexec(ff): + try: + # Removes dots from the name since ipython + # will assume names with dots to be python. + self.shell.alias_manager.define_alias( + ff.replace('.',''), ff) + except InvalidAliasError: + pass + else: + syscmdlist.append(ff) + else: + no_alias = self.shell.alias_manager.no_alias + for pdir in path: + os.chdir(pdir) + for ff in os.listdir(pdir): + base, ext = os.path.splitext(ff) + if isexec(ff) and base.lower() not in no_alias: + if ext.lower() == '.exe': + ff = base + try: + # Removes dots from the name since ipython + # will assume names with dots to be python. + self.shell.alias_manager.define_alias( + base.lower().replace('.',''), ff) + except InvalidAliasError: + pass + syscmdlist.append(ff) + self.shell.db['syscmdlist'] = syscmdlist + finally: + os.chdir(savedir) + + @skip_doctest + @line_magic + def pwd(self, parameter_s=''): + """Return the current working directory path. + + Examples + -------- + :: + + In [9]: pwd + Out[9]: '/home/tsuser/sprint/ipython' + """ + return os.getcwdu() + + @skip_doctest + @line_magic + def cd(self, parameter_s=''): + """Change the current working directory. + + This command automatically maintains an internal list of directories + you visit during your IPython session, in the variable _dh. The + command %dhist shows this history nicely formatted. You can also + do 'cd -' to see directory history conveniently. + + Usage: + + cd 'dir': changes to directory 'dir'. + + cd -: changes to the last visited directory. + + cd -: changes to the n-th directory in the directory history. + + cd --foo: change to directory that matches 'foo' in history + + cd -b : jump to a bookmark set by %bookmark + (note: cd is enough if there is no + directory , but a bookmark with the name exists.) + 'cd -b ' allows you to tab-complete bookmark names. + + Options: + + -q: quiet. Do not print the working directory after the cd command is + executed. By default IPython's cd command does print this directory, + since the default prompts do not display path information. + + Note that !cd doesn't work for this purpose because the shell where + !command runs is immediately discarded after executing 'command'. + + Examples + -------- + :: + + In [10]: cd parent/child + /home/tsuser/parent/child + """ + + #bkms = self.shell.persist.get("bookmarks",{}) + + oldcwd = os.getcwdu() + numcd = re.match(r'(-)(\d+)$',parameter_s) + # jump in directory history by number + if numcd: + nn = int(numcd.group(2)) + try: + ps = self.shell.user_ns['_dh'][nn] + except IndexError: + print 'The requested directory does not exist in history.' + return + else: + opts = {} + elif parameter_s.startswith('--'): + ps = None + fallback = None + pat = parameter_s[2:] + dh = self.shell.user_ns['_dh'] + # first search only by basename (last component) + for ent in reversed(dh): + if pat in os.path.basename(ent) and os.path.isdir(ent): + ps = ent + break + + if fallback is None and pat in ent and os.path.isdir(ent): + fallback = ent + + # if we have no last part match, pick the first full path match + if ps is None: + ps = fallback + + if ps is None: + print "No matching entry in directory history" + return + else: + opts = {} + + + else: + #turn all non-space-escaping backslashes to slashes, + # for c:\windows\directory\names\ + parameter_s = re.sub(r'\\(?! )','/', parameter_s) + opts,ps = self.parse_options(parameter_s,'qb',mode='string') + # jump to previous + if ps == '-': + try: + ps = self.shell.user_ns['_dh'][-2] + except IndexError: + raise UsageError('%cd -: No previous directory to change to.') + # jump to bookmark if needed + else: + if not os.path.isdir(ps) or opts.has_key('b'): + bkms = self.shell.db.get('bookmarks', {}) + + if bkms.has_key(ps): + target = bkms[ps] + print '(bookmark:%s) -> %s' % (ps,target) + ps = target + else: + if opts.has_key('b'): + raise UsageError("Bookmark '%s' not found. " + "Use '%%bookmark -l' to see your bookmarks." % ps) + + # strip extra quotes on Windows, because os.chdir doesn't like them + ps = unquote_filename(ps) + # at this point ps should point to the target dir + if ps: + try: + os.chdir(os.path.expanduser(ps)) + if hasattr(self.shell, 'term_title') and self.shell.term_title: + set_term_title('IPython: ' + abbrev_cwd()) + except OSError: + print sys.exc_info()[1] + else: + cwd = os.getcwdu() + dhist = self.shell.user_ns['_dh'] + if oldcwd != cwd: + dhist.append(cwd) + self.shell.db['dhist'] = compress_dhist(dhist)[-100:] + + else: + os.chdir(self.shell.home_dir) + if hasattr(self.shell, 'term_title') and self.shell.term_title: + set_term_title('IPython: ' + '~') + cwd = os.getcwdu() + dhist = self.shell.user_ns['_dh'] + + if oldcwd != cwd: + dhist.append(cwd) + self.shell.db['dhist'] = compress_dhist(dhist)[-100:] + if not 'q' in opts and self.shell.user_ns['_dh']: + print self.shell.user_ns['_dh'][-1] + + + @line_magic + def env(self, parameter_s=''): + """List environment variables.""" + + return dict(os.environ) + + @line_magic + def pushd(self, parameter_s=''): + """Place the current dir on stack and change directory. + + Usage:\\ + %pushd ['dirname'] + """ + + dir_s = self.shell.dir_stack + tgt = os.path.expanduser(unquote_filename(parameter_s)) + cwd = os.getcwdu().replace(self.shell.home_dir,'~') + if tgt: + self.cd(parameter_s) + dir_s.insert(0,cwd) + return self.shell.magic('dirs') + + @line_magic + def popd(self, parameter_s=''): + """Change to directory popped off the top of the stack. + """ + if not self.shell.dir_stack: + raise UsageError("%popd on empty stack") + top = self.shell.dir_stack.pop(0) + self.cd(top) + print "popd ->",top + + @line_magic + def dirs(self, parameter_s=''): + """Return the current directory stack.""" + + return self.shell.dir_stack + + @line_magic + def dhist(self, parameter_s=''): + """Print your history of visited directories. + + %dhist -> print full history\\ + %dhist n -> print last n entries only\\ + %dhist n1 n2 -> print entries between n1 and n2 (n1 not included)\\ + + This history is automatically maintained by the %cd command, and + always available as the global list variable _dh. You can use %cd - + to go to directory number . + + Note that most of time, you should view directory history by entering + cd -. + + """ + + dh = self.shell.user_ns['_dh'] + if parameter_s: + try: + args = map(int,parameter_s.split()) + except: + self.arg_err(self.dhist) + return + if len(args) == 1: + ini,fin = max(len(dh)-(args[0]),0),len(dh) + elif len(args) == 2: + ini,fin = args + else: + self.arg_err(self.dhist) + return + else: + ini,fin = 0,len(dh) + nlprint(dh, + header = 'Directory history (kept in _dh)', + start=ini,stop=fin) + + @skip_doctest + @line_magic + def sc(self, parameter_s=''): + """Shell capture - execute a shell command and capture its output. + + DEPRECATED. Suboptimal, retained for backwards compatibility. + + You should use the form 'var = !command' instead. Example: + + "%sc -l myfiles = ls ~" should now be written as + + "myfiles = !ls ~" + + myfiles.s, myfiles.l and myfiles.n still apply as documented + below. + + -- + %sc [options] varname=command + + IPython will run the given command using commands.getoutput(), and + will then update the user's interactive namespace with a variable + called varname, containing the value of the call. Your command can + contain shell wildcards, pipes, etc. + + The '=' sign in the syntax is mandatory, and the variable name you + supply must follow Python's standard conventions for valid names. + + (A special format without variable name exists for internal use) + + Options: + + -l: list output. Split the output on newlines into a list before + assigning it to the given variable. By default the output is stored + as a single string. + + -v: verbose. Print the contents of the variable. + + In most cases you should not need to split as a list, because the + returned value is a special type of string which can automatically + provide its contents either as a list (split on newlines) or as a + space-separated string. These are convenient, respectively, either + for sequential processing or to be passed to a shell command. + + For example:: + + # Capture into variable a + In [1]: sc a=ls *py + + # a is a string with embedded newlines + In [2]: a + Out[2]: 'setup.py\\nwin32_manual_post_install.py' + + # which can be seen as a list: + In [3]: a.l + Out[3]: ['setup.py', 'win32_manual_post_install.py'] + + # or as a whitespace-separated string: + In [4]: a.s + Out[4]: 'setup.py win32_manual_post_install.py' + + # a.s is useful to pass as a single command line: + In [5]: !wc -l $a.s + 146 setup.py + 130 win32_manual_post_install.py + 276 total + + # while the list form is useful to loop over: + In [6]: for f in a.l: + ...: !wc -l $f + ...: + 146 setup.py + 130 win32_manual_post_install.py + + Similarly, the lists returned by the -l option are also special, in + the sense that you can equally invoke the .s attribute on them to + automatically get a whitespace-separated string from their contents:: + + In [7]: sc -l b=ls *py + + In [8]: b + Out[8]: ['setup.py', 'win32_manual_post_install.py'] + + In [9]: b.s + Out[9]: 'setup.py win32_manual_post_install.py' + + In summary, both the lists and strings used for output capture have + the following special attributes:: + + .l (or .list) : value as list. + .n (or .nlstr): value as newline-separated string. + .s (or .spstr): value as space-separated string. + """ + + opts,args = self.parse_options(parameter_s,'lv') + # Try to get a variable name and command to run + try: + # the variable name must be obtained from the parse_options + # output, which uses shlex.split to strip options out. + var,_ = args.split('=',1) + var = var.strip() + # But the command has to be extracted from the original input + # parameter_s, not on what parse_options returns, to avoid the + # quote stripping which shlex.split performs on it. + _,cmd = parameter_s.split('=',1) + except ValueError: + var,cmd = '','' + # If all looks ok, proceed + split = 'l' in opts + out = self.shell.getoutput(cmd, split=split) + if opts.has_key('v'): + print '%s ==\n%s' % (var,pformat(out)) + if var: + self.shell.user_ns.update({var:out}) + else: + return out + + @line_magic + def sx(self, parameter_s=''): + """Shell execute - run a shell command and capture its output. + + %sx command + + IPython will run the given command using commands.getoutput(), and + return the result formatted as a list (split on '\\n'). Since the + output is _returned_, it will be stored in ipython's regular output + cache Out[N] and in the '_N' automatic variables. + + Notes: + + 1) If an input line begins with '!!', then %sx is automatically + invoked. That is, while:: + + !ls + + causes ipython to simply issue system('ls'), typing:: + + !!ls + + is a shorthand equivalent to:: + + %sx ls + + 2) %sx differs from %sc in that %sx automatically splits into a list, + like '%sc -l'. The reason for this is to make it as easy as possible + to process line-oriented shell output via further python commands. + %sc is meant to provide much finer control, but requires more + typing. + + 3) Just like %sc -l, this is a list with special attributes: + :: + + .l (or .list) : value as list. + .n (or .nlstr): value as newline-separated string. + .s (or .spstr): value as whitespace-separated string. + + This is very useful when trying to use such lists as arguments to + system commands.""" + + if parameter_s: + return self.shell.getoutput(parameter_s) + + + @line_magic + def bookmark(self, parameter_s=''): + """Manage IPython's bookmark system. + + %bookmark - set bookmark to current dir + %bookmark - set bookmark to + %bookmark -l - list all bookmarks + %bookmark -d - remove bookmark + %bookmark -r - remove all bookmarks + + You can later on access a bookmarked folder with:: + + %cd -b + + or simply '%cd ' if there is no directory called AND + there is such a bookmark defined. + + Your bookmarks persist through IPython sessions, but they are + associated with each profile.""" + + opts,args = self.parse_options(parameter_s,'drl',mode='list') + if len(args) > 2: + raise UsageError("%bookmark: too many arguments") + + bkms = self.shell.db.get('bookmarks',{}) + + if opts.has_key('d'): + try: + todel = args[0] + except IndexError: + raise UsageError( + "%bookmark -d: must provide a bookmark to delete") + else: + try: + del bkms[todel] + except KeyError: + raise UsageError( + "%%bookmark -d: Can't delete bookmark '%s'" % todel) + + elif opts.has_key('r'): + bkms = {} + elif opts.has_key('l'): + bks = bkms.keys() + bks.sort() + if bks: + size = max(map(len,bks)) + else: + size = 0 + fmt = '%-'+str(size)+'s -> %s' + print 'Current bookmarks:' + for bk in bks: + print fmt % (bk,bkms[bk]) + else: + if not args: + raise UsageError("%bookmark: You must specify the bookmark name") + elif len(args)==1: + bkms[args[0]] = os.getcwdu() + elif len(args)==2: + bkms[args[0]] = args[1] + self.shell.db['bookmarks'] = bkms + + @line_magic + def pycat(self, parameter_s=''): + """Show a syntax-highlighted file through a pager. + + This magic is similar to the cat utility, but it will assume the file + to be Python source and will show it with syntax highlighting. """ + + try: + filename = get_py_filename(parameter_s) + cont = file_read(filename) + except IOError: + try: + cont = eval(parameter_s, self.shell.user_ns) + except NameError: + cont = None + if cont is None: + print "Error: no such file or variable" + return + + page.page(self.shell.pycolorize(cont)) From 47dea0297159628924a84de380187ab5912a8490 Mon Sep 17 00:00:00 2001 From: Fernando Perez Date: Wed, 23 May 2012 16:22:24 -0700 Subject: [PATCH 061/103] Create core.magics.logging according to new API. --- IPython/core/interactiveshell.py | 2 +- IPython/core/magic_functions.py | 159 +---------------------------- IPython/core/magics/__init__.py | 3 +- IPython/core/magics/logging.py | 169 +++++++++++++++++++++++++++++++ 4 files changed, 173 insertions(+), 160 deletions(-) create mode 100644 IPython/core/magics/logging.py diff --git a/IPython/core/interactiveshell.py b/IPython/core/interactiveshell.py index 1dccf72ff8e..5c44af43d53 100644 --- a/IPython/core/interactiveshell.py +++ b/IPython/core/interactiveshell.py @@ -2007,7 +2007,7 @@ def init_magics(self): self.register_magics(m.AutoMagics, m.BasicMagics, m.CodeMagics, m.ConfigMagics, mf.DeprecatedMagics, m.ExecutionMagics, - mf.ExtensionsMagics, m.HistoryMagics, mf.LoggingMagics, + mf.ExtensionsMagics, m.HistoryMagics, m.LoggingMagics, m.NamespaceMagics, m.OSMagics, mf.PylabMagics ) # FIXME: Move the color initialization to the DisplayHook, which diff --git a/IPython/core/magic_functions.py b/IPython/core/magic_functions.py index 9e372d1a13a..6c4f982d71b 100644 --- a/IPython/core/magic_functions.py +++ b/IPython/core/magic_functions.py @@ -16,173 +16,16 @@ # Stdlib import os -import re -import sys -from pprint import pformat # Our own packages from IPython.config.application import Application -from IPython.core import oinspect -from IPython.core import page -from IPython.core.error import UsageError -from IPython.core.magic import (Magics, compress_dhist, - register_magics, line_magic) +from IPython.core.magic import Magics, register_magics, line_magic from IPython.testing.skipdoctest import skip_doctest -from IPython.utils.io import file_read, nlprint -from IPython.utils.path import get_py_filename, unquote_filename -from IPython.utils.process import abbrev_cwd -from IPython.utils.terminal import set_term_title -from IPython.utils.warn import warn #----------------------------------------------------------------------------- # Magic implementation classes #----------------------------------------------------------------------------- -@register_magics -class LoggingMagics(Magics): - """Magics related to all logging machinery.""" - - @line_magic - def logstart(self, parameter_s=''): - """Start logging anywhere in a session. - - %logstart [-o|-r|-t] [log_name [log_mode]] - - If no name is given, it defaults to a file named 'ipython_log.py' in your - current directory, in 'rotate' mode (see below). - - '%logstart name' saves to file 'name' in 'backup' mode. It saves your - history up to that point and then continues logging. - - %logstart takes a second optional parameter: logging mode. This can be one - of (note that the modes are given unquoted):\\ - append: well, that says it.\\ - backup: rename (if exists) to name~ and start name.\\ - global: single logfile in your home dir, appended to.\\ - over : overwrite existing log.\\ - rotate: create rotating logs name.1~, name.2~, etc. - - Options: - - -o: log also IPython's output. In this mode, all commands which - generate an Out[NN] prompt are recorded to the logfile, right after - their corresponding input line. The output lines are always - prepended with a '#[Out]# ' marker, so that the log remains valid - Python code. - - Since this marker is always the same, filtering only the output from - a log is very easy, using for example a simple awk call:: - - awk -F'#\\[Out\\]# ' '{if($2) {print $2}}' ipython_log.py - - -r: log 'raw' input. Normally, IPython's logs contain the processed - input, so that user lines are logged in their final form, converted - into valid Python. For example, %Exit is logged as - _ip.magic("Exit"). If the -r flag is given, all input is logged - exactly as typed, with no transformations applied. - - -t: put timestamps before each input line logged (these are put in - comments).""" - - opts,par = self.parse_options(parameter_s,'ort') - log_output = 'o' in opts - log_raw_input = 'r' in opts - timestamp = 't' in opts - - logger = self.shell.logger - - # if no args are given, the defaults set in the logger constructor by - # ipython remain valid - if par: - try: - logfname,logmode = par.split() - except: - logfname = par - logmode = 'backup' - else: - logfname = logger.logfname - logmode = logger.logmode - # put logfname into rc struct as if it had been called on the command - # line, so it ends up saved in the log header Save it in case we need - # to restore it... - old_logfile = self.shell.logfile - if logfname: - logfname = os.path.expanduser(logfname) - self.shell.logfile = logfname - - loghead = '# IPython log file\n\n' - try: - logger.logstart(logfname, loghead, logmode, log_output, timestamp, - log_raw_input) - except: - self.shell.logfile = old_logfile - warn("Couldn't start log: %s" % sys.exc_info()[1]) - else: - # log input history up to this point, optionally interleaving - # output if requested - - if timestamp: - # disable timestamping for the previous history, since we've - # lost those already (no time machine here). - logger.timestamp = False - - if log_raw_input: - input_hist = self.shell.history_manager.input_hist_raw - else: - input_hist = self.shell.history_manager.input_hist_parsed - - if log_output: - log_write = logger.log_write - output_hist = self.shell.history_manager.output_hist - for n in range(1,len(input_hist)-1): - log_write(input_hist[n].rstrip() + '\n') - if n in output_hist: - log_write(repr(output_hist[n]),'output') - else: - logger.log_write('\n'.join(input_hist[1:])) - logger.log_write('\n') - if timestamp: - # re-enable timestamping - logger.timestamp = True - - print ('Activating auto-logging. ' - 'Current session state plus future input saved.') - logger.logstate() - - @line_magic - def logstop(self, parameter_s=''): - """Fully stop logging and close log file. - - In order to start logging again, a new %logstart call needs to be made, - possibly (though not necessarily) with a new filename, mode and other - options.""" - self.logger.logstop() - - @line_magic - def logoff(self, parameter_s=''): - """Temporarily stop logging. - - You must have previously started logging.""" - self.shell.logger.switch_log(0) - - @line_magic - def logon(self, parameter_s=''): - """Restart logging. - - This function is for restarting logging which you've temporarily - stopped with %logoff. For starting logging for the first time, you - must use the %logstart function, which allows you to specify an - optional log filename.""" - - self.shell.logger.switch_log(1) - - @line_magic - def logstate(self, parameter_s=''): - """Print the status of the logging system.""" - - self.shell.logger.logstate() - - @register_magics class ExtensionsMagics(Magics): """Magics to manage the IPython extensions system.""" diff --git a/IPython/core/magics/__init__.py b/IPython/core/magics/__init__.py index 0af99fc303c..61b319e3648 100644 --- a/IPython/core/magics/__init__.py +++ b/IPython/core/magics/__init__.py @@ -12,13 +12,14 @@ # Imports #----------------------------------------------------------------------------- -from IPython.core.magic import Magics, register_magics +from ..magic import Magics, register_magics from .auto import AutoMagics from .basic import BasicMagics from .code import CodeMagics, MacroToEdit from .config import ConfigMagics from .execution import ExecutionMagics from .history import HistoryMagics +from .logging import LoggingMagics from .namespace import NamespaceMagics from .osm import OSMagics diff --git a/IPython/core/magics/logging.py b/IPython/core/magics/logging.py new file mode 100644 index 00000000000..f8606fb6a1e --- /dev/null +++ b/IPython/core/magics/logging.py @@ -0,0 +1,169 @@ +"""Implementation of magic functions for IPython's own logging. +""" +#----------------------------------------------------------------------------- +# Copyright (c) 2012 The IPython Development Team. +# +# Distributed under the terms of the Modified BSD License. +# +# The full license is in the file COPYING.txt, distributed with this software. +#----------------------------------------------------------------------------- + +#----------------------------------------------------------------------------- +# Imports +#----------------------------------------------------------------------------- + +# Stdlib +import os +import sys + +# Our own packages +from IPython.core.magic import Magics, register_magics, line_magic +from IPython.utils.warn import warn + +#----------------------------------------------------------------------------- +# Magic implementation classes +#----------------------------------------------------------------------------- + +@register_magics +class LoggingMagics(Magics): + """Magics related to all logging machinery.""" + + @line_magic + def logstart(self, parameter_s=''): + """Start logging anywhere in a session. + + %logstart [-o|-r|-t] [log_name [log_mode]] + + If no name is given, it defaults to a file named 'ipython_log.py' in your + current directory, in 'rotate' mode (see below). + + '%logstart name' saves to file 'name' in 'backup' mode. It saves your + history up to that point and then continues logging. + + %logstart takes a second optional parameter: logging mode. This can be one + of (note that the modes are given unquoted):\\ + append: well, that says it.\\ + backup: rename (if exists) to name~ and start name.\\ + global: single logfile in your home dir, appended to.\\ + over : overwrite existing log.\\ + rotate: create rotating logs name.1~, name.2~, etc. + + Options: + + -o: log also IPython's output. In this mode, all commands which + generate an Out[NN] prompt are recorded to the logfile, right after + their corresponding input line. The output lines are always + prepended with a '#[Out]# ' marker, so that the log remains valid + Python code. + + Since this marker is always the same, filtering only the output from + a log is very easy, using for example a simple awk call:: + + awk -F'#\\[Out\\]# ' '{if($2) {print $2}}' ipython_log.py + + -r: log 'raw' input. Normally, IPython's logs contain the processed + input, so that user lines are logged in their final form, converted + into valid Python. For example, %Exit is logged as + _ip.magic("Exit"). If the -r flag is given, all input is logged + exactly as typed, with no transformations applied. + + -t: put timestamps before each input line logged (these are put in + comments).""" + + opts,par = self.parse_options(parameter_s,'ort') + log_output = 'o' in opts + log_raw_input = 'r' in opts + timestamp = 't' in opts + + logger = self.shell.logger + + # if no args are given, the defaults set in the logger constructor by + # ipython remain valid + if par: + try: + logfname,logmode = par.split() + except: + logfname = par + logmode = 'backup' + else: + logfname = logger.logfname + logmode = logger.logmode + # put logfname into rc struct as if it had been called on the command + # line, so it ends up saved in the log header Save it in case we need + # to restore it... + old_logfile = self.shell.logfile + if logfname: + logfname = os.path.expanduser(logfname) + self.shell.logfile = logfname + + loghead = '# IPython log file\n\n' + try: + logger.logstart(logfname, loghead, logmode, log_output, timestamp, + log_raw_input) + except: + self.shell.logfile = old_logfile + warn("Couldn't start log: %s" % sys.exc_info()[1]) + else: + # log input history up to this point, optionally interleaving + # output if requested + + if timestamp: + # disable timestamping for the previous history, since we've + # lost those already (no time machine here). + logger.timestamp = False + + if log_raw_input: + input_hist = self.shell.history_manager.input_hist_raw + else: + input_hist = self.shell.history_manager.input_hist_parsed + + if log_output: + log_write = logger.log_write + output_hist = self.shell.history_manager.output_hist + for n in range(1,len(input_hist)-1): + log_write(input_hist[n].rstrip() + '\n') + if n in output_hist: + log_write(repr(output_hist[n]),'output') + else: + logger.log_write('\n'.join(input_hist[1:])) + logger.log_write('\n') + if timestamp: + # re-enable timestamping + logger.timestamp = True + + print ('Activating auto-logging. ' + 'Current session state plus future input saved.') + logger.logstate() + + @line_magic + def logstop(self, parameter_s=''): + """Fully stop logging and close log file. + + In order to start logging again, a new %logstart call needs to be made, + possibly (though not necessarily) with a new filename, mode and other + options.""" + self.logger.logstop() + + @line_magic + def logoff(self, parameter_s=''): + """Temporarily stop logging. + + You must have previously started logging.""" + self.shell.logger.switch_log(0) + + @line_magic + def logon(self, parameter_s=''): + """Restart logging. + + This function is for restarting logging which you've temporarily + stopped with %logoff. For starting logging for the first time, you + must use the %logstart function, which allows you to specify an + optional log filename.""" + + self.shell.logger.switch_log(1) + + @line_magic + def logstate(self, parameter_s=''): + """Print the status of the logging system.""" + + self.shell.logger.logstate() From 16abc36f037a79383b001fcd21236fac5667de07 Mon Sep 17 00:00:00 2001 From: Fernando Perez Date: Wed, 23 May 2012 16:27:13 -0700 Subject: [PATCH 062/103] Create core.magics.extension according to new API. --- IPython/core/interactiveshell.py | 2 +- IPython/core/magic_functions.py | 50 ----------------------- IPython/core/magics/__init__.py | 1 + IPython/core/magics/extension.py | 69 ++++++++++++++++++++++++++++++++ 4 files changed, 71 insertions(+), 51 deletions(-) create mode 100644 IPython/core/magics/extension.py diff --git a/IPython/core/interactiveshell.py b/IPython/core/interactiveshell.py index 5c44af43d53..fab14621f58 100644 --- a/IPython/core/interactiveshell.py +++ b/IPython/core/interactiveshell.py @@ -2007,7 +2007,7 @@ def init_magics(self): self.register_magics(m.AutoMagics, m.BasicMagics, m.CodeMagics, m.ConfigMagics, mf.DeprecatedMagics, m.ExecutionMagics, - mf.ExtensionsMagics, m.HistoryMagics, m.LoggingMagics, + m.ExtensionMagics, m.HistoryMagics, m.LoggingMagics, m.NamespaceMagics, m.OSMagics, mf.PylabMagics ) # FIXME: Move the color initialization to the DisplayHook, which diff --git a/IPython/core/magic_functions.py b/IPython/core/magic_functions.py index 6c4f982d71b..d8dfbea4877 100644 --- a/IPython/core/magic_functions.py +++ b/IPython/core/magic_functions.py @@ -14,9 +14,6 @@ # Imports #----------------------------------------------------------------------------- -# Stdlib -import os - # Our own packages from IPython.config.application import Application from IPython.core.magic import Magics, register_magics, line_magic @@ -26,53 +23,6 @@ # Magic implementation classes #----------------------------------------------------------------------------- -@register_magics -class ExtensionsMagics(Magics): - """Magics to manage the IPython extensions system.""" - - @line_magic - def install_ext(self, parameter_s=''): - """Download and install an extension from a URL, e.g.:: - - %install_ext https://bitbucket.org/birkenfeld/ipython-physics/raw/d1310a2ab15d/physics.py - - The URL should point to an importable Python module - either a .py file - or a .zip file. - - Parameters: - - -n filename : Specify a name for the file, rather than taking it from - the URL. - """ - opts, args = self.parse_options(parameter_s, 'n:') - try: - filename = self.shell.extension_manager.install_extension(args, - opts.get('n')) - except ValueError as e: - print e - return - - filename = os.path.basename(filename) - print "Installed %s. To use it, type:" % filename - print " %%load_ext %s" % os.path.splitext(filename)[0] - - - @line_magic - def load_ext(self, module_str): - """Load an IPython extension by its module name.""" - return self.shell.extension_manager.load_extension(module_str) - - @line_magic - def unload_ext(self, module_str): - """Unload an IPython extension by its module name.""" - self.shell.extension_manager.unload_extension(module_str) - - @line_magic - def reload_ext(self, module_str): - """Reload an IPython extension by its module name.""" - self.shell.extension_manager.reload_extension(module_str) - - @register_magics class PylabMagics(Magics): """Magics related to matplotlib's pylab support""" diff --git a/IPython/core/magics/__init__.py b/IPython/core/magics/__init__.py index 61b319e3648..afcf318ba36 100644 --- a/IPython/core/magics/__init__.py +++ b/IPython/core/magics/__init__.py @@ -18,6 +18,7 @@ from .code import CodeMagics, MacroToEdit from .config import ConfigMagics from .execution import ExecutionMagics +from .extension import ExtensionMagics from .history import HistoryMagics from .logging import LoggingMagics from .namespace import NamespaceMagics diff --git a/IPython/core/magics/extension.py b/IPython/core/magics/extension.py new file mode 100644 index 00000000000..9425eff4776 --- /dev/null +++ b/IPython/core/magics/extension.py @@ -0,0 +1,69 @@ +"""Implementation of magic functions for the extension machinery. +""" +#----------------------------------------------------------------------------- +# Copyright (c) 2012 The IPython Development Team. +# +# Distributed under the terms of the Modified BSD License. +# +# The full license is in the file COPYING.txt, distributed with this software. +#----------------------------------------------------------------------------- + +#----------------------------------------------------------------------------- +# Imports +#----------------------------------------------------------------------------- + +# Stdlib +import os + +# Our own packages +from IPython.core.magic import Magics, register_magics, line_magic + +#----------------------------------------------------------------------------- +# Magic implementation classes +#----------------------------------------------------------------------------- + +@register_magics +class ExtensionMagics(Magics): + """Magics to manage the IPython extensions system.""" + + @line_magic + def install_ext(self, parameter_s=''): + """Download and install an extension from a URL, e.g.:: + + %install_ext https://bitbucket.org/birkenfeld/ipython-physics/raw/d1310a2ab15d/physics.py + + The URL should point to an importable Python module - either a .py file + or a .zip file. + + Parameters: + + -n filename : Specify a name for the file, rather than taking it from + the URL. + """ + opts, args = self.parse_options(parameter_s, 'n:') + try: + filename = self.shell.extension_manager.install_extension(args, + opts.get('n')) + except ValueError as e: + print e + return + + filename = os.path.basename(filename) + print "Installed %s. To use it, type:" % filename + print " %%load_ext %s" % os.path.splitext(filename)[0] + + + @line_magic + def load_ext(self, module_str): + """Load an IPython extension by its module name.""" + return self.shell.extension_manager.load_extension(module_str) + + @line_magic + def unload_ext(self, module_str): + """Unload an IPython extension by its module name.""" + self.shell.extension_manager.unload_extension(module_str) + + @line_magic + def reload_ext(self, module_str): + """Reload an IPython extension by its module name.""" + self.shell.extension_manager.reload_extension(module_str) From 7fd6a540580de2e6a11794bb493480ea6b8a4a28 Mon Sep 17 00:00:00 2001 From: Fernando Perez Date: Wed, 23 May 2012 16:29:18 -0700 Subject: [PATCH 063/103] Create core.magics.pylab according to new API. --- IPython/core/interactiveshell.py | 2 +- IPython/core/magic_functions.py | 68 ------------------------ IPython/core/magics/__init__.py | 1 + IPython/core/magics/pylab.py | 88 ++++++++++++++++++++++++++++++++ 4 files changed, 90 insertions(+), 69 deletions(-) create mode 100644 IPython/core/magics/pylab.py diff --git a/IPython/core/interactiveshell.py b/IPython/core/interactiveshell.py index fab14621f58..a8935d91414 100644 --- a/IPython/core/interactiveshell.py +++ b/IPython/core/interactiveshell.py @@ -2008,7 +2008,7 @@ def init_magics(self): self.register_magics(m.AutoMagics, m.BasicMagics, m.CodeMagics, m.ConfigMagics, mf.DeprecatedMagics, m.ExecutionMagics, m.ExtensionMagics, m.HistoryMagics, m.LoggingMagics, - m.NamespaceMagics, m.OSMagics, mf.PylabMagics ) + m.NamespaceMagics, m.OSMagics, m.PylabMagics ) # FIXME: Move the color initialization to the DisplayHook, which # should be split into a prompt manager and displayhook. We probably diff --git a/IPython/core/magic_functions.py b/IPython/core/magic_functions.py index d8dfbea4877..3d166c88574 100644 --- a/IPython/core/magic_functions.py +++ b/IPython/core/magic_functions.py @@ -15,80 +15,12 @@ #----------------------------------------------------------------------------- # Our own packages -from IPython.config.application import Application from IPython.core.magic import Magics, register_magics, line_magic -from IPython.testing.skipdoctest import skip_doctest #----------------------------------------------------------------------------- # Magic implementation classes #----------------------------------------------------------------------------- -@register_magics -class PylabMagics(Magics): - """Magics related to matplotlib's pylab support""" - - @skip_doctest - @line_magic - def pylab(self, parameter_s=''): - """Load numpy and matplotlib to work interactively. - - %pylab [GUINAME] - - This function lets you activate pylab (matplotlib, numpy and - interactive support) at any point during an IPython session. - - It will import at the top level numpy as np, pyplot as plt, matplotlib, - pylab and mlab, as well as all names from numpy and pylab. - - If you are using the inline matplotlib backend for embedded figures, - you can adjust its behavior via the %config magic:: - - # enable SVG figures, necessary for SVG+XHTML export in the qtconsole - In [1]: %config InlineBackend.figure_format = 'svg' - - # change the behavior of closing all figures at the end of each - # execution (cell), or allowing reuse of active figures across - # cells: - In [2]: %config InlineBackend.close_figures = False - - Parameters - ---------- - guiname : optional - One of the valid arguments to the %gui magic ('qt', 'wx', 'gtk', - 'osx' or 'tk'). If given, the corresponding Matplotlib backend is - used, otherwise matplotlib's default (which you can override in your - matplotlib config file) is used. - - Examples - -------- - In this case, where the MPL default is TkAgg:: - - In [2]: %pylab - - Welcome to pylab, a matplotlib-based Python environment. - Backend in use: TkAgg - For more information, type 'help(pylab)'. - - But you can explicitly request a different backend:: - - In [3]: %pylab qt - - Welcome to pylab, a matplotlib-based Python environment. - Backend in use: Qt4Agg - For more information, type 'help(pylab)'. - """ - - if Application.initialized(): - app = Application.instance() - try: - import_all_status = app.pylab_import_all - except AttributeError: - import_all_status = True - else: - import_all_status = True - - self.shell.enable_pylab(parameter_s, import_all=import_all_status) - @register_magics class DeprecatedMagics(Magics): diff --git a/IPython/core/magics/__init__.py b/IPython/core/magics/__init__.py index afcf318ba36..15b9cef456c 100644 --- a/IPython/core/magics/__init__.py +++ b/IPython/core/magics/__init__.py @@ -23,6 +23,7 @@ from .logging import LoggingMagics from .namespace import NamespaceMagics from .osm import OSMagics +from .pylab import PylabMagics #----------------------------------------------------------------------------- # Magic implementation classes diff --git a/IPython/core/magics/pylab.py b/IPython/core/magics/pylab.py new file mode 100644 index 00000000000..d64c9298746 --- /dev/null +++ b/IPython/core/magics/pylab.py @@ -0,0 +1,88 @@ +"""Implementation of magic functions for matplotlib/pylab support. +""" +#----------------------------------------------------------------------------- +# Copyright (c) 2012 The IPython Development Team. +# +# Distributed under the terms of the Modified BSD License. +# +# The full license is in the file COPYING.txt, distributed with this software. +#----------------------------------------------------------------------------- + +#----------------------------------------------------------------------------- +# Imports +#----------------------------------------------------------------------------- + +# Our own packages +from IPython.config.application import Application +from IPython.core.magic import Magics, register_magics, line_magic +from IPython.testing.skipdoctest import skip_doctest + +#----------------------------------------------------------------------------- +# Magic implementation classes +#----------------------------------------------------------------------------- + +@register_magics +class PylabMagics(Magics): + """Magics related to matplotlib's pylab support""" + + @skip_doctest + @line_magic + def pylab(self, parameter_s=''): + """Load numpy and matplotlib to work interactively. + + %pylab [GUINAME] + + This function lets you activate pylab (matplotlib, numpy and + interactive support) at any point during an IPython session. + + It will import at the top level numpy as np, pyplot as plt, matplotlib, + pylab and mlab, as well as all names from numpy and pylab. + + If you are using the inline matplotlib backend for embedded figures, + you can adjust its behavior via the %config magic:: + + # enable SVG figures, necessary for SVG+XHTML export in the qtconsole + In [1]: %config InlineBackend.figure_format = 'svg' + + # change the behavior of closing all figures at the end of each + # execution (cell), or allowing reuse of active figures across + # cells: + In [2]: %config InlineBackend.close_figures = False + + Parameters + ---------- + guiname : optional + One of the valid arguments to the %gui magic ('qt', 'wx', 'gtk', + 'osx' or 'tk'). If given, the corresponding Matplotlib backend is + used, otherwise matplotlib's default (which you can override in your + matplotlib config file) is used. + + Examples + -------- + In this case, where the MPL default is TkAgg:: + + In [2]: %pylab + + Welcome to pylab, a matplotlib-based Python environment. + Backend in use: TkAgg + For more information, type 'help(pylab)'. + + But you can explicitly request a different backend:: + + In [3]: %pylab qt + + Welcome to pylab, a matplotlib-based Python environment. + Backend in use: Qt4Agg + For more information, type 'help(pylab)'. + """ + + if Application.initialized(): + app = Application.instance() + try: + import_all_status = app.pylab_import_all + except AttributeError: + import_all_status = True + else: + import_all_status = True + + self.shell.enable_pylab(parameter_s, import_all=import_all_status) From b36afb8c4cd36ad67e40b8f222436c3f2ba00a81 Mon Sep 17 00:00:00 2001 From: Fernando Perez Date: Wed, 23 May 2012 16:30:50 -0700 Subject: [PATCH 064/103] Create core.magics.deprecated according to new API. --- IPython/core/interactiveshell.py | 3 +-- IPython/core/magics/__init__.py | 1 + .../{magic_functions.py => magics/deprecated.py} | 15 ++++++--------- 3 files changed, 8 insertions(+), 11 deletions(-) rename IPython/core/{magic_functions.py => magics/deprecated.py} (82%) diff --git a/IPython/core/interactiveshell.py b/IPython/core/interactiveshell.py index a8935d91414..e39e1fe96db 100644 --- a/IPython/core/interactiveshell.py +++ b/IPython/core/interactiveshell.py @@ -1993,7 +1993,6 @@ def set_completer_frame(self, frame=None): #------------------------------------------------------------------------- def init_magics(self): - from IPython.core import magic_functions as mf from IPython.core import magics as m self.magics_manager = magic.MagicsManager(shell=self, confg=self.config, @@ -2006,7 +2005,7 @@ def init_magics(self): self.define_magic = self.magics_manager.define_magic self.register_magics(m.AutoMagics, m.BasicMagics, m.CodeMagics, - m.ConfigMagics, mf.DeprecatedMagics, m.ExecutionMagics, + m.ConfigMagics, m.DeprecatedMagics, m.ExecutionMagics, m.ExtensionMagics, m.HistoryMagics, m.LoggingMagics, m.NamespaceMagics, m.OSMagics, m.PylabMagics ) diff --git a/IPython/core/magics/__init__.py b/IPython/core/magics/__init__.py index 15b9cef456c..b4b8cac9750 100644 --- a/IPython/core/magics/__init__.py +++ b/IPython/core/magics/__init__.py @@ -17,6 +17,7 @@ from .basic import BasicMagics from .code import CodeMagics, MacroToEdit from .config import ConfigMagics +from .deprecated import DeprecatedMagics from .execution import ExecutionMagics from .extension import ExtensionMagics from .history import HistoryMagics diff --git a/IPython/core/magic_functions.py b/IPython/core/magics/deprecated.py similarity index 82% rename from IPython/core/magic_functions.py rename to IPython/core/magics/deprecated.py index 3d166c88574..4f544c69deb 100644 --- a/IPython/core/magic_functions.py +++ b/IPython/core/magics/deprecated.py @@ -1,13 +1,11 @@ -"""Magic functions for InteractiveShell. +"""Deprecated Magic functions. """ - #----------------------------------------------------------------------------- -# Copyright (C) 2001 Janko Hauser and -# Copyright (C) 2001 Fernando Perez -# Copyright (C) 2008 The IPython Development Team - -# Distributed under the terms of the BSD License. The full license is in -# the file COPYING, distributed as part of this software. +# Copyright (c) 2012 The IPython Development Team. +# +# Distributed under the terms of the Modified BSD License. +# +# The full license is in the file COPYING.txt, distributed with this software. #----------------------------------------------------------------------------- #----------------------------------------------------------------------------- @@ -21,7 +19,6 @@ # Magic implementation classes #----------------------------------------------------------------------------- - @register_magics class DeprecatedMagics(Magics): """Magics slated for later removal.""" From 659282d087ce89223874d0519da431eeec8ec9ff Mon Sep 17 00:00:00 2001 From: Fernando Perez Date: Wed, 23 May 2012 16:37:23 -0700 Subject: [PATCH 065/103] Fix failing tests in core and zmq --- IPython/core/tests/test_magic.py | 4 ++-- IPython/zmq/zmqshell.py | 3 +-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/IPython/core/tests/test_magic.py b/IPython/core/tests/test_magic.py index e8f3ea65f5e..8fa215365af 100644 --- a/IPython/core/tests/test_magic.py +++ b/IPython/core/tests/test_magic.py @@ -17,7 +17,7 @@ import nose.tools as nt from IPython.core import magic -from IPython.core import magic_functions as mf +from IPython.core.magics import execution from IPython.nbformat.v3.tests.nbexamples import nb0 from IPython.nbformat import current from IPython.testing import decorators as dec @@ -429,7 +429,7 @@ def test_timeit_arguments(): _ip.magic("timeit ('#')") -@dec.skipif(mf.profile is None) +@dec.skipif(execution.profile is None) def test_prun_quotes(): "Test that prun does not clobber string escapes (GH #1302)" _ip.magic("prun -q x = '\t'") diff --git a/IPython/zmq/zmqshell.py b/IPython/zmq/zmqshell.py index 518c76c1ee8..7d9a7b7ec15 100644 --- a/IPython/zmq/zmqshell.py +++ b/IPython/zmq/zmqshell.py @@ -33,7 +33,7 @@ from IPython.core.autocall import ZMQExitAutocall from IPython.core.displaypub import DisplayPublisher from IPython.core.macro import Macro -from IPython.core.magic_functions import MacroToEdit +from IPython.core.magics import MacroToEdit from IPython.core.payloadpage import install_payload_page from IPython.lib.kernel import ( get_connection_file, get_connection_info, connect_qtconsole @@ -49,7 +49,6 @@ from IPython.zmq.session import extract_header from session import Session - #----------------------------------------------------------------------------- # Functions and classes #----------------------------------------------------------------------------- From 6854bb5a5d8a49e5d49ea36e907a5d6e007e3233 Mon Sep 17 00:00:00 2001 From: Fernando Perez Date: Wed, 23 May 2012 16:45:44 -0700 Subject: [PATCH 066/103] Fix parallel tests --- IPython/extensions/parallelmagic.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/IPython/extensions/parallelmagic.py b/IPython/extensions/parallelmagic.py index effada2ce5f..f2b910a3bb8 100644 --- a/IPython/extensions/parallelmagic.py +++ b/IPython/extensions/parallelmagic.py @@ -39,7 +39,6 @@ from IPython.core.magic import Magics, register_magics, line_magic from IPython.testing.skipdoctest import skip_doctest -from IPython.utils.traitlets import Instance #----------------------------------------------------------------------------- # Definitions of magic functions for use with IPython @@ -54,7 +53,6 @@ class ParallelMagics(Magics): """A set of magics useful when controlling a parallel IPython cluster. """ - active_view = Instance('IPython.parallel.client.view.DirectView') def __init__(self, shell): super(ParallelMagics, self).__init__(shell) From b7491ba6626b909c9c90682b02963fd5dc55e2f6 Mon Sep 17 00:00:00 2001 From: Fernando Perez Date: Wed, 23 May 2012 17:49:13 -0700 Subject: [PATCH 067/103] Create decorators for standalone magic functions, as per review.x --- IPython/core/magic.py | 50 +++++++++++++++++++++++++++++++++ IPython/core/magics/__init__.py | 2 +- 2 files changed, 51 insertions(+), 1 deletion(-) diff --git a/IPython/core/magic.py b/IPython/core/magic.py index 230175a28ef..d61ea88f650 100644 --- a/IPython/core/magic.py +++ b/IPython/core/magic.py @@ -130,16 +130,66 @@ def mark(func, *a, **kw): else: raise ValueError("Decorator can only be called with " "string or function") + return retval + + return magic_deco + +def _function_magic_marker(magic_type): + validate_type(magic_type) + + # This is a closure to capture the magic_type. We could also use a class, + # but it's overkill for just that one bit of state. + def magic_deco(arg): + call = lambda f, *a, **k: f(*a, **k) + + # Find get_ipython() in the caller's namespace + caller = sys._getframe(1) + for ns in ['f_locals', 'f_globals', 'f_builtins']: + get_ipython = getattr(caller, ns).get('get_ipython') + if get_ipython is not None: + break + else: + raise('Decorator can only run in context where `get_ipython` exists') + + ip = get_ipython() + + if callable(arg): + # "Naked" decorator call (just @foo, no args) + func = arg + #name = func.func_name + #func.magic_name = name + ip.register_magic_function(func) + retval = decorator(call, func) + elif isinstance(arg, basestring): + # Decorator called with arguments (@foo('bar')) + name = arg + def mark(func, *a, **kw): + #func.magic_name = name + ip.register_magic_function(func) + return decorator(call, func) + retval = mark + else: + raise ValueError("Decorator can only be called with " + "string or function") return retval return magic_deco +# Create the actual decorators for public use + +# These three are used to decorate methods in class definitions line_magic = _magic_marker('line') cell_magic = _magic_marker('cell') line_cell_magic = _magic_marker('line_cell') +# These three decorate standalone functions and perform the decoration +# immediately. They can only run where get_ipython() works +register_line_magic = _function_magic_marker('line') +register_cell_magic = _function_magic_marker('cell') +register_line_cell_magic = _function_magic_marker('line_cell') + #----------------------------------------------------------------------------- # Core Magic classes #----------------------------------------------------------------------------- diff --git a/IPython/core/magics/__init__.py b/IPython/core/magics/__init__.py index b4b8cac9750..fa82e20e6db 100644 --- a/IPython/core/magics/__init__.py +++ b/IPython/core/magics/__init__.py @@ -1,7 +1,7 @@ """Implementation of all the magic functions built into IPython. """ #----------------------------------------------------------------------------- -# Copyright (c) 2012, IPython Development Team. +# Copyright (c) 2012 The IPython Development Team. # # Distributed under the terms of the Modified BSD License. # From 1f1d8fa1ae509c8112860526adbabc1727c9c8e1 Mon Sep 17 00:00:00 2001 From: Fernando Perez Date: Wed, 23 May 2012 17:54:18 -0700 Subject: [PATCH 068/103] Renamed @register_magics to @magics_class to avoid confusion. The main ipython object also has a .register_magics method that must often be used in close proximity to the class decorator, yet does something completely different. Having these two objects with the same name yet different purposes was proving to be quite confusing in my testing usage so far. --- IPython/core/magic.py | 8 ++++---- IPython/core/magics/__init__.py | 4 ++-- IPython/core/magics/auto.py | 4 ++-- IPython/core/magics/basic.py | 4 ++-- IPython/core/magics/code.py | 4 ++-- IPython/core/magics/config.py | 4 ++-- IPython/core/magics/deprecated.py | 4 ++-- IPython/core/magics/execution.py | 4 ++-- IPython/core/magics/extension.py | 4 ++-- IPython/core/magics/history.py | 4 ++-- IPython/core/magics/logging.py | 4 ++-- IPython/core/magics/namespace.py | 4 ++-- IPython/core/magics/osm.py | 4 ++-- IPython/core/magics/pylab.py | 4 ++-- IPython/core/tests/test_magic.py | 2 +- IPython/extensions/autoreload.py | 4 ++-- IPython/extensions/parallelmagic.py | 4 ++-- IPython/extensions/storemagic.py | 4 ++-- IPython/frontend/terminal/embed.py | 4 ++-- IPython/frontend/terminal/interactiveshell.py | 4 ++-- 20 files changed, 41 insertions(+), 41 deletions(-) diff --git a/IPython/core/magic.py b/IPython/core/magic.py index d61ea88f650..81734170e07 100644 --- a/IPython/core/magic.py +++ b/IPython/core/magic.py @@ -38,7 +38,7 @@ # A dict we'll use for each class that has magics, used as temporary storage to # pass information between the @line/cell_magic method decorators and the -# @register_magics class decorator, because the method decorators have no +# @magics_class class decorator, because the method decorators have no # access to the class when they run. See for more details: # http://stackoverflow.com/questions/2366713/can-a-python-decorator-of-an-instance-method-access-the-class @@ -82,7 +82,7 @@ def needs_local_scope(func): # Class and method decorators for registering magics #----------------------------------------------------------------------------- -def register_magics(cls): +def magics_class(cls): cls.registered = True cls.magics = dict(line = magics['line'], cell = magics['cell']) @@ -294,7 +294,7 @@ class Magics(object): - Use the method decorators `@line_magic` and `@cell_magic` to decorate individual methods as magic functions, AND - - Use the class decorator `@register_magics` to ensure that the magic + - Use the class decorator `@magics_class` to ensure that the magic methods are properly registered at the instance level upon instance initialization. @@ -312,7 +312,7 @@ class Magics(object): def __init__(self, shell): if not(self.__class__.registered): raise ValueError('Magics subclass without registration - ' - 'did you forget to apply @register_magics?') + 'did you forget to apply @magics_class?') self.shell = shell self.options_table = {} # The method decorators are run when the instance doesn't exist yet, so diff --git a/IPython/core/magics/__init__.py b/IPython/core/magics/__init__.py index fa82e20e6db..23479b5f3e1 100644 --- a/IPython/core/magics/__init__.py +++ b/IPython/core/magics/__init__.py @@ -12,7 +12,7 @@ # Imports #----------------------------------------------------------------------------- -from ..magic import Magics, register_magics +from ..magic import Magics, magics_class from .auto import AutoMagics from .basic import BasicMagics from .code import CodeMagics, MacroToEdit @@ -30,7 +30,7 @@ # Magic implementation classes #----------------------------------------------------------------------------- -@register_magics +@magics_class class UserMagics(Magics): """Placeholder for user-defined magics to be added at runtime. diff --git a/IPython/core/magics/auto.py b/IPython/core/magics/auto.py index bfa5059de58..348d982ef18 100644 --- a/IPython/core/magics/auto.py +++ b/IPython/core/magics/auto.py @@ -13,7 +13,7 @@ #----------------------------------------------------------------------------- # Our own packages -from IPython.core.magic import Bunch, Magics, register_magics, line_magic +from IPython.core.magic import Bunch, Magics, magics_class, line_magic from IPython.testing.skipdoctest import skip_doctest from IPython.utils.warn import error @@ -21,7 +21,7 @@ # Magic implementation classes #----------------------------------------------------------------------------- -@register_magics +@magics_class class AutoMagics(Magics): """Magics that control various autoX behaviors.""" diff --git a/IPython/core/magics/basic.py b/IPython/core/magics/basic.py index 57b5953d2e8..707bbf0d1ed 100644 --- a/IPython/core/magics/basic.py +++ b/IPython/core/magics/basic.py @@ -20,7 +20,7 @@ # Our own packages from IPython.core.error import UsageError -from IPython.core.magic import Magics, register_magics, line_magic +from IPython.core.magic import Magics, magics_class, line_magic from IPython.core.prefilter import ESC_MAGIC from IPython.utils.text import format_screen from IPython.core import magic_arguments, page @@ -33,7 +33,7 @@ # Magics class implementation #----------------------------------------------------------------------------- -@register_magics +@magics_class class BasicMagics(Magics): """Magics that provide central IPython functionality. diff --git a/IPython/core/magics/code.py b/IPython/core/magics/code.py index 7052d58b14f..9bf5e6cb959 100644 --- a/IPython/core/magics/code.py +++ b/IPython/core/magics/code.py @@ -23,7 +23,7 @@ # Our own packages from IPython.core.error import TryNext from IPython.core.macro import Macro -from IPython.core.magic import Magics, register_magics, line_magic +from IPython.core.magic import Magics, magics_class, line_magic from IPython.testing.skipdoctest import skip_doctest from IPython.utils import openpy from IPython.utils import py3compat @@ -39,7 +39,7 @@ class MacroToEdit(ValueError): pass -@register_magics +@magics_class class CodeMagics(Magics): """Magics related to code management (loading, saving, editing, ...).""" diff --git a/IPython/core/magics/config.py b/IPython/core/magics/config.py index 204e8811a34..5480d129016 100644 --- a/IPython/core/magics/config.py +++ b/IPython/core/magics/config.py @@ -17,14 +17,14 @@ # Our own packages from IPython.core.error import UsageError -from IPython.core.magic import Magics, register_magics, line_magic +from IPython.core.magic import Magics, magics_class, line_magic from IPython.utils.warn import error #----------------------------------------------------------------------------- # Magic implementation classes #----------------------------------------------------------------------------- -@register_magics +@magics_class class ConfigMagics(Magics): def __init__(self, shell): diff --git a/IPython/core/magics/deprecated.py b/IPython/core/magics/deprecated.py index 4f544c69deb..254b101c934 100644 --- a/IPython/core/magics/deprecated.py +++ b/IPython/core/magics/deprecated.py @@ -13,13 +13,13 @@ #----------------------------------------------------------------------------- # Our own packages -from IPython.core.magic import Magics, register_magics, line_magic +from IPython.core.magic import Magics, magics_class, line_magic #----------------------------------------------------------------------------- # Magic implementation classes #----------------------------------------------------------------------------- -@register_magics +@magics_class class DeprecatedMagics(Magics): """Magics slated for later removal.""" diff --git a/IPython/core/magics/execution.py b/IPython/core/magics/execution.py index 30a1a8f3afc..7ddf6db82c4 100644 --- a/IPython/core/magics/execution.py +++ b/IPython/core/magics/execution.py @@ -36,7 +36,7 @@ from IPython.core import page from IPython.core.error import UsageError from IPython.core.macro import Macro -from IPython.core.magic import (Magics, register_magics, line_magic, +from IPython.core.magic import (Magics, magics_class, line_magic, on_off, needs_local_scope) from IPython.testing.skipdoctest import skip_doctest from IPython.utils import py3compat @@ -50,7 +50,7 @@ # Magic implementation classes #----------------------------------------------------------------------------- -@register_magics +@magics_class class ExecutionMagics(Magics): """Magics related to code execution, debugging, profiling, etc. diff --git a/IPython/core/magics/extension.py b/IPython/core/magics/extension.py index 9425eff4776..37982e54fab 100644 --- a/IPython/core/magics/extension.py +++ b/IPython/core/magics/extension.py @@ -16,13 +16,13 @@ import os # Our own packages -from IPython.core.magic import Magics, register_magics, line_magic +from IPython.core.magic import Magics, magics_class, line_magic #----------------------------------------------------------------------------- # Magic implementation classes #----------------------------------------------------------------------------- -@register_magics +@magics_class class ExtensionMagics(Magics): """Magics to manage the IPython extensions system.""" diff --git a/IPython/core/magics/history.py b/IPython/core/magics/history.py index 4516582e1ea..594824dad83 100644 --- a/IPython/core/magics/history.py +++ b/IPython/core/magics/history.py @@ -19,7 +19,7 @@ # Our own packages from IPython.core.error import StdinNotImplementedError -from IPython.core.magic import Magics, register_magics, line_magic +from IPython.core.magic import Magics, magics_class, line_magic from IPython.testing.skipdoctest import skip_doctest from IPython.utils import io @@ -27,7 +27,7 @@ # Magics class implementation #----------------------------------------------------------------------------- -@register_magics +@magics_class class HistoryMagics(Magics): @skip_doctest diff --git a/IPython/core/magics/logging.py b/IPython/core/magics/logging.py index f8606fb6a1e..23b55677433 100644 --- a/IPython/core/magics/logging.py +++ b/IPython/core/magics/logging.py @@ -17,14 +17,14 @@ import sys # Our own packages -from IPython.core.magic import Magics, register_magics, line_magic +from IPython.core.magic import Magics, magics_class, line_magic from IPython.utils.warn import warn #----------------------------------------------------------------------------- # Magic implementation classes #----------------------------------------------------------------------------- -@register_magics +@magics_class class LoggingMagics(Magics): """Magics related to all logging machinery.""" diff --git a/IPython/core/magics/namespace.py b/IPython/core/magics/namespace.py index e20d0de6ce1..08e0dc946fb 100644 --- a/IPython/core/magics/namespace.py +++ b/IPython/core/magics/namespace.py @@ -20,7 +20,7 @@ # Our own packages from IPython.core import page from IPython.core.error import StdinNotImplementedError -from IPython.core.magic import Magics, register_magics, line_magic +from IPython.core.magic import Magics, magics_class, line_magic from IPython.testing.skipdoctest import skip_doctest from IPython.utils.encoding import DEFAULT_ENCODING from IPython.utils.path import get_py_filename @@ -29,7 +29,7 @@ # Magic implementation classes #----------------------------------------------------------------------------- -@register_magics +@magics_class class NamespaceMagics(Magics): """Magics to manage various aspects of the user's namespace. diff --git a/IPython/core/magics/osm.py b/IPython/core/magics/osm.py index f4eae8c4a8a..0a358b997a9 100644 --- a/IPython/core/magics/osm.py +++ b/IPython/core/magics/osm.py @@ -25,7 +25,7 @@ from IPython.core import oinspect from IPython.core import page from IPython.core.error import UsageError -from IPython.core.magic import (Magics, compress_dhist, register_magics, +from IPython.core.magic import (Magics, compress_dhist, magics_class, line_magic) from IPython.testing.skipdoctest import skip_doctest from IPython.utils.io import file_read, nlprint @@ -35,7 +35,7 @@ #----------------------------------------------------------------------------- # Magic implementation classes #----------------------------------------------------------------------------- -@register_magics +@magics_class class OSMagics(Magics): """Magics to interact with the underlying OS (shell-type functionality). """ diff --git a/IPython/core/magics/pylab.py b/IPython/core/magics/pylab.py index d64c9298746..f07b717e712 100644 --- a/IPython/core/magics/pylab.py +++ b/IPython/core/magics/pylab.py @@ -14,14 +14,14 @@ # Our own packages from IPython.config.application import Application -from IPython.core.magic import Magics, register_magics, line_magic +from IPython.core.magic import Magics, magics_class, line_magic from IPython.testing.skipdoctest import skip_doctest #----------------------------------------------------------------------------- # Magic implementation classes #----------------------------------------------------------------------------- -@register_magics +@magics_class class PylabMagics(Magics): """Magics related to matplotlib's pylab support""" diff --git a/IPython/core/tests/test_magic.py b/IPython/core/tests/test_magic.py index 8fa215365af..d664931ff9d 100644 --- a/IPython/core/tests/test_magic.py +++ b/IPython/core/tests/test_magic.py @@ -29,7 +29,7 @@ # Test functions begin #----------------------------------------------------------------------------- -@magic.register_magics +@magic.magics_class class DummyMagics(magic.Magics): pass def test_rehashx(): diff --git a/IPython/extensions/autoreload.py b/IPython/extensions/autoreload.py index 75b71c6ad35..6ca46356db6 100644 --- a/IPython/extensions/autoreload.py +++ b/IPython/extensions/autoreload.py @@ -406,10 +406,10 @@ def superreload(module, reload=reload, old_objects={}): #------------------------------------------------------------------------------ from IPython.core.hooks import TryNext -from IPython.core.magic import Magics, register_magics, line_magic +from IPython.core.magic import Magics, magics_class, line_magic from IPython.core.plugin import Plugin -@register_magics +@magics_class class AutoreloadMagics(Magics): def __init__(self, *a, **kw): super(AutoreloadMagics, self).__init__(*a, **kw) diff --git a/IPython/extensions/parallelmagic.py b/IPython/extensions/parallelmagic.py index f2b910a3bb8..abb49fb7c96 100644 --- a/IPython/extensions/parallelmagic.py +++ b/IPython/extensions/parallelmagic.py @@ -37,7 +37,7 @@ import ast import re -from IPython.core.magic import Magics, register_magics, line_magic +from IPython.core.magic import Magics, magics_class, line_magic from IPython.testing.skipdoctest import skip_doctest #----------------------------------------------------------------------------- @@ -49,7 +49,7 @@ """ -@register_magics +@magics_class class ParallelMagics(Magics): """A set of magics useful when controlling a parallel IPython cluster. """ diff --git a/IPython/extensions/storemagic.py b/IPython/extensions/storemagic.py index 607d2ab6bfe..20cb84d5ba8 100644 --- a/IPython/extensions/storemagic.py +++ b/IPython/extensions/storemagic.py @@ -14,7 +14,7 @@ from IPython.core.error import UsageError from IPython.core.fakemodule import FakeModule -from IPython.core.magic import Magics, register_magics, line_magic +from IPython.core.magic import Magics, magics_class, line_magic from IPython.core.plugin import Plugin from IPython.testing.skipdoctest import skip_doctest from IPython.utils.traitlets import Bool, Instance @@ -53,7 +53,7 @@ def restore_data(ip): restore_dhist(ip) -@register_magics +@magics_class class StoreMagics(Magics): """Lightweight persistence for python variables. diff --git a/IPython/frontend/terminal/embed.py b/IPython/frontend/terminal/embed.py index 5cdead6bdc3..f1430a7832a 100644 --- a/IPython/frontend/terminal/embed.py +++ b/IPython/frontend/terminal/embed.py @@ -29,7 +29,7 @@ import warnings from IPython.core import ultratb -from IPython.core.magic import Magics, register_magics, line_magic +from IPython.core.magic import Magics, magics_class, line_magic from IPython.frontend.terminal.interactiveshell import TerminalInteractiveShell from IPython.frontend.terminal.ipapp import load_default_config @@ -42,7 +42,7 @@ #----------------------------------------------------------------------------- # This is an additional magic that is exposed in embedded shells. -@register_magics +@magics_class class EmbeddedMagics(Magics): @line_magic diff --git a/IPython/frontend/terminal/interactiveshell.py b/IPython/frontend/terminal/interactiveshell.py index 7b45eb17b2a..1086eddcb45 100644 --- a/IPython/frontend/terminal/interactiveshell.py +++ b/IPython/frontend/terminal/interactiveshell.py @@ -25,7 +25,7 @@ from IPython.core.error import TryNext, UsageError from IPython.core.usage import interactive_usage, default_banner from IPython.core.interactiveshell import InteractiveShell, InteractiveShellABC -from IPython.core.magic import Magics, register_magics, line_magic +from IPython.core.magic import Magics, magics_class, line_magic from IPython.testing.skipdoctest import skip_doctest from IPython.utils.encoding import get_stream_enc from IPython.utils import py3compat @@ -120,7 +120,7 @@ def rerun_pasted(shell, name='pasted_block'): # Terminal-specific magics #------------------------------------------------------------------------ -@register_magics +@magics_class class TerminalMagics(Magics): @line_magic From 3f192dc6a1873b0d2e5345fc475ffab0fa403466 Mon Sep 17 00:00:00 2001 From: Fernando Perez Date: Wed, 23 May 2012 21:29:45 -0700 Subject: [PATCH 069/103] Remove next_input nonsense in magic calls (but keep functionality). --- IPython/core/inputsplitter.py | 12 ++++--- IPython/core/interactiveshell.py | 18 +++------- IPython/core/magic.py | 44 +++++++++++------------- IPython/core/tests/test_inputsplitter.py | 12 ++++--- IPython/core/tests/test_magic.py | 5 +++ 5 files changed, 47 insertions(+), 44 deletions(-) diff --git a/IPython/core/inputsplitter.py b/IPython/core/inputsplitter.py index f2e148a0189..146104e5a96 100644 --- a/IPython/core/inputsplitter.py +++ b/IPython/core/inputsplitter.py @@ -559,19 +559,23 @@ def _make_help_call(target, esc, lspace, next_input=None): else 'pinfo' arg = " ".join([method, target]) - if next_input: - tpl = '%sget_ipython().magic(%r, next_input=%r)' - return tpl % (lspace, arg, next_input) - else: + if next_input is None: return '%sget_ipython().magic(%r)' % (lspace, arg) + else: + return '%sget_ipython().set_next_input(%r);get_ipython().magic(%r)' % \ + (lspace, next_input, arg) + _initial_space_re = re.compile(r'\s*') + _help_end_re = re.compile(r"""(%? [a-zA-Z_*][\w*]* # Variable name (\.[a-zA-Z_*][\w*]*)* # .etc.etc ) (\?\??)$ # ? or ??""", re.VERBOSE) + + def transform_help_end(line): """Translate lines with ?/?? at the end""" m = _help_end_re.search(line) diff --git a/IPython/core/interactiveshell.py b/IPython/core/interactiveshell.py index e39e1fe96db..e1c53559c96 100644 --- a/IPython/core/interactiveshell.py +++ b/IPython/core/interactiveshell.py @@ -2014,7 +2014,7 @@ def init_magics(self): # even need a centralize colors management object. self.magic('colors %s' % self.colors) - def line_magic(self, magic_name, line, next_input=None): + def line_magic(self, magic_name, line): """Execute the given line magic. Parameters @@ -2024,15 +2024,7 @@ def line_magic(self, magic_name, line, next_input=None): line : str The rest of the input line as a single string. - - next_input : str, optional - Text to pre-load into the next input line. """ - # Allow setting the next input - this is used if the user does `a=abs?`. - # We do this first so that magic functions can override it. - if next_input: - self.set_next_input(next_input) - fn = self.find_line_magic(magic_name) if fn is None: error("Magic function `%s` not found." % magic_name) @@ -2079,13 +2071,13 @@ def find_cell_magic(self, magic_name): Returns None if the magic isn't found.""" return self.magics_manager.magics['cell'].get(magic_name) - def find_magic(self, magic_name, magic_type='line'): + def find_magic(self, magic_name, magic_kind='line'): """Find and return a magic of the given type by name. Returns None if the magic isn't found.""" - return self.magics_manager.magics[magic_type].get(magic_name) + return self.magics_manager.magics[magic_kind].get(magic_name) - def magic(self, arg_s, next_input=None): + def magic(self, arg_s): """DEPRECATED. Use line_magic() instead. Call a magic function by name. @@ -2107,7 +2099,7 @@ def magic(self, arg_s, next_input=None): # TODO: should we issue a loud deprecation warning here? magic_name, _, magic_arg_s = arg_s.partition(' ') magic_name = magic_name.lstrip(prefilter.ESC_MAGIC) - return self.line_magic(magic_name, magic_arg_s, next_input) + return self.line_magic(magic_name, magic_arg_s) #------------------------------------------------------------------------- # Things related to macros diff --git a/IPython/core/magic.py b/IPython/core/magic.py index 81734170e07..a87abf41d69 100644 --- a/IPython/core/magic.py +++ b/IPython/core/magic.py @@ -44,7 +44,7 @@ magics = dict(line={}, cell={}) -magic_types = ('line', 'cell') +magic_kinds = ('line', 'cell') magic_spec = ('line', 'cell', 'line_cell') #----------------------------------------------------------------------------- @@ -98,16 +98,16 @@ def record_magic(dct, mtype, mname, func): dct[mtype][mname] = func -def validate_type(magic_type): - if magic_type not in magic_spec: - raise ValueError('magic_type must be one of %s, %s given' % - magic_types, magic_type) +def validate_type(magic_kind): + if magic_kind not in magic_spec: + raise ValueError('magic_kind must be one of %s, %s given' % + magic_kinds, magic_kind) -def _magic_marker(magic_type): - validate_type(magic_type) +def _magic_marker(magic_kind): + validate_type(magic_kind) - # This is a closure to capture the magic_type. We could also use a class, + # This is a closure to capture the magic_kind. We could also use a class, # but it's overkill for just that one bit of state. def magic_deco(arg): call = lambda f, *a, **k: f(*a, **k) @@ -118,13 +118,13 @@ def magic_deco(arg): name = func.func_name func.magic_name = name retval = decorator(call, func) - record_magic(magics, magic_type, name, name) + record_magic(magics, magic_kind, name, name) elif isinstance(arg, basestring): # Decorator called with arguments (@foo('bar')) name = arg def mark(func, *a, **kw): func.magic_name = name - record_magic(magics, magic_type, name, func.func_name) + record_magic(magics, magic_kind, name, func.func_name) return decorator(call, func) retval = mark else: @@ -135,10 +135,10 @@ def mark(func, *a, **kw): return magic_deco -def _function_magic_marker(magic_type): - validate_type(magic_type) +def _function_magic_marker(magic_kind): + validate_type(magic_kind) - # This is a closure to capture the magic_type. We could also use a class, + # This is a closure to capture the magic_kind. We could also use a class, # but it's overkill for just that one bit of state. def magic_deco(arg): call = lambda f, *a, **k: f(*a, **k) @@ -157,16 +157,14 @@ def magic_deco(arg): if callable(arg): # "Naked" decorator call (just @foo, no args) func = arg - #name = func.func_name - #func.magic_name = name - ip.register_magic_function(func) + name = func.func_name + ip.register_magic_function(func, magic_kind, name) retval = decorator(call, func) elif isinstance(arg, basestring): # Decorator called with arguments (@foo('bar')) name = arg def mark(func, *a, **kw): - #func.magic_name = name - ip.register_magic_function(func) + ip.register_magic_function(func, magic_kind, name) return decorator(call, func) retval = mark else: @@ -254,19 +252,19 @@ def register(self, *magic_objects): # Now that we have an instance, we can register it and update the # table of callables self.registry[m.__class__.__name__] = m - for mtype in magic_types: + for mtype in magic_kinds: self.magics[mtype].update(m.magics[mtype]) - def register_function(self, func, magic_type='line', magic_name=None): + def register_function(self, func, magic_kind='line', magic_name=None): """Expose a standalone function as magic function for ipython. """ # Create the new method in the user_magics and register it in the # global table - validate_type(magic_type) + validate_type(magic_kind) magic_name = func.func_name if magic_name is None else magic_name setattr(self.user_magics, magic_name, func) - record_magic(self.magics, magic_type, magic_name, func) + record_magic(self.magics, magic_kind, magic_name, func) def define_magic(self, name, func): """Support for deprecated API. @@ -320,7 +318,7 @@ def __init__(self, shell): # grab. Only now, that the instance exists, can we create the proper # mapping to bound methods. So we read the info off the original names # table and replace each method name by the actual bound method. - for mtype in magic_types: + for mtype in magic_kinds: tab = self.magics[mtype] # must explicitly use keys, as we're mutating this puppy for magic_name in tab.keys(): diff --git a/IPython/core/tests/test_inputsplitter.py b/IPython/core/tests/test_inputsplitter.py index 73e4dba3a12..0d244b97b02 100644 --- a/IPython/core/tests/test_inputsplitter.py +++ b/IPython/core/tests/test_inputsplitter.py @@ -466,10 +466,14 @@ def transform_checker(tests, func): (u'%hist?', "get_ipython().magic({u}'pinfo %hist')"), (u'f*?', "get_ipython().magic({u}'psearch f*')"), (u'ax.*aspe*?', "get_ipython().magic({u}'psearch ax.*aspe*')"), - (u'a = abc?', "get_ipython().magic({u}'pinfo abc', next_input={u}'a = abc')"), - (u'a = abc.qe??', "get_ipython().magic({u}'pinfo2 abc.qe', next_input={u}'a = abc.qe')"), - (u'a = *.items?', "get_ipython().magic({u}'psearch *.items', next_input={u}'a = *.items')"), - (u'plot(a?', "get_ipython().magic({u}'pinfo a', next_input={u}'plot(a')"), + (u'a = abc?', "get_ipython().set_next_input({u}'a = abc');" + "get_ipython().magic({u}'pinfo abc')"), + (u'a = abc.qe??', "get_ipython().set_next_input({u}'a = abc.qe');" + "get_ipython().magic({u}'pinfo2 abc.qe')"), + (u'a = *.items?', "get_ipython().set_next_input({u}'a = *.items');" + "get_ipython().magic({u}'psearch *.items')"), + (u'plot(a?', "get_ipython().set_next_input({u}'plot(a');" + "get_ipython().magic({u}'pinfo a')"), (u'a*2 #comment?', 'a*2 #comment?'), ]], diff --git a/IPython/core/tests/test_magic.py b/IPython/core/tests/test_magic.py index d664931ff9d..bc17c765d51 100644 --- a/IPython/core/tests/test_magic.py +++ b/IPython/core/tests/test_magic.py @@ -13,10 +13,15 @@ import os import sys from StringIO import StringIO +from unittest import TestCase import nose.tools as nt from IPython.core import magic +from IPython.core.magic import (Magics, magics_class, line_magic, + cell_magic, line_cell_magic, + register_line_magic, register_cell_magic, + register_line_cell_magic) from IPython.core.magics import execution from IPython.nbformat.v3.tests.nbexamples import nb0 from IPython.nbformat import current From df87cbec40fb3c2c4efd1274db86800d33017a9b Mon Sep 17 00:00:00 2001 From: Fernando Perez Date: Wed, 23 May 2012 21:30:10 -0700 Subject: [PATCH 070/103] Add first set of cell magic tests. --- IPython/core/tests/test_magic.py | 45 ++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/IPython/core/tests/test_magic.py b/IPython/core/tests/test_magic.py index bc17c765d51..5d51638d53c 100644 --- a/IPython/core/tests/test_magic.py +++ b/IPython/core/tests/test_magic.py @@ -490,3 +490,48 @@ def test_notebook_reformat_json(): def test_env(): env = _ip.magic("env") assert isinstance(env, dict), type(env) + + +class CellMagicTestCase(TestCase): + + def check_ident(self, magic): + out = _ip.cell_magic(magic, 'a', 'b') + nt.assert_equals(out, ('a','b')) + out = _ip.run_cell('%%' + magic +' a\nb') + nt.assert_equals(out, ('a','b')) + + def test_cell_magic_func_deco(self): + "Cell magic using simple decorator" + @register_cell_magic + def cellm(line, cell): + return line, cell + + self.check_ident('cellm') + + def test_cell_magic_reg(self): + "Cell magic manually registered" + def cellm(line, cell): + return line, cell + + _ip.register_magic_function(cellm, 'cell', 'cellm2') + self.check_ident('cellm2') + + def test_cell_magic_class(self): + "Cell magics declared via a class" + @magics_class + class MyMagics(Magics): + + @cell_magic + def cellm3(self, line, cell): + return line, cell + + @cell_magic('cellm4') + def cellm33(self, line, cell): + return line, cell + + _ip.register_magics(MyMagics) + self.check_ident('cellm3') + self.check_ident('cellm4') + # Check that nothing is registered as 'cellm33' + c33 = _ip.find_cell_magic('cellm33') + nt.assert_equals(c33, None) From 1a856e1b905b271c497e8e292889230b4edfaba3 Mon Sep 17 00:00:00 2001 From: Fernando Perez Date: Thu, 24 May 2012 00:13:07 -0700 Subject: [PATCH 071/103] First implementation of cell magics that goes via inputsplitter. The code is still ugly and probably somewhat fragile, but the basic idea is there. I need to clean things up and test in the qt console and terminal, where things aren't probably quite right yet. --- IPython/core/inputsplitter.py | 43 +++++++++++++++++++++++++++++--- IPython/core/interactiveshell.py | 20 ++++++++++----- IPython/core/tests/test_magic.py | 19 ++++++++++---- 3 files changed, 68 insertions(+), 14 deletions(-) diff --git a/IPython/core/inputsplitter.py b/IPython/core/inputsplitter.py index 146104e5a96..67403cebb02 100644 --- a/IPython/core/inputsplitter.py +++ b/IPython/core/inputsplitter.py @@ -55,7 +55,7 @@ * Brian Granger """ #----------------------------------------------------------------------------- -# Copyright (C) 2010-2011 The IPython Development Team +# Copyright (C) 2010 The IPython Development Team # # Distributed under the terms of the BSD License. The full license is in # the file COPYING, distributed as part of this software. @@ -685,20 +685,23 @@ class IPythonInputSplitter(InputSplitter): # String with raw, untransformed input. source_raw = '' + cell_magic_body = None + # Private attributes # List with lines of raw input accumulated so far. _buffer_raw = None def __init__(self, input_mode=None): - InputSplitter.__init__(self, input_mode) + super(IPythonInputSplitter, self).__init__(input_mode) self._buffer_raw = [] def reset(self): """Reset the input buffer and associated state.""" - InputSplitter.reset(self) + super(IPythonInputSplitter, self).reset() self._buffer_raw[:] = [] self.source_raw = '' + self.cell_magic_body = None def source_raw_reset(self): """Return input and raw source and perform a full reset. @@ -710,6 +713,26 @@ def source_raw_reset(self): def push(self, lines): """Push one or more lines of IPython input. + + This stores the given lines and returns a status code indicating + whether the code forms a complete Python block or not, after processing + all input lines for special IPython syntax. + + Any exceptions generated in compilation are swallowed, but if an + exception was produced, the method returns True. + + Parameters + ---------- + lines : string + One or more lines of Python input. + + Returns + ------- + is_complete : boolean + True if the current input source (the result of the current input + plus prior inputs) forms a complete Python execution block. Note that + this value is also stored as a private attribute (_is_complete), so it + can be queried at any time. """ if not lines: return super(IPythonInputSplitter, self).push(lines) @@ -717,6 +740,20 @@ def push(self, lines): # We must ensure all input is pure unicode lines = cast_unicode(lines, self.encoding) + # cell magic support + #print('IM:', self.input_mode,'\n'+lines); print('---') # dbg + #if self.input_mode == 'cell' and lines.startswith('%%'): + if lines.startswith('%%'): + # Cell magics bypass all further transformations + self.reset() + self._is_complete = is_complete = True + first, _, body = lines.partition('\n') + magic_name, _, line = first.partition(' ') + magic_name = magic_name.lstrip(ESC_MAGIC) + self.cell_magic_body = body + tpl = 'get_ipython()._cell_magic(%r, %r)' + lines = tpl % (magic_name, line) + lines_list = lines.splitlines() transforms = [transform_ipy_prompt, transform_classic_prompt, diff --git a/IPython/core/interactiveshell.py b/IPython/core/interactiveshell.py index e1c53559c96..0f96b3aabae 100644 --- a/IPython/core/interactiveshell.py +++ b/IPython/core/interactiveshell.py @@ -2027,7 +2027,7 @@ def line_magic(self, magic_name, line): """ fn = self.find_line_magic(magic_name) if fn is None: - error("Magic function `%s` not found." % magic_name) + error("Line magic function `%%%s` not found." % magic_name) else: # Note: this is the distance in the stack to the user's frame. # This will need to be updated if the internal calling logic gets @@ -2048,7 +2048,7 @@ def cell_magic(self, magic_name, line, cell): """ fn = self.find_cell_magic(magic_name) if fn is None: - error("Magic function `%s` not found." % magic_name) + error("Cell magic function `%%%%%s` not found." % magic_name) else: # Note: this is the distance in the stack to the user's frame. # This will need to be updated if the internal calling logic gets @@ -2475,6 +2475,11 @@ def call_cell_magic(self, raw_cell, store_history=False): magic_name = magic_name.lstrip(prefilter.ESC_MAGIC) return self.cell_magic(magic_name, line, cell) + def _cell_magic(self, magic_name, line): + cell = self._current_cell_magic_body + self._current_cell_magic_body = None + return self.cell_magic(magic_name, line, cell) + def run_cell(self, raw_cell, store_history=False, silent=False): """Run a complete IPython cell. @@ -2496,11 +2501,14 @@ def run_cell(self, raw_cell, store_history=False, silent=False): if silent: store_history = False - if raw_cell.startswith('%%'): - return self.call_cell_magic(raw_cell, store_history) + self.input_splitter.push(raw_cell) - for line in raw_cell.splitlines(): - self.input_splitter.push(line) + # Check for cell magics, which leave state behind. This interface is + # ugly, we need to do something cleaner later... Now the logic is + # simply that the input_splitter remembers if there was a cell magic, + # and in that case we grab the cell body. + if self.input_splitter.cell_magic_body is not None: + self._current_cell_magic_body = self.input_splitter.cell_magic_body cell = self.input_splitter.source_reset() with self.builtin_trap: diff --git a/IPython/core/tests/test_magic.py b/IPython/core/tests/test_magic.py index 5d51638d53c..362eb89746c 100644 --- a/IPython/core/tests/test_magic.py +++ b/IPython/core/tests/test_magic.py @@ -495,10 +495,12 @@ def test_env(): class CellMagicTestCase(TestCase): def check_ident(self, magic): + # Manually called, we get the result out = _ip.cell_magic(magic, 'a', 'b') nt.assert_equals(out, ('a','b')) - out = _ip.run_cell('%%' + magic +' a\nb') - nt.assert_equals(out, ('a','b')) + # Via run_cell, it goes into the user's namespace via displayhook + _ip.run_cell('%%' + magic +' c\nd') + nt.assert_equals(_ip.user_ns['_'], ('c','d')) def test_cell_magic_func_deco(self): "Cell magic using simple decorator" @@ -525,12 +527,19 @@ class MyMagics(Magics): def cellm3(self, line, cell): return line, cell + _ip.register_magics(MyMagics) + self.check_ident('cellm3') + + def test_cell_magic_class2(self): + "Cell magics declared via a class, #2" + @magics_class + class MyMagics2(Magics): + @cell_magic('cellm4') def cellm33(self, line, cell): return line, cell - - _ip.register_magics(MyMagics) - self.check_ident('cellm3') + + _ip.register_magics(MyMagics2) self.check_ident('cellm4') # Check that nothing is registered as 'cellm33' c33 = _ip.find_cell_magic('cellm33') From 089a60159ce5f50dee8cc756534ec301743aebec Mon Sep 17 00:00:00 2001 From: Fernando Perez Date: Thu, 24 May 2012 14:56:00 -0700 Subject: [PATCH 072/103] Remove stray code and update to use 'in' instead of 'has_key'. --- IPython/core/magics/osm.py | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/IPython/core/magics/osm.py b/IPython/core/magics/osm.py index 0a358b997a9..4280476ec20 100644 --- a/IPython/core/magics/osm.py +++ b/IPython/core/magics/osm.py @@ -261,8 +261,6 @@ def cd(self, parameter_s=''): /home/tsuser/parent/child """ - #bkms = self.shell.persist.get("bookmarks",{}) - oldcwd = os.getcwdu() numcd = re.match(r'(-)(\d+)$',parameter_s) # jump in directory history by number @@ -313,15 +311,15 @@ def cd(self, parameter_s=''): raise UsageError('%cd -: No previous directory to change to.') # jump to bookmark if needed else: - if not os.path.isdir(ps) or opts.has_key('b'): + if not os.path.isdir(ps) or 'b' in opts: bkms = self.shell.db.get('bookmarks', {}) - if bkms.has_key(ps): + if ps in bkms: target = bkms[ps] print '(bookmark:%s) -> %s' % (ps,target) ps = target else: - if opts.has_key('b'): + if 'b' in opts: raise UsageError("Bookmark '%s' not found. " "Use '%%bookmark -l' to see your bookmarks." % ps) @@ -540,7 +538,7 @@ def sc(self, parameter_s=''): # If all looks ok, proceed split = 'l' in opts out = self.shell.getoutput(cmd, split=split) - if opts.has_key('v'): + if 'v' in opts: print '%s ==\n%s' % (var,pformat(out)) if var: self.shell.user_ns.update({var:out}) @@ -619,7 +617,7 @@ def bookmark(self, parameter_s=''): bkms = self.shell.db.get('bookmarks',{}) - if opts.has_key('d'): + if 'd' in opts: try: todel = args[0] except IndexError: @@ -632,19 +630,19 @@ def bookmark(self, parameter_s=''): raise UsageError( "%%bookmark -d: Can't delete bookmark '%s'" % todel) - elif opts.has_key('r'): + elif 'r' in opts: bkms = {} - elif opts.has_key('l'): + elif 'l' in opts: bks = bkms.keys() bks.sort() if bks: - size = max(map(len,bks)) + size = max(map(len, bks)) else: size = 0 fmt = '%-'+str(size)+'s -> %s' print 'Current bookmarks:' for bk in bks: - print fmt % (bk,bkms[bk]) + print fmt % (bk, bkms[bk]) else: if not args: raise UsageError("%bookmark: You must specify the bookmark name") From 720ddccce45247230e7721f9401a8e847b6868c5 Mon Sep 17 00:00:00 2001 From: Fernando Perez Date: Thu, 24 May 2012 23:28:00 -0700 Subject: [PATCH 073/103] First working version of cell magics in inputsplitter in line mode. Cell mode still to do. --- IPython/core/inputsplitter.py | 138 ++++++++++++++++++++--- IPython/core/interactiveshell.py | 20 ++-- IPython/core/tests/test_inputsplitter.py | 35 ++++++ 3 files changed, 166 insertions(+), 27 deletions(-) diff --git a/IPython/core/inputsplitter.py b/IPython/core/inputsplitter.py index 67403cebb02..e2c51fde7b4 100644 --- a/IPython/core/inputsplitter.py +++ b/IPython/core/inputsplitter.py @@ -142,6 +142,21 @@ def num_ini_spaces(s): return 0 +def last_blank(src): + """Determine if the input source ends in a blank. + + A blank is either a newline or a line consisting of whitespace. + + Parameters + ---------- + src : string + A single or multiline string. + """ + if not src: return False + ll = src.splitlines()[-1] + return (ll == '') or ll.isspace() + + def remove_comments(src): """Remove all comments from input source. @@ -685,23 +700,27 @@ class IPythonInputSplitter(InputSplitter): # String with raw, untransformed input. source_raw = '' - cell_magic_body = None + cell_magic_parts = [] + + cell_magic_mode = False # Private attributes - + # List with lines of raw input accumulated so far. _buffer_raw = None def __init__(self, input_mode=None): super(IPythonInputSplitter, self).__init__(input_mode) self._buffer_raw = [] + self._validate = True def reset(self): """Reset the input buffer and associated state.""" super(IPythonInputSplitter, self).reset() self._buffer_raw[:] = [] self.source_raw = '' - self.cell_magic_body = None + self.cell_magic_parts = [] + self.cell_magic_mode = False def source_raw_reset(self): """Return input and raw source and perform a full reset. @@ -711,6 +730,96 @@ def source_raw_reset(self): self.reset() return out, out_r + def push_accepts_more(self): + if self.cell_magic_mode: + return not self._is_complete + else: + return super(IPythonInputSplitter, self).push_accepts_more() + + def _push_line_mode(self, lines): + """Push in line mode. + + This means that we only get individual 'lines' with each call, though + in practice each input may be multiline. But this is in contrast to + cell mode, which feeds the entirety of the cell from the start with + each call. + """ + # cell magic support + #print('#'*10) + #print(lines+'\n---') # dbg + #print (repr(lines)+'\n+++') + #print('raw', self._buffer_raw, 'validate', self.cell_magic_mode) + # Only trigger this block if we're at a 'fresh' pumping start. + if lines.startswith('%%') and (not self.cell_magic_mode) and \ + not self._buffer_raw: + # Cell magics bypass all further transformations + self.cell_magic_mode = True + first, _, body = lines.partition('\n') + magic_name, _, line = first.partition(' ') + magic_name = magic_name.lstrip(ESC_MAGIC) + # We store the body of the cell and create a call to a method that + # will use this stored value. This is ugly, but it's a first cut to + # get it all working, as right now changing the return API of our + # methods would require major refactoring. + self.cell_magic_parts = [body] + tpl = 'get_ipython()._cell_magic(%r, %r)' + tlines = tpl % (magic_name, line) + self._store(tlines) + self._store(lines, self._buffer_raw, 'source_raw') + self._is_complete = False + return False + + if self.cell_magic_mode: + # Find out if the last stored block has a whitespace line as its + # last line and also this line is whitespace, case in which we're + # done (two contiguous blank lines signal termination). Note that + # the storage logic *enforces* that every stored block is + # newline-terminated, so we grab everything but the last character + # so we can have the body of the block alone. + last_block = self.cell_magic_parts[-1] + self._is_complete = last_blank(last_block) and lines.isspace() + # Only store the raw input. For lines beyond the first one, we + # only store them for history purposes, and for execution we want + # the caller to only receive the _cell_magic() call. + self._store(lines, self._buffer_raw, 'source_raw') + self.cell_magic_parts.append(lines) + return self._is_complete + + lines_list = lines.splitlines() + + transforms = [transform_ipy_prompt, transform_classic_prompt, + transform_help_end, transform_escaped, + transform_assign_system, transform_assign_magic] + + # Transform logic + # + # We only apply the line transformers to the input if we have either no + # input yet, or complete input, or if the last line of the buffer ends + # with ':' (opening an indented block). This prevents the accidental + # transformation of escapes inside multiline expressions like + # triple-quoted strings or parenthesized expressions. + # + # The last heuristic, while ugly, ensures that the first line of an + # indented block is correctly transformed. + # + # FIXME: try to find a cleaner approach for this last bit. + + # Store raw source before applying any transformations to it. Note + # that this must be done *after* the reset() call that would otherwise + # flush the buffer. + self._store(lines, self._buffer_raw, 'source_raw') + + push = super(IPythonInputSplitter, self).push + buf = self._buffer + for line in lines_list: + if self._is_complete or not buf or \ + (buf and buf[-1].rstrip().endswith((':', ','))): + for f in transforms: + line = f(line) + + out = push(line) + return out + def push(self, lines): """Push one or more lines of IPython input. @@ -734,25 +843,19 @@ def push(self, lines): this value is also stored as a private attribute (_is_complete), so it can be queried at any time. """ + print('mode:', self.input_mode) + print('lines:',repr(lines)) if not lines: return super(IPythonInputSplitter, self).push(lines) # We must ensure all input is pure unicode lines = cast_unicode(lines, self.encoding) - # cell magic support - #print('IM:', self.input_mode,'\n'+lines); print('---') # dbg - #if self.input_mode == 'cell' and lines.startswith('%%'): - if lines.startswith('%%'): - # Cell magics bypass all further transformations - self.reset() - self._is_complete = is_complete = True - first, _, body = lines.partition('\n') - magic_name, _, line = first.partition(' ') - magic_name = magic_name.lstrip(ESC_MAGIC) - self.cell_magic_body = body - tpl = 'get_ipython()._cell_magic(%r, %r)' - lines = tpl % (magic_name, line) + if self.input_mode == 'line': + return self._push_line_mode(lines) + + ## else: + ## return self._push_cell_mode(lines) lines_list = lines.splitlines() @@ -796,8 +899,7 @@ def push(self, lines): buf = self._buffer for line in lines_list: if self._is_complete or not buf or \ - (buf and (buf[-1].rstrip().endswith(':') or - buf[-1].rstrip().endswith(',')) ): + (buf and buf[-1].rstrip().endswith((':', ','))): for f in transforms: line = f(line) diff --git a/IPython/core/interactiveshell.py b/IPython/core/interactiveshell.py index 0f96b3aabae..bd0a3df1a8e 100644 --- a/IPython/core/interactiveshell.py +++ b/IPython/core/interactiveshell.py @@ -2027,7 +2027,12 @@ def line_magic(self, magic_name, line): """ fn = self.find_line_magic(magic_name) if fn is None: - error("Line magic function `%%%s` not found." % magic_name) + em = "Line magic function `%%%s` not found" % magic_name + cm = self.find_cell_magic(magic_name) + if cm is not None: + em += (' (Did you by chance mean the cell magic `%%%%%s` ' + 'instead?).') + error() else: # Note: this is the distance in the stack to the user's frame. # This will need to be updated if the internal calling logic gets @@ -2469,13 +2474,9 @@ def safe_run_module(self, mod_name, where): self.showtraceback() warn('Unknown failure executing module: <%s>' % mod_name) - def call_cell_magic(self, raw_cell, store_history=False): - line, _, cell = raw_cell.partition(os.linesep) - magic_name, _, line = line.partition(' ') - magic_name = magic_name.lstrip(prefilter.ESC_MAGIC) - return self.cell_magic(magic_name, line, cell) - def _cell_magic(self, magic_name, line): + """Special method to call a cell magic with the data stored in self. + """ cell = self._current_cell_magic_body self._current_cell_magic_body = None return self.cell_magic(magic_name, line, cell) @@ -2507,8 +2508,9 @@ def run_cell(self, raw_cell, store_history=False, silent=False): # ugly, we need to do something cleaner later... Now the logic is # simply that the input_splitter remembers if there was a cell magic, # and in that case we grab the cell body. - if self.input_splitter.cell_magic_body is not None: - self._current_cell_magic_body = self.input_splitter.cell_magic_body + if self.input_splitter.cell_magic_parts: + self._current_cell_magic_body = \ + ''.join(self.input_splitter.cell_magic_parts) cell = self.input_splitter.source_reset() with self.builtin_trap: diff --git a/IPython/core/tests/test_inputsplitter.py b/IPython/core/tests/test_inputsplitter.py index 0d244b97b02..c7e647eb461 100644 --- a/IPython/core/tests/test_inputsplitter.py +++ b/IPython/core/tests/test_inputsplitter.py @@ -599,6 +599,41 @@ def test_escaped_paren(): tt.check_pairs(isp.transform_escaped, syntax['escaped_paren']) +def test_last_blank(): + nt.assert_false(isp.last_blank('')) + nt.assert_false(isp.last_blank('abc')) + nt.assert_false(isp.last_blank('abc\n')) + nt.assert_false(isp.last_blank('abc\na')) + nt.assert_true(isp.last_blank('\n')) + nt.assert_true(isp.last_blank('\n ')) + nt.assert_true(isp.last_blank('abc\n ')) + nt.assert_true(isp.last_blank('abc\n\n')) + + +def test_cell_magics(): + from IPython.core import magic + + cell = """\ +%%cellm line +body +""" + sp = isp.IPythonInputSplitter(input_mode='line') + sp.push(cell) + nt.assert_equal(sp.cell_magic_parts, ['body\n']) + out = sp.source + ref = u"get_ipython()._cell_magic(u'cellm', u'line')\n" + nt.assert_equal(out, ref) + + sp.reset() + + sp.push('%%cellm line2\n') + nt.assert_true(sp.push_accepts_more()) #1 + sp.push('\n') + nt.assert_true(sp.push_accepts_more()) #2 + sp.push('\n') + nt.assert_false(sp.push_accepts_more()) #3 + + class IPythonInputTestCase(InputSplitterTestCase): """By just creating a new class whose .isp is a different instance, we re-run the same test battery on the new input splitter. From b89562be85b8babef065febf29e1b83c87135fb8 Mon Sep 17 00:00:00 2001 From: Fernando Perez Date: Fri, 25 May 2012 01:16:58 -0700 Subject: [PATCH 074/103] Working implementation of cell mode with regular expressions - cleaner. --- IPython/core/inputsplitter.py | 97 ++++++++++++++++++++++-- IPython/core/tests/test_inputsplitter.py | 15 ++++ 2 files changed, 104 insertions(+), 8 deletions(-) diff --git a/IPython/core/inputsplitter.py b/IPython/core/inputsplitter.py index e2c51fde7b4..73f1245663f 100644 --- a/IPython/core/inputsplitter.py +++ b/IPython/core/inputsplitter.py @@ -141,6 +141,7 @@ def num_ini_spaces(s): else: return 0 +last_blank_re = re.compile(r'^.*\n\s+$', re.MULTILINE) def last_blank(src): """Determine if the input source ends in a blank. @@ -152,9 +153,22 @@ def last_blank(src): src : string A single or multiline string. """ - if not src: return False - ll = src.splitlines()[-1] - return (ll == '') or ll.isspace() + return src == '\n' or bool(last_blank_re.match(src)) + + +last_two_blanks_re = re.compile(r'^.*\n\s*\n\s*$', re.MULTILINE) + +def last_two_blanks(src): + """Determine if the input source ends in two blanks. + + A blank is either a newline or a line consisting of whitespace. + + Parameters + ---------- + src : string + A single or multiline string. + """ + return bool(last_two_blanks_re.match(src)) def remove_comments(src): @@ -820,6 +834,76 @@ def _push_line_mode(self, lines): out = push(line) return out + + def _push_cell_mode(self, lines): + """Push in cell mode. + + This means that we get the entire cell with each call. Between resets, + the calls simply add more text to the input.""" + + if lines.startswith('%%'): + # Cell magics bypass all further transformations + self.cell_magic_mode = True + first, _, body = lines.partition('\n') + magic_name, _, line = first.partition(' ') + magic_name = magic_name.lstrip(ESC_MAGIC) + # We store the body of the cell and create a call to a method that + # will use this stored value. This is ugly, but it's a first cut to + # get it all working, as right now changing the return API of our + # methods would require major refactoring. + self.cell_magic_parts = [body] + tpl = 'get_ipython()._cell_magic(%r, %r)' + tlines = tpl % (magic_name, line) + self._store(tlines) + self._store(lines, self._buffer_raw, 'source_raw') + self._is_complete = last_two_blanks(lines) + return self._is_complete + + lines_list = lines.splitlines() + + transforms = [transform_ipy_prompt, transform_classic_prompt, + transform_help_end, transform_escaped, + transform_assign_system, transform_assign_magic] + + # Transform logic + # + # We only apply the line transformers to the input if we have either no + # input yet, or complete input, or if the last line of the buffer ends + # with ':' (opening an indented block). This prevents the accidental + # transformation of escapes inside multiline expressions like + # triple-quoted strings or parenthesized expressions. + # + # The last heuristic, while ugly, ensures that the first line of an + # indented block is correctly transformed. + # + # FIXME: try to find a cleaner approach for this last bit. + + # In cell mode, since we're going to pump the parent class by hand line + # by line, we need to temporarily switch out to 'line' mode, do a + # single manual reset and then feed the lines one by one. Note that + # this only matters if the input has more than one line. + self.reset() + self.input_mode = 'line' + + # Store raw source before applying any transformations to it. Note + # that this must be done *after* the reset() call that would otherwise + # flush the buffer. + self._store(lines, self._buffer_raw, 'source_raw') + + try: + push = super(IPythonInputSplitter, self).push + buf = self._buffer + for line in lines_list: + if self._is_complete or not buf or \ + (buf and buf[-1].rstrip().endswith((':', ','))): + for f in transforms: + line = f(line) + + out = push(line) + finally: + self.input_mode = 'cell' + return out + def push(self, lines): """Push one or more lines of IPython input. @@ -843,8 +927,6 @@ def push(self, lines): this value is also stored as a private attribute (_is_complete), so it can be queried at any time. """ - print('mode:', self.input_mode) - print('lines:',repr(lines)) if not lines: return super(IPythonInputSplitter, self).push(lines) @@ -853,9 +935,8 @@ def push(self, lines): if self.input_mode == 'line': return self._push_line_mode(lines) - - ## else: - ## return self._push_cell_mode(lines) + else: + return self._push_cell_mode(lines) lines_list = lines.splitlines() diff --git a/IPython/core/tests/test_inputsplitter.py b/IPython/core/tests/test_inputsplitter.py index c7e647eb461..d391974354b 100644 --- a/IPython/core/tests/test_inputsplitter.py +++ b/IPython/core/tests/test_inputsplitter.py @@ -604,12 +604,27 @@ def test_last_blank(): nt.assert_false(isp.last_blank('abc')) nt.assert_false(isp.last_blank('abc\n')) nt.assert_false(isp.last_blank('abc\na')) + nt.assert_true(isp.last_blank('\n')) nt.assert_true(isp.last_blank('\n ')) nt.assert_true(isp.last_blank('abc\n ')) nt.assert_true(isp.last_blank('abc\n\n')) +def test_last_two_blanks(): + nt.assert_false(isp.last_two_blanks('')) + nt.assert_false(isp.last_two_blanks('abc')) + nt.assert_false(isp.last_two_blanks('abc\n')) + nt.assert_false(isp.last_two_blanks('abc\n\na')) + + nt.assert_true(isp.last_two_blanks('\n\n')) + nt.assert_true(isp.last_two_blanks('\n\n ')) + nt.assert_true(isp.last_two_blanks('\n \n')) + nt.assert_true(isp.last_two_blanks('abc\n \n')) + nt.assert_true(isp.last_two_blanks('abc\n\n ')) + nt.assert_true(isp.last_two_blanks('abc\n\n')) + + def test_cell_magics(): from IPython.core import magic From a7d2e2738f8bc8f957d1c92f96c01c5903072f73 Mon Sep 17 00:00:00 2001 From: Fernando Perez Date: Fri, 25 May 2012 01:48:36 -0700 Subject: [PATCH 075/103] Refine regexp checks for end of blocks logic. --- IPython/core/inputsplitter.py | 6 ++-- IPython/core/tests/test_inputsplitter.py | 37 +++++++++++++++++++++--- 2 files changed, 37 insertions(+), 6 deletions(-) diff --git a/IPython/core/inputsplitter.py b/IPython/core/inputsplitter.py index 73f1245663f..4750bab9907 100644 --- a/IPython/core/inputsplitter.py +++ b/IPython/core/inputsplitter.py @@ -156,7 +156,8 @@ def last_blank(src): return src == '\n' or bool(last_blank_re.match(src)) -last_two_blanks_re = re.compile(r'^.*\n\s*\n\s*$', re.MULTILINE) +last_two_blanks_re = re.compile(r'^\n\s*\n\s*$', re.MULTILINE) +last_two_blanks_re2 = re.compile(r'^.+\n\s*\n\s+$', re.MULTILINE) def last_two_blanks(src): """Determine if the input source ends in two blanks. @@ -168,7 +169,8 @@ def last_two_blanks(src): src : string A single or multiline string. """ - return bool(last_two_blanks_re.match(src)) + return (bool(last_two_blanks_re.match(src)) or + bool(last_two_blanks_re2.match(src)) ) def remove_comments(src): diff --git a/IPython/core/tests/test_inputsplitter.py b/IPython/core/tests/test_inputsplitter.py index d391974354b..e3692894572 100644 --- a/IPython/core/tests/test_inputsplitter.py +++ b/IPython/core/tests/test_inputsplitter.py @@ -616,17 +616,20 @@ def test_last_two_blanks(): nt.assert_false(isp.last_two_blanks('abc')) nt.assert_false(isp.last_two_blanks('abc\n')) nt.assert_false(isp.last_two_blanks('abc\n\na')) + nt.assert_false(isp.last_two_blanks('abc\n \n')) + nt.assert_false(isp.last_two_blanks('abc\n\n')) nt.assert_true(isp.last_two_blanks('\n\n')) nt.assert_true(isp.last_two_blanks('\n\n ')) nt.assert_true(isp.last_two_blanks('\n \n')) - nt.assert_true(isp.last_two_blanks('abc\n \n')) nt.assert_true(isp.last_two_blanks('abc\n\n ')) - nt.assert_true(isp.last_two_blanks('abc\n\n')) + nt.assert_true(isp.last_two_blanks('abc\n\n\n')) + nt.assert_true(isp.last_two_blanks('abc\n\n \n')) + nt.assert_true(isp.last_two_blanks('abc\n\n \n ')) + nt.assert_true(isp.last_two_blanks('abc\n\n \n \n')) -def test_cell_magics(): - from IPython.core import magic +def test_cell_magics_line_mode(): cell = """\ %%cellm line @@ -649,6 +652,32 @@ def test_cell_magics(): nt.assert_false(sp.push_accepts_more()) #3 +def test_cell_magics_cell_mode(): + + cell = """\ +%%cellm line +body +""" + sp = isp.IPythonInputSplitter(input_mode='cell') + sp.push(cell) + nt.assert_equal(sp.cell_magic_parts, ['body\n']) + out = sp.source + ref = u"get_ipython()._cell_magic(u'cellm', u'line')\n" + nt.assert_equal(out, ref) + + sp.reset() + + src = '%%cellm line2\n' + sp.push(src) + nt.assert_true(sp.push_accepts_more()) #1 + src += '\n' + sp.push(src) + nt.assert_true(sp.push_accepts_more()) #2 + src += '\n' + sp.push(src) + nt.assert_false(sp.push_accepts_more()) #3 + + class IPythonInputTestCase(InputSplitterTestCase): """By just creating a new class whose .isp is a different instance, we re-run the same test battery on the new input splitter. From 03d77d1eeef335263ac4fe4ccceee32aa0f93f1d Mon Sep 17 00:00:00 2001 From: Fernando Perez Date: Fri, 25 May 2012 02:17:22 -0700 Subject: [PATCH 076/103] Add a few more fixes to cell/line input code, switch approaches. The pure regexp approach failed in more complex cases, so I've gone for a hybrid with some manual string logic and also regex. There are a lot more tests now that pass, so I'm starting to trust this code. --- IPython/core/inputsplitter.py | 27 +++++++++++++++++------- IPython/core/tests/test_inputsplitter.py | 5 +++++ 2 files changed, 24 insertions(+), 8 deletions(-) diff --git a/IPython/core/inputsplitter.py b/IPython/core/inputsplitter.py index 4750bab9907..74833a0eca5 100644 --- a/IPython/core/inputsplitter.py +++ b/IPython/core/inputsplitter.py @@ -141,8 +141,6 @@ def num_ini_spaces(s): else: return 0 -last_blank_re = re.compile(r'^.*\n\s+$', re.MULTILINE) - def last_blank(src): """Determine if the input source ends in a blank. @@ -153,11 +151,13 @@ def last_blank(src): src : string A single or multiline string. """ - return src == '\n' or bool(last_blank_re.match(src)) + if not src: return False + ll = src.splitlines()[-1] + return (ll == '') or ll.isspace() -last_two_blanks_re = re.compile(r'^\n\s*\n\s*$', re.MULTILINE) -last_two_blanks_re2 = re.compile(r'^.+\n\s*\n\s+$', re.MULTILINE) +last_two_blanks_re = re.compile(r'\n\s*\n\s*$', re.MULTILINE) +last_two_blanks_re2 = re.compile(r'.+\n\s*\n\s+$', re.MULTILINE) def last_two_blanks(src): """Determine if the input source ends in two blanks. @@ -169,8 +169,17 @@ def last_two_blanks(src): src : string A single or multiline string. """ - return (bool(last_two_blanks_re.match(src)) or - bool(last_two_blanks_re2.match(src)) ) + if not src: return False + # The logic here is tricky: I couldn't get a regexp to work and pass all + # the tests, so I took a different approach: split the source by lines, + # grab the last two and prepend '###\n' as a stand-in for whatever was in + # the body before the last two lines. Then, with that structure, it's + # possible to analyze with two regexps. Not the most elegant solution, but + # it works. If anyone tries to change this logic, make sure to validate + # the whole test suite first! + new_src = '\n'.join(['###\n'] + src.splitlines()[-2:]) + return (bool(last_two_blanks_re.match(new_src)) or + bool(last_two_blanks_re2.match(new_src)) ) def remove_comments(src): @@ -786,6 +795,7 @@ def _push_line_mode(self, lines): return False if self.cell_magic_mode: + #print('c2 lines', repr(lines)) # dbg # Find out if the last stored block has a whitespace line as its # last line and also this line is whitespace, case in which we're # done (two contiguous blank lines signal termination). Note that @@ -842,7 +852,7 @@ def _push_cell_mode(self, lines): This means that we get the entire cell with each call. Between resets, the calls simply add more text to the input.""" - + print('lines', repr(lines)) # dbg if lines.startswith('%%'): # Cell magics bypass all further transformations self.cell_magic_mode = True @@ -859,6 +869,7 @@ def _push_cell_mode(self, lines): self._store(tlines) self._store(lines, self._buffer_raw, 'source_raw') self._is_complete = last_two_blanks(lines) + print('IC', self._is_complete) # dbg return self._is_complete lines_list = lines.splitlines() diff --git a/IPython/core/tests/test_inputsplitter.py b/IPython/core/tests/test_inputsplitter.py index e3692894572..0fd1b11e842 100644 --- a/IPython/core/tests/test_inputsplitter.py +++ b/IPython/core/tests/test_inputsplitter.py @@ -609,6 +609,9 @@ def test_last_blank(): nt.assert_true(isp.last_blank('\n ')) nt.assert_true(isp.last_blank('abc\n ')) nt.assert_true(isp.last_blank('abc\n\n')) + nt.assert_true(isp.last_blank('abc\nd\n\n')) + nt.assert_true(isp.last_blank('abc\nd\ne\n\n')) + nt.assert_true(isp.last_blank('abc \n \n \n\n')) def test_last_two_blanks(): @@ -627,6 +630,8 @@ def test_last_two_blanks(): nt.assert_true(isp.last_two_blanks('abc\n\n \n')) nt.assert_true(isp.last_two_blanks('abc\n\n \n ')) nt.assert_true(isp.last_two_blanks('abc\n\n \n \n')) + nt.assert_true(isp.last_two_blanks('abc\nd\n\n\n')) + nt.assert_true(isp.last_two_blanks('abc\nd\ns\nds\n\n\n')) def test_cell_magics_line_mode(): From b49896d52eed0cad784e440e1445a21fa62de6ac Mon Sep 17 00:00:00 2001 From: Fernando Perez Date: Fri, 25 May 2012 02:27:17 -0700 Subject: [PATCH 077/103] Fixes for the text console. --- IPython/core/inputsplitter.py | 8 ++++---- IPython/core/tests/test_inputsplitter.py | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/IPython/core/inputsplitter.py b/IPython/core/inputsplitter.py index 74833a0eca5..cc9007f7a5f 100644 --- a/IPython/core/inputsplitter.py +++ b/IPython/core/inputsplitter.py @@ -775,8 +775,7 @@ def _push_line_mode(self, lines): #print (repr(lines)+'\n+++') #print('raw', self._buffer_raw, 'validate', self.cell_magic_mode) # Only trigger this block if we're at a 'fresh' pumping start. - if lines.startswith('%%') and (not self.cell_magic_mode) and \ - not self._buffer_raw: + if lines.startswith('%%'): # Cell magics bypass all further transformations self.cell_magic_mode = True first, _, body = lines.partition('\n') @@ -791,8 +790,9 @@ def _push_line_mode(self, lines): tlines = tpl % (magic_name, line) self._store(tlines) self._store(lines, self._buffer_raw, 'source_raw') - self._is_complete = False - return False + self._is_complete = last_two_blanks(lines) + #print('IC', self._is_complete) # dbg + return self._is_complete if self.cell_magic_mode: #print('c2 lines', repr(lines)) # dbg diff --git a/IPython/core/tests/test_inputsplitter.py b/IPython/core/tests/test_inputsplitter.py index 0fd1b11e842..3fd4e96aca5 100644 --- a/IPython/core/tests/test_inputsplitter.py +++ b/IPython/core/tests/test_inputsplitter.py @@ -631,7 +631,7 @@ def test_last_two_blanks(): nt.assert_true(isp.last_two_blanks('abc\n\n \n ')) nt.assert_true(isp.last_two_blanks('abc\n\n \n \n')) nt.assert_true(isp.last_two_blanks('abc\nd\n\n\n')) - nt.assert_true(isp.last_two_blanks('abc\nd\ns\nds\n\n\n')) + nt.assert_true(isp.last_two_blanks('abc\nd\ne\nf\n\n\n')) def test_cell_magics_line_mode(): From cb344b64aedac1f2c714103ce7ad77e54e9bc140 Mon Sep 17 00:00:00 2001 From: Fernando Perez Date: Fri, 25 May 2012 11:50:37 -0700 Subject: [PATCH 078/103] Improve error messages for line/cell magics. --- IPython/core/interactiveshell.py | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/IPython/core/interactiveshell.py b/IPython/core/interactiveshell.py index bd0a3df1a8e..66610b1c4bf 100644 --- a/IPython/core/interactiveshell.py +++ b/IPython/core/interactiveshell.py @@ -2027,12 +2027,11 @@ def line_magic(self, magic_name, line): """ fn = self.find_line_magic(magic_name) if fn is None: - em = "Line magic function `%%%s` not found" % magic_name cm = self.find_cell_magic(magic_name) - if cm is not None: - em += (' (Did you by chance mean the cell magic `%%%%%s` ' - 'instead?).') - error() + etpl = "Line magic function `%%%s` not found%s." + extra = '' if cm is None else (' (But cell magic `%%%%%s` exists, ' + 'did you mean that instead?)' % magic_name ) + error(etpl % (magic_name, extra)) else: # Note: this is the distance in the stack to the user's frame. # This will need to be updated if the internal calling logic gets @@ -2053,7 +2052,11 @@ def cell_magic(self, magic_name, line, cell): """ fn = self.find_cell_magic(magic_name) if fn is None: - error("Cell magic function `%%%%%s` not found." % magic_name) + lm = self.find_line_magic(magic_name) + etpl = "Cell magic function `%%%%%s` not found%s." + extra = '' if lm is None else (' (But line magic `%%%s` exists, ' + 'did you mean that instead?)' % magic_name ) + error(etpl % (magic_name, extra)) else: # Note: this is the distance in the stack to the user's frame. # This will need to be updated if the internal calling logic gets From 7b8fa8a33e3649a68135910116ed537555326643 Mon Sep 17 00:00:00 2001 From: Fernando Perez Date: Fri, 25 May 2012 12:00:30 -0700 Subject: [PATCH 079/103] Make cell magic input only allow one blank line for consistency. --- IPython/core/inputsplitter.py | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/IPython/core/inputsplitter.py b/IPython/core/inputsplitter.py index cc9007f7a5f..a9212196ec9 100644 --- a/IPython/core/inputsplitter.py +++ b/IPython/core/inputsplitter.py @@ -852,7 +852,7 @@ def _push_cell_mode(self, lines): This means that we get the entire cell with each call. Between resets, the calls simply add more text to the input.""" - print('lines', repr(lines)) # dbg + #print('lines', repr(lines)) # dbg if lines.startswith('%%'): # Cell magics bypass all further transformations self.cell_magic_mode = True @@ -868,8 +868,14 @@ def _push_cell_mode(self, lines): tlines = tpl % (magic_name, line) self._store(tlines) self._store(lines, self._buffer_raw, 'source_raw') - self._is_complete = last_two_blanks(lines) - print('IC', self._is_complete) # dbg + # We can actually choose whether to allow for single blank lines + # here.. My first implementation did that, and then I realized it + # wasn't consistent with the console behavior, so I've reverted it + # to one line. But I'm leaving it here so we can easily test both + # behaviors, I kind of liked having the extra blanks be allowed in + # the cell magics... + #self._is_complete = last_two_blanks(lines) + self._is_complete = last_blank(lines) return self._is_complete lines_list = lines.splitlines() From 7d068b4d414615e8fd2fbcef4caf0e6b89b256be Mon Sep 17 00:00:00 2001 From: Fernando Perez Date: Fri, 25 May 2012 18:37:48 -0700 Subject: [PATCH 080/103] Clean up implementation of cell magics in inputsplitter. Final pass of cleanups readying for full review and merge. --- IPython/core/inputsplitter.py | 223 ++++++----------------- IPython/core/tests/test_inputsplitter.py | 177 +++++++++--------- 2 files changed, 151 insertions(+), 249 deletions(-) diff --git a/IPython/core/inputsplitter.py b/IPython/core/inputsplitter.py index a9212196ec9..d4de3f67fcf 100644 --- a/IPython/core/inputsplitter.py +++ b/IPython/core/inputsplitter.py @@ -725,9 +725,13 @@ class IPythonInputSplitter(InputSplitter): # String with raw, untransformed input. source_raw = '' - cell_magic_parts = [] + # Flag to track when we're in the middle of processing a cell magic, since + # the logic has to change. In that case, we apply no transformations at + # all. + processing_cell_magic = False - cell_magic_mode = False + # Storage for all blocks of input that make up a cell magic + cell_magic_parts = [] # Private attributes @@ -745,7 +749,7 @@ def reset(self): self._buffer_raw[:] = [] self.source_raw = '' self.cell_magic_parts = [] - self.cell_magic_mode = False + self.processing_cell_magic = False def source_raw_reset(self): """Return input and raw source and perform a full reset. @@ -756,172 +760,55 @@ def source_raw_reset(self): return out, out_r def push_accepts_more(self): - if self.cell_magic_mode: + if self.processing_cell_magic: return not self._is_complete else: return super(IPythonInputSplitter, self).push_accepts_more() - def _push_line_mode(self, lines): - """Push in line mode. - - This means that we only get individual 'lines' with each call, though - in practice each input may be multiline. But this is in contrast to - cell mode, which feeds the entirety of the cell from the start with - each call. + def _handle_cell_magic(self, lines): + """Process lines when they start with %%, which marks cell magics. """ - # cell magic support - #print('#'*10) - #print(lines+'\n---') # dbg - #print (repr(lines)+'\n+++') - #print('raw', self._buffer_raw, 'validate', self.cell_magic_mode) - # Only trigger this block if we're at a 'fresh' pumping start. - if lines.startswith('%%'): - # Cell magics bypass all further transformations - self.cell_magic_mode = True - first, _, body = lines.partition('\n') - magic_name, _, line = first.partition(' ') - magic_name = magic_name.lstrip(ESC_MAGIC) - # We store the body of the cell and create a call to a method that - # will use this stored value. This is ugly, but it's a first cut to - # get it all working, as right now changing the return API of our - # methods would require major refactoring. - self.cell_magic_parts = [body] - tpl = 'get_ipython()._cell_magic(%r, %r)' - tlines = tpl % (magic_name, line) - self._store(tlines) - self._store(lines, self._buffer_raw, 'source_raw') - self._is_complete = last_two_blanks(lines) - #print('IC', self._is_complete) # dbg - return self._is_complete - - if self.cell_magic_mode: - #print('c2 lines', repr(lines)) # dbg - # Find out if the last stored block has a whitespace line as its - # last line and also this line is whitespace, case in which we're - # done (two contiguous blank lines signal termination). Note that - # the storage logic *enforces* that every stored block is - # newline-terminated, so we grab everything but the last character - # so we can have the body of the block alone. - last_block = self.cell_magic_parts[-1] - self._is_complete = last_blank(last_block) and lines.isspace() - # Only store the raw input. For lines beyond the first one, we - # only store them for history purposes, and for execution we want - # the caller to only receive the _cell_magic() call. - self._store(lines, self._buffer_raw, 'source_raw') - self.cell_magic_parts.append(lines) - return self._is_complete - - lines_list = lines.splitlines() - - transforms = [transform_ipy_prompt, transform_classic_prompt, - transform_help_end, transform_escaped, - transform_assign_system, transform_assign_magic] - - # Transform logic - # - # We only apply the line transformers to the input if we have either no - # input yet, or complete input, or if the last line of the buffer ends - # with ':' (opening an indented block). This prevents the accidental - # transformation of escapes inside multiline expressions like - # triple-quoted strings or parenthesized expressions. - # - # The last heuristic, while ugly, ensures that the first line of an - # indented block is correctly transformed. - # - # FIXME: try to find a cleaner approach for this last bit. - - # Store raw source before applying any transformations to it. Note - # that this must be done *after* the reset() call that would otherwise - # flush the buffer. + self.processing_cell_magic = True + first, _, body = lines.partition('\n') + magic_name, _, line = first.partition(' ') + magic_name = magic_name.lstrip(ESC_MAGIC) + # We store the body of the cell and create a call to a method that + # will use this stored value. This is ugly, but it's a first cut to + # get it all working, as right now changing the return API of our + # methods would require major refactoring. + self.cell_magic_parts = [body] + tpl = 'get_ipython()._cell_magic(%r, %r)' + tlines = tpl % (magic_name, line) + self._store(tlines) self._store(lines, self._buffer_raw, 'source_raw') + # We can actually choose whether to allow for single blank lines here + # during input for clients that use cell mode to decide when to stop + # pushing input (currently only the Qt console). + # My first implementation did that, and then I realized it wasn't + # consistent with the terminal behavior, so I've reverted it to one + # line. But I'm leaving it here so we can easily test both behaviors, + # I kind of liked having full blank lines allowed in the cell magics... + #self._is_complete = last_two_blanks(lines) + self._is_complete = last_blank(lines) + return self._is_complete - push = super(IPythonInputSplitter, self).push - buf = self._buffer - for line in lines_list: - if self._is_complete or not buf or \ - (buf and buf[-1].rstrip().endswith((':', ','))): - for f in transforms: - line = f(line) - - out = push(line) - return out - - - def _push_cell_mode(self, lines): - """Push in cell mode. - - This means that we get the entire cell with each call. Between resets, - the calls simply add more text to the input.""" - #print('lines', repr(lines)) # dbg - if lines.startswith('%%'): - # Cell magics bypass all further transformations - self.cell_magic_mode = True - first, _, body = lines.partition('\n') - magic_name, _, line = first.partition(' ') - magic_name = magic_name.lstrip(ESC_MAGIC) - # We store the body of the cell and create a call to a method that - # will use this stored value. This is ugly, but it's a first cut to - # get it all working, as right now changing the return API of our - # methods would require major refactoring. - self.cell_magic_parts = [body] - tpl = 'get_ipython()._cell_magic(%r, %r)' - tlines = tpl % (magic_name, line) - self._store(tlines) - self._store(lines, self._buffer_raw, 'source_raw') - # We can actually choose whether to allow for single blank lines - # here.. My first implementation did that, and then I realized it - # wasn't consistent with the console behavior, so I've reverted it - # to one line. But I'm leaving it here so we can easily test both - # behaviors, I kind of liked having the extra blanks be allowed in - # the cell magics... - #self._is_complete = last_two_blanks(lines) - self._is_complete = last_blank(lines) - return self._is_complete - - lines_list = lines.splitlines() - - transforms = [transform_ipy_prompt, transform_classic_prompt, - transform_help_end, transform_escaped, - transform_assign_system, transform_assign_magic] - - # Transform logic - # - # We only apply the line transformers to the input if we have either no - # input yet, or complete input, or if the last line of the buffer ends - # with ':' (opening an indented block). This prevents the accidental - # transformation of escapes inside multiline expressions like - # triple-quoted strings or parenthesized expressions. - # - # The last heuristic, while ugly, ensures that the first line of an - # indented block is correctly transformed. - # - # FIXME: try to find a cleaner approach for this last bit. - - # In cell mode, since we're going to pump the parent class by hand line - # by line, we need to temporarily switch out to 'line' mode, do a - # single manual reset and then feed the lines one by one. Note that - # this only matters if the input has more than one line. - self.reset() - self.input_mode = 'line' - - # Store raw source before applying any transformations to it. Note - # that this must be done *after* the reset() call that would otherwise - # flush the buffer. + def _line_mode_cell_append(self, lines): + """Append new content for a cell magic in line mode. + """ + # Only store the raw input. Lines beyond the first one are only only + # stored for history purposes; for execution the caller will grab the + # magic pieces from cell_magic_parts and will assemble the cell body self._store(lines, self._buffer_raw, 'source_raw') - - try: - push = super(IPythonInputSplitter, self).push - buf = self._buffer - for line in lines_list: - if self._is_complete or not buf or \ - (buf and buf[-1].rstrip().endswith((':', ','))): - for f in transforms: - line = f(line) - - out = push(line) - finally: - self.input_mode = 'cell' - return out + self.cell_magic_parts.append(lines) + # Find out if the last stored block has a whitespace line as its + # last line and also this line is whitespace, case in which we're + # done (two contiguous blank lines signal termination). Note that + # the storage logic *enforces* that every stored block is + # newline-terminated, so we grab everything but the last character + # so we can have the body of the block alone. + last_block = self.cell_magic_parts[-1] + self._is_complete = last_blank(last_block) and lines.isspace() + return self._is_complete def push(self, lines): """Push one or more lines of IPython input. @@ -952,11 +839,17 @@ def push(self, lines): # We must ensure all input is pure unicode lines = cast_unicode(lines, self.encoding) - if self.input_mode == 'line': - return self._push_line_mode(lines) - else: - return self._push_cell_mode(lines) + # If the entire input block is a cell magic, return after handling it + # as the rest of the transformation logic should be skipped. + if lines.startswith('%%'): + return self._handle_cell_magic(lines) + + # In line mode, a cell magic can arrive in separate pieces + if self.input_mode == 'line' and self.processing_cell_magic: + return self._line_mode_cell_append(lines) + # The rest of the processing is for 'normal' content, i.e. IPython + # source that we process through our transformations pipeline. lines_list = lines.splitlines() transforms = [transform_ipy_prompt, transform_classic_prompt, diff --git a/IPython/core/tests/test_inputsplitter.py b/IPython/core/tests/test_inputsplitter.py index 3fd4e96aca5..1ddf97aa9f1 100644 --- a/IPython/core/tests/test_inputsplitter.py +++ b/IPython/core/tests/test_inputsplitter.py @@ -599,90 +599,6 @@ def test_escaped_paren(): tt.check_pairs(isp.transform_escaped, syntax['escaped_paren']) -def test_last_blank(): - nt.assert_false(isp.last_blank('')) - nt.assert_false(isp.last_blank('abc')) - nt.assert_false(isp.last_blank('abc\n')) - nt.assert_false(isp.last_blank('abc\na')) - - nt.assert_true(isp.last_blank('\n')) - nt.assert_true(isp.last_blank('\n ')) - nt.assert_true(isp.last_blank('abc\n ')) - nt.assert_true(isp.last_blank('abc\n\n')) - nt.assert_true(isp.last_blank('abc\nd\n\n')) - nt.assert_true(isp.last_blank('abc\nd\ne\n\n')) - nt.assert_true(isp.last_blank('abc \n \n \n\n')) - - -def test_last_two_blanks(): - nt.assert_false(isp.last_two_blanks('')) - nt.assert_false(isp.last_two_blanks('abc')) - nt.assert_false(isp.last_two_blanks('abc\n')) - nt.assert_false(isp.last_two_blanks('abc\n\na')) - nt.assert_false(isp.last_two_blanks('abc\n \n')) - nt.assert_false(isp.last_two_blanks('abc\n\n')) - - nt.assert_true(isp.last_two_blanks('\n\n')) - nt.assert_true(isp.last_two_blanks('\n\n ')) - nt.assert_true(isp.last_two_blanks('\n \n')) - nt.assert_true(isp.last_two_blanks('abc\n\n ')) - nt.assert_true(isp.last_two_blanks('abc\n\n\n')) - nt.assert_true(isp.last_two_blanks('abc\n\n \n')) - nt.assert_true(isp.last_two_blanks('abc\n\n \n ')) - nt.assert_true(isp.last_two_blanks('abc\n\n \n \n')) - nt.assert_true(isp.last_two_blanks('abc\nd\n\n\n')) - nt.assert_true(isp.last_two_blanks('abc\nd\ne\nf\n\n\n')) - - -def test_cell_magics_line_mode(): - - cell = """\ -%%cellm line -body -""" - sp = isp.IPythonInputSplitter(input_mode='line') - sp.push(cell) - nt.assert_equal(sp.cell_magic_parts, ['body\n']) - out = sp.source - ref = u"get_ipython()._cell_magic(u'cellm', u'line')\n" - nt.assert_equal(out, ref) - - sp.reset() - - sp.push('%%cellm line2\n') - nt.assert_true(sp.push_accepts_more()) #1 - sp.push('\n') - nt.assert_true(sp.push_accepts_more()) #2 - sp.push('\n') - nt.assert_false(sp.push_accepts_more()) #3 - - -def test_cell_magics_cell_mode(): - - cell = """\ -%%cellm line -body -""" - sp = isp.IPythonInputSplitter(input_mode='cell') - sp.push(cell) - nt.assert_equal(sp.cell_magic_parts, ['body\n']) - out = sp.source - ref = u"get_ipython()._cell_magic(u'cellm', u'line')\n" - nt.assert_equal(out, ref) - - sp.reset() - - src = '%%cellm line2\n' - sp.push(src) - nt.assert_true(sp.push_accepts_more()) #1 - src += '\n' - sp.push(src) - nt.assert_true(sp.push_accepts_more()) #2 - src += '\n' - sp.push(src) - nt.assert_false(sp.push_accepts_more()) #3 - - class IPythonInputTestCase(InputSplitterTestCase): """By just creating a new class whose .isp is a different instance, we re-run the same test battery on the new input splitter. @@ -792,3 +708,96 @@ def test_syntax_multiline(self): print 'Raw source was:\n', raw except EOFError: print 'Bye' + +# Tests for cell magics support + +def test_last_blank(): + nt.assert_false(isp.last_blank('')) + nt.assert_false(isp.last_blank('abc')) + nt.assert_false(isp.last_blank('abc\n')) + nt.assert_false(isp.last_blank('abc\na')) + + nt.assert_true(isp.last_blank('\n')) + nt.assert_true(isp.last_blank('\n ')) + nt.assert_true(isp.last_blank('abc\n ')) + nt.assert_true(isp.last_blank('abc\n\n')) + nt.assert_true(isp.last_blank('abc\nd\n\n')) + nt.assert_true(isp.last_blank('abc\nd\ne\n\n')) + nt.assert_true(isp.last_blank('abc \n \n \n\n')) + + +def test_last_two_blanks(): + nt.assert_false(isp.last_two_blanks('')) + nt.assert_false(isp.last_two_blanks('abc')) + nt.assert_false(isp.last_two_blanks('abc\n')) + nt.assert_false(isp.last_two_blanks('abc\n\na')) + nt.assert_false(isp.last_two_blanks('abc\n \n')) + nt.assert_false(isp.last_two_blanks('abc\n\n')) + + nt.assert_true(isp.last_two_blanks('\n\n')) + nt.assert_true(isp.last_two_blanks('\n\n ')) + nt.assert_true(isp.last_two_blanks('\n \n')) + nt.assert_true(isp.last_two_blanks('abc\n\n ')) + nt.assert_true(isp.last_two_blanks('abc\n\n\n')) + nt.assert_true(isp.last_two_blanks('abc\n\n \n')) + nt.assert_true(isp.last_two_blanks('abc\n\n \n ')) + nt.assert_true(isp.last_two_blanks('abc\n\n \n \n')) + nt.assert_true(isp.last_two_blanks('abc\nd\n\n\n')) + nt.assert_true(isp.last_two_blanks('abc\nd\ne\nf\n\n\n')) + + +class CellModeCellMagics(unittest.TestCase): + sp = isp.IPythonInputSplitter(input_mode='cell') + + def test_whole_cell(self): + src = "%%cellm line\nbody\n" + sp = self.sp + sp.push(src) + nt.assert_equal(sp.cell_magic_parts, ['body\n']) + out = sp.source + ref = u"get_ipython()._cell_magic(u'cellm', u'line')\n" + nt.assert_equal(out, ref) + + def test_incremental(self): + sp = self.sp + src = '%%cellm line2\n' + sp.push(src) + nt.assert_true(sp.push_accepts_more()) #1 + src += '\n' + sp.push(src) + # Note: if we ever change the logic to allow full blank lines (see + # _handle_cell_magic), then the following test should change to true + nt.assert_false(sp.push_accepts_more()) #2 + # By now, even with full blanks allowed, a second blank should signal + # the end. For now this test is only a redundancy safety, but don't + # delete it in case we change our mind and the previous one goes to + # true. + src += '\n' + sp.push(src) + nt.assert_false(sp.push_accepts_more()) #3 + + def tearDown(self): + self.sp.reset() + + +class LineModeCellMagics(unittest.TestCase): + sp = isp.IPythonInputSplitter(input_mode='line') + + def test_whole_cell(self): + src = "%%cellm line\nbody\n" + sp = self.sp + sp.push(src) + nt.assert_equal(sp.cell_magic_parts, ['body\n']) + out = sp.source + ref = u"get_ipython()._cell_magic(u'cellm', u'line')\n" + nt.assert_equal(out, ref) + + def test_incremental(self): + sp = self.sp + sp.push('%%cellm line2\n') + nt.assert_true(sp.push_accepts_more()) #1 + sp.push('\n') + nt.assert_false(sp.push_accepts_more()) #2 + + def tearDown(self): + self.sp.reset() From 9bb583cd4e63dcc9b2850f32d66c5fdfcc4911a2 Mon Sep 17 00:00:00 2001 From: Fernando Perez Date: Fri, 25 May 2012 18:53:54 -0700 Subject: [PATCH 081/103] Small fixes as per @certik's review. --- IPython/core/magics/auto.py | 2 +- IPython/core/magics/basic.py | 2 +- IPython/core/magics/code.py | 6 +++--- IPython/core/magics/namespace.py | 6 +++--- IPython/core/magics/osm.py | 10 +++++----- IPython/extensions/storemagic.py | 12 ++++++------ 6 files changed, 19 insertions(+), 19 deletions(-) diff --git a/IPython/core/magics/auto.py b/IPython/core/magics/auto.py index 348d982ef18..04aff30dd8a 100644 --- a/IPython/core/magics/auto.py +++ b/IPython/core/magics/auto.py @@ -109,7 +109,7 @@ def autocall(self, parameter_s=''): else: arg = 'toggle' - if not arg in (0, 1, 2,'toggle'): + if not arg in (0, 1, 2, 'toggle'): error('Valid modes: (0->Off, 1->Smart, 2->Full') return diff --git a/IPython/core/magics/basic.py b/IPython/core/magics/basic.py index 707bbf0d1ed..7f062c12463 100644 --- a/IPython/core/magics/basic.py +++ b/IPython/core/magics/basic.py @@ -71,7 +71,7 @@ def magic(self, parameter_s=''): mode = parameter_s.split()[0][1:] if mode == 'rest': rest_docs = [] - except: + except IndexError: pass magic_docs = [] diff --git a/IPython/core/magics/code.py b/IPython/core/magics/code.py index 9bf5e6cb959..faad35337ec 100644 --- a/IPython/core/magics/code.py +++ b/IPython/core/magics/code.py @@ -249,7 +249,7 @@ class DataIsObject(Exception): pass filename = make_filename(args) datafile = 1 warn('Could not find file where `%s` is defined.\n' - 'Opening a file named `%s`' % (args,filename)) + 'Opening a file named `%s`' % (args, filename)) # Now, make sure we can actually read the source (if it was # in a temp file it's gone by now). if datafile: @@ -259,8 +259,8 @@ class DataIsObject(Exception): pass except IOError: filename = make_filename(args) if filename is None: - warn('The file `%s` where `%s` was defined cannot ' - 'be read.' % (filename,data)) + warn('The file `%s` where `%s` was defined ' + 'cannot be read.' % (filename, data)) return use_temp = False diff --git a/IPython/core/magics/namespace.py b/IPython/core/magics/namespace.py index 08e0dc946fb..62df7e729b4 100644 --- a/IPython/core/magics/namespace.py +++ b/IPython/core/magics/namespace.py @@ -448,9 +448,9 @@ def type_name(v): vdtype = var.dtype if vbytes < 100000: - print aformat % (vshape,vsize,vdtype,vbytes) + print aformat % (vshape, vsize, vdtype, vbytes) else: - print aformat % (vshape,vsize,vdtype,vbytes), + print aformat % (vshape, vsize, vdtype, vbytes), if vbytes < Mb: print '(%s kb)' % (vbytes/kb,) else: @@ -463,7 +463,7 @@ def type_name(v): 'backslashreplace') except: vstr = "" % id(var) - vstr = vstr.replace('\n','\\n') + vstr = vstr.replace('\n', '\\n') if len(vstr) < 50: print vstr else: diff --git a/IPython/core/magics/osm.py b/IPython/core/magics/osm.py index 4280476ec20..723e12b885b 100644 --- a/IPython/core/magics/osm.py +++ b/IPython/core/magics/osm.py @@ -316,7 +316,7 @@ def cd(self, parameter_s=''): if ps in bkms: target = bkms[ps] - print '(bookmark:%s) -> %s' % (ps,target) + print '(bookmark:%s) -> %s' % (ps, target) ps = target else: if 'b' in opts: @@ -522,24 +522,24 @@ def sc(self, parameter_s=''): .s (or .spstr): value as space-separated string. """ - opts,args = self.parse_options(parameter_s,'lv') + opts,args = self.parse_options(parameter_s, 'lv') # Try to get a variable name and command to run try: # the variable name must be obtained from the parse_options # output, which uses shlex.split to strip options out. - var,_ = args.split('=',1) + var,_ = args.split('=', 1) var = var.strip() # But the command has to be extracted from the original input # parameter_s, not on what parse_options returns, to avoid the # quote stripping which shlex.split performs on it. - _,cmd = parameter_s.split('=',1) + _,cmd = parameter_s.split('=', 1) except ValueError: var,cmd = '','' # If all looks ok, proceed split = 'l' in opts out = self.shell.getoutput(cmd, split=split) if 'v' in opts: - print '%s ==\n%s' % (var,pformat(out)) + print '%s ==\n%s' % (var, pformat(out)) if var: self.shell.user_ns.update({var:out}) else: diff --git a/IPython/extensions/storemagic.py b/IPython/extensions/storemagic.py index 20cb84d5ba8..0a603308a04 100644 --- a/IPython/extensions/storemagic.py +++ b/IPython/extensions/storemagic.py @@ -127,7 +127,7 @@ def store(self, parameter_s=''): vars = self.db.keys('autorestore/*') vars.sort() if vars: - size = max(map(len,vars)) + size = max(map(len, vars)) else: size = 0 @@ -137,7 +137,7 @@ def store(self, parameter_s=''): for var in vars: justkey = os.path.basename(var) # print 30 first characters from every var - print fmt % (justkey,repr(get(var,''))[:50]) + print fmt % (justkey, repr(get(var, ''))[:50]) # default action - store the variable else: @@ -145,17 +145,17 @@ def store(self, parameter_s=''): if len(args) > 1 and args[1].startswith('>'): fnam = os.path.expanduser(args[1].lstrip('>').lstrip()) if args[1].startswith('>>'): - fil = open(fnam,'a') + fil = open(fnam, 'a') else: - fil = open(fnam,'w') + fil = open(fnam, 'w') obj = ip.ev(args[0]) print "Writing '%s' (%s) to file '%s'." % (args[0], obj.__class__.__name__, fnam) - if not isinstance (obj,basestring): + if not isinstance (obj, basestring): from pprint import pprint - pprint(obj,fil) + pprint(obj, fil) else: fil.write(obj) if not obj.endswith('\n'): From cf15c8f0ed4493492b50e8fccb72175157afac7c Mon Sep 17 00:00:00 2001 From: Fernando Perez Date: Fri, 25 May 2012 18:58:00 -0700 Subject: [PATCH 082/103] Fix contextlib imports for python3 as per @takluyver's review. --- IPython/core/interactiveshell.py | 9 +++++++-- IPython/frontend/terminal/embed.py | 8 +++++++- IPython/frontend/terminal/interactiveshell.py | 7 ++++++- 3 files changed, 20 insertions(+), 4 deletions(-) diff --git a/IPython/core/interactiveshell.py b/IPython/core/interactiveshell.py index 66610b1c4bf..0055887bfb0 100644 --- a/IPython/core/interactiveshell.py +++ b/IPython/core/interactiveshell.py @@ -28,8 +28,13 @@ import sys import tempfile import types -import urllib -from io import open as io_open + +# We need to use nested to support python 2.6, once we move to >=2.7, we can +# use the with keyword's new builtin support for nested managers +try: + from contextlib import nested +except: + from IPython.utils.nested_context import nested from IPython.config.configurable import SingletonConfigurable from IPython.core import debugger, oinspect diff --git a/IPython/frontend/terminal/embed.py b/IPython/frontend/terminal/embed.py index f1430a7832a..f6506ee94f7 100644 --- a/IPython/frontend/terminal/embed.py +++ b/IPython/frontend/terminal/embed.py @@ -25,9 +25,15 @@ from __future__ import with_statement import sys -from contextlib import nested import warnings +# We need to use nested to support python 2.6, once we move to >=2.7, we can +# use the with keyword's new builtin support for nested managers +try: + from contextlib import nested +except: + from IPython.utils.nested_context import nested + from IPython.core import ultratb from IPython.core.magic import Magics, magics_class, line_magic from IPython.frontend.terminal.interactiveshell import TerminalInteractiveShell diff --git a/IPython/frontend/terminal/interactiveshell.py b/IPython/frontend/terminal/interactiveshell.py index 1086eddcb45..5ddbcccdb9d 100644 --- a/IPython/frontend/terminal/interactiveshell.py +++ b/IPython/frontend/terminal/interactiveshell.py @@ -20,7 +20,12 @@ import sys import textwrap -from contextlib import nested +# We need to use nested to support python 2.6, once we move to >=2.7, we can +# use the with keyword's new builtin support for nested managers +try: + from contextlib import nested +except: + from IPython.utils.nested_context import nested from IPython.core.error import TryNext, UsageError from IPython.core.usage import interactive_usage, default_banner From b537de1513fbab47bdb04e830ad6d8a8baca719b Mon Sep 17 00:00:00 2001 From: Fernando Perez Date: Fri, 25 May 2012 19:02:27 -0700 Subject: [PATCH 083/103] Minor fixes as per @Carreau's review --- IPython/core/history.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/IPython/core/history.py b/IPython/core/history.py index bb03131f902..317f76c1c51 100644 --- a/IPython/core/history.py +++ b/IPython/core/history.py @@ -72,7 +72,7 @@ class HistoryAccessor(Configurable): By default, IPython will put the history database in the IPython profile directory. If you would rather share one history among - profiles, you ca set this value in each, so that they are consistent. + profiles, you can set this value in each, so that they are consistent. Due to an issue with fcntl, SQLite is known to misbehave on some NFS mounts. If you see IPython hanging, try setting this to something on a From b3a081fb1f0e4c75131efaf6db4efe1c7b958dd2 Mon Sep 17 00:00:00 2001 From: Fernando Perez Date: Fri, 25 May 2012 20:54:03 -0700 Subject: [PATCH 084/103] Support PYTHONPATH in zmq tests. When creating a custom environment, we need to add the user's PYTHONPATH as that may be the only way the caller is finding IPython. --- IPython/zmq/tests/test_embed_kernel.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/IPython/zmq/tests/test_embed_kernel.py b/IPython/zmq/tests/test_embed_kernel.py index 4cc7f896823..89667775a90 100644 --- a/IPython/zmq/tests/test_embed_kernel.py +++ b/IPython/zmq/tests/test_embed_kernel.py @@ -25,7 +25,6 @@ from IPython.zmq.blockingkernelmanager import BlockingKernelManager from IPython.utils import path, py3compat - #------------------------------------------------------------------------------- # Tests #------------------------------------------------------------------------------- @@ -37,7 +36,8 @@ def setup(): global save_get_ipython_dir IPYTHONDIR = tempfile.mkdtemp() - env = dict(IPYTHONDIR=IPYTHONDIR) + env = dict(IPYTHONDIR=IPYTHONDIR, + PYTHONPATH=os.environ['PYTHONPATH']) save_get_ipython_dir = path.get_ipython_dir path.get_ipython_dir = lambda : IPYTHONDIR From 681e926dcab4959b8aff3137de0a4ac6327a90b3 Mon Sep 17 00:00:00 2001 From: Fernando Perez Date: Fri, 25 May 2012 21:25:47 -0700 Subject: [PATCH 085/103] Ensure pythonpath is applied to test env only if present. --- IPython/zmq/tests/test_embed_kernel.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/IPython/zmq/tests/test_embed_kernel.py b/IPython/zmq/tests/test_embed_kernel.py index 89667775a90..ac5739e6085 100644 --- a/IPython/zmq/tests/test_embed_kernel.py +++ b/IPython/zmq/tests/test_embed_kernel.py @@ -36,8 +36,9 @@ def setup(): global save_get_ipython_dir IPYTHONDIR = tempfile.mkdtemp() - env = dict(IPYTHONDIR=IPYTHONDIR, - PYTHONPATH=os.environ['PYTHONPATH']) + env = dict(IPYTHONDIR=IPYTHONDIR) + if 'PYTHONPATH' in os.environ: + env['PYTHONPATH'] = os.environ['PYTHONPATH'] save_get_ipython_dir = path.get_ipython_dir path.get_ipython_dir = lambda : IPYTHONDIR From b29fa5a8e76babe5b7c34fd5bfee159c7a24000a Mon Sep 17 00:00:00 2001 From: Fernando Perez Date: Fri, 25 May 2012 23:50:20 -0700 Subject: [PATCH 086/103] Add completion support for cell magics and handling of line/cell magics. Tests included. --- IPython/core/completer.py | 26 +++++++++----- IPython/core/tests/test_completer.py | 51 ++++++++++++++++++++++++++++ 2 files changed, 69 insertions(+), 8 deletions(-) diff --git a/IPython/core/completer.py b/IPython/core/completer.py index d451fcd894d..52fa2168c31 100644 --- a/IPython/core/completer.py +++ b/IPython/core/completer.py @@ -53,15 +53,14 @@ # proper procedure is to maintain its copyright as belonging to the Python # Software Foundation (in addition to my own, for all new code). # -# Copyright (C) 2008-2011 IPython Development Team -# Copyright (C) 2001-2007 Fernando Perez. +# Copyright (C) 2008 IPython Development Team +# Copyright (C) 2001 Fernando Perez. # Copyright (C) 2001 Python Software Foundation, www.python.org # # Distributed under the terms of the BSD License. The full license is in # the file COPYING, distributed as part of this software. # #***************************************************************************** -from __future__ import print_function #----------------------------------------------------------------------------- # Imports @@ -606,12 +605,23 @@ def magic_matches(self, text): """Match magics""" #print 'Completer->magic_matches:',text,'lb',self.text_until_cursor # dbg # Get all shell magics now rather than statically, so magics loaded at - # runtime show up too - # FIXME - cell magics not implemented here yet. - magics = self.shell.magics_manager.lsmagic()['line'] + # runtime show up too. + lsm = self.shell.magics_manager.lsmagic() + line_magics = lsm['line'] + cell_magics = lsm['cell'] pre = self.magic_escape - baretext = text.lstrip(pre) - return [ pre+m for m in magics if m.startswith(baretext)] + pre2 = pre+pre + + # Completion logic: + # - user gives %%: only do cell magics + # - user gives %: do both line and cell magics + # - no prefix: do both + # In other words, line magics are skipped if the user gives %% explicitly + bare_text = text.lstrip(pre) + comp = [ pre2+m for m in cell_magics if m.startswith(bare_text)] + if not text.startswith(pre2): + comp += [ pre+m for m in line_magics if m.startswith(bare_text)] + return comp def alias_matches(self, text): """Match internal system aliases""" diff --git a/IPython/core/tests/test_completer.py b/IPython/core/tests/test_completer.py index 15285cca63f..c0bddd822d0 100644 --- a/IPython/core/tests/test_completer.py +++ b/IPython/core/tests/test_completer.py @@ -290,3 +290,54 @@ def test_func_kw_completions(): # Simulate completing with cursor right after b (pos==10): s, matches = c.complete(None,'myfunc(1,b)', 10) nt.assert_in('b=', matches) + + +def test_line_magics(): + ip = get_ipython() + c = ip.Completer + s, matches = c.complete(None, 'lsmag') + nt.assert_in('%lsmagic', matches) + s, matches = c.complete(None, '%lsmag') + nt.assert_in('%lsmagic', matches) + + +def test_cell_magics(): + from IPython.core.magic import register_cell_magic + + @register_cell_magic + def _foo_cellm(line, cell): + pass + + ip = get_ipython() + c = ip.Completer + + s, matches = c.complete(None, '_foo_ce') + nt.assert_in('%%_foo_cellm', matches) + s, matches = c.complete(None, '%%_foo_ce') + nt.assert_in('%%_foo_cellm', matches) + + +def test_line_cell_magics(): + from IPython.core.magic import register_line_cell_magic + + @register_line_cell_magic + def _bar_cellm(line, cell): + pass + + ip = get_ipython() + c = ip.Completer + + # The policy here is trickier, see comments in completion code. The + # returned values depend on whether the user passes %% or not explicitly, + # and this will show a difference if the same name is both a line and cell + # magic. + s, matches = c.complete(None, '_bar_ce') + nt.assert_in('%_bar_cellm', matches) + nt.assert_in('%%_bar_cellm', matches) + s, matches = c.complete(None, '%_bar_ce') + nt.assert_in('%_bar_cellm', matches) + nt.assert_in('%%_bar_cellm', matches) + s, matches = c.complete(None, '%%_bar_ce') + nt.assert_not_in('%_bar_cellm', matches) + nt.assert_in('%%_bar_cellm', matches) + From 954dc34947d5f2d1fbe43f590801d414ea6a0dbe Mon Sep 17 00:00:00 2001 From: Fernando Perez Date: Sat, 26 May 2012 15:14:55 -0700 Subject: [PATCH 087/103] Ensure that all public magic decorators have descriptive docstrings. --- IPython/core/magic.py | 52 +++++++++++++++++++++++++++++++++++++------ 1 file changed, 45 insertions(+), 7 deletions(-) diff --git a/IPython/core/magic.py b/IPython/core/magic.py index a87abf41d69..51ec3332a8b 100644 --- a/IPython/core/magic.py +++ b/IPython/core/magic.py @@ -104,7 +104,40 @@ def validate_type(magic_kind): magic_kinds, magic_kind) -def _magic_marker(magic_kind): +# The docstrings for the decorator below will be fairly similar for the two +# types (method and function), so we generate them here once and reuse the +# templates below. +_docstring_template = \ +"""Decorate the given {0} as {1} magic. + +The decorator can be used: + +i) without arguments: it will create a {1} magic named as the {0} being +decorated:: + + @deco + def foo(...) + +will create a {1} magic named `foo`. + +ii) with one string argument: which will be used as the actual name of the +resulting magic:: + + @deco('bar') + def foo(...) + +will create a {1} magic named `bar`. +""" + +# These two are decorator factories. While they are conceptually very similar, +# there are enough differences in the details that it's simpler to have them +# written as completely standalone functions rather than trying to share code +# and make a single one with convoluted logic. + +def _method_magic_marker(magic_kind): + """Decorator factory for methods in Magics subclasses. + """ + validate_type(magic_kind) # This is a closure to capture the magic_kind. We could also use a class, @@ -116,14 +149,12 @@ def magic_deco(arg): # "Naked" decorator call (just @foo, no args) func = arg name = func.func_name - func.magic_name = name retval = decorator(call, func) record_magic(magics, magic_kind, name, name) elif isinstance(arg, basestring): # Decorator called with arguments (@foo('bar')) name = arg def mark(func, *a, **kw): - func.magic_name = name record_magic(magics, magic_kind, name, func.func_name) return decorator(call, func) retval = mark @@ -132,12 +163,17 @@ def mark(func, *a, **kw): "string or function") return retval + # Ensure the resulting decorator has a usable docstring + magic_deco.__doc__ = _docstring_template.format('method', magic_kind) return magic_deco def _function_magic_marker(magic_kind): + """Decorator factory for standalone functions. + """ + validate_type(magic_kind) - + # This is a closure to capture the magic_kind. We could also use a class, # but it's overkill for just that one bit of state. def magic_deco(arg): @@ -172,15 +208,17 @@ def mark(func, *a, **kw): "string or function") return retval + # Ensure the resulting decorator has a usable docstring + magic_deco.__doc__ = _docstring_template.format('function', magic_kind) return magic_deco # Create the actual decorators for public use # These three are used to decorate methods in class definitions -line_magic = _magic_marker('line') -cell_magic = _magic_marker('cell') -line_cell_magic = _magic_marker('line_cell') +line_magic = _method_magic_marker('line') +cell_magic = _method_magic_marker('cell') +line_cell_magic = _method_magic_marker('line_cell') # These three decorate standalone functions and perform the decoration # immediately. They can only run where get_ipython() works From 54728731283b4d3522dbd57d909692b06b8b0820 Mon Sep 17 00:00:00 2001 From: Fernando Perez Date: Sat, 26 May 2012 15:23:00 -0700 Subject: [PATCH 088/103] Improve decorator docstrings and clarify execution context for function decos. --- IPython/core/magic.py | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/IPython/core/magic.py b/IPython/core/magic.py index 51ec3332a8b..1a8ae47805c 100644 --- a/IPython/core/magic.py +++ b/IPython/core/magic.py @@ -29,6 +29,7 @@ from IPython.external.decorator import decorator from IPython.utils.ipstruct import Struct from IPython.utils.process import arg_split +from IPython.utils.text import dedent from IPython.utils.traitlets import Bool, Dict, Instance from IPython.utils.warn import error, warn @@ -110,7 +111,7 @@ def validate_type(magic_kind): _docstring_template = \ """Decorate the given {0} as {1} magic. -The decorator can be used: +The decorator can be used with or without arguments, as follows. i) without arguments: it will create a {1} magic named as the {0} being decorated:: @@ -209,7 +210,18 @@ def mark(func, *a, **kw): return retval # Ensure the resulting decorator has a usable docstring - magic_deco.__doc__ = _docstring_template.format('function', magic_kind) + ds = _docstring_template.format('function', magic_kind) + + ds += dedent(""" + Note: this decorator can only be used in a context where IPython is already + active, so that the `get_ipython()` call succeeds. You can therefore use + it in your startup files loaded after IPython initializes, but *not* in the + IPython configuration file itself, which is executed before IPython is + fully up and running. Any file located in the `startup` subdirectory of + your configuration profile will be OK in this sense. + """) + + magic_deco.__doc__ = ds return magic_deco From 6f16ed92444f754b8be46beaac389f924501fe10 Mon Sep 17 00:00:00 2001 From: Fernando Perez Date: Sat, 26 May 2012 18:03:02 -0700 Subject: [PATCH 089/103] Add tests for object inspector with magics of all types. --- IPython/core/tests/test_oinspect.py | 63 +++++++++++++++++++++++++++++ 1 file changed, 63 insertions(+) diff --git a/IPython/core/tests/test_oinspect.py b/IPython/core/tests/test_oinspect.py index f7fc5ecd94e..bfdfc474591 100644 --- a/IPython/core/tests/test_oinspect.py +++ b/IPython/core/tests/test_oinspect.py @@ -20,6 +20,10 @@ # Our own imports from .. import oinspect +from IPython.core.magic import (Magics, magics_class, line_magic, + cell_magic, line_cell_magic, + register_line_magic, register_cell_magic, + register_line_cell_magic) from IPython.utils import py3compat #----------------------------------------------------------------------------- @@ -27,6 +31,7 @@ #----------------------------------------------------------------------------- inspector = oinspect.Inspector() +ip = get_ipython() #----------------------------------------------------------------------------- # Local utilities @@ -46,17 +51,50 @@ def __call__(self, *a, **kw): def method(self, x, z=2): """Some method's docstring""" + class OldStyle: """An old-style class for testing.""" pass + def f(x, y=2, *a, **kw): """A simple function.""" + def g(y, z=3, *a, **kw): pass # no docstring +@register_line_magic +def lmagic(line): + "A line magic" + + +@register_cell_magic +def cmagic(line, cell): + "A cell magic" + + +@register_line_cell_magic +def lcmagic(line, cell=None): + "A line/cell magic" + + +@magics_class +class SimpleMagics(Magics): + @line_magic + def Clmagic(self, cline): + "A class-based line magic" + + @cell_magic + def Ccmagic(self, cline, ccell): + "A class-based cell magic" + + @line_cell_magic + def Clcmagic(self, cline, ccell=None): + "A class-based line/cell magic" + + def check_calltip(obj, name, call, docstring): """Generic check pattern all calltip tests will use""" info = inspector.info(obj, name) @@ -93,6 +131,31 @@ def test_calltip_function2(): def test_calltip_builtin(): check_calltip(sum, 'sum', None, sum.__doc__) + +def test_calltip_line_magic(): + check_calltip(lmagic, 'lmagic', 'lmagic(line)', "A line magic") + + +def test_calltip_cell_magic(): + check_calltip(cmagic, 'cmagic', 'cmagic(line, cell)', "A cell magic") + + +def test_calltip_line_magic(): + check_calltip(lcmagic, 'lcmagic', 'lcmagic(line, cell=None)', + "A line/cell magic") + + +def test_class_magics(): + cm = SimpleMagics(ip) + ip.register_magics(cm) + check_calltip(cm.Clmagic, 'Clmagic', 'Clmagic(cline)', + "A class-based line magic") + check_calltip(cm.Ccmagic, 'Ccmagic', 'Ccmagic(cline, ccell)', + "A class-based cell magic") + check_calltip(cm.Clcmagic, 'Clcmagic', 'Clcmagic(cline, ccell=None)', + "A class-based line/cell magic") + + def test_info(): "Check that Inspector.info fills out various fields as expected." i = inspector.info(Call, oname='Call') From 9961b83805bbd0e2759a33e90720f494cb90cf64 Mon Sep 17 00:00:00 2001 From: Fernando Perez Date: Sat, 26 May 2012 18:09:37 -0700 Subject: [PATCH 090/103] Clarify that automagic is only for line magics. Cell magics should always be explicitly prefixed. --- IPython/core/magic.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/IPython/core/magic.py b/IPython/core/magic.py index 1a8ae47805c..fecd98b12c6 100644 --- a/IPython/core/magic.py +++ b/IPython/core/magic.py @@ -260,8 +260,8 @@ class MagicsManager(Configurable): auto_magic = Bool _auto_status = [ - 'Automagic is OFF, % prefix IS needed for magic functions.', - 'Automagic is ON, % prefix IS NOT needed for magic functions.'] + 'Automagic is OFF, % prefix IS needed for line magics.', + 'Automagic is ON, % prefix IS NOT needed for line magics.'] user_magics = Instance('IPython.core.magics.UserMagics') From 446d5043cafb4eddcad60f5f3f4d1510d67c8efd Mon Sep 17 00:00:00 2001 From: Fernando Perez Date: Sat, 26 May 2012 18:40:42 -0700 Subject: [PATCH 091/103] Add support for finding cell magics with ?/??. Note that this doesn't yet cover the case where '%%cellm?' is typed, only the case without any '%' markers. --- IPython/core/interactiveshell.py | 6 +- IPython/core/magic.py | 1 - IPython/core/magics/namespace.py | 2 - IPython/core/tests/test_interactiveshell.py | 69 +++++++++++++-------- IPython/core/tests/test_magic.py | 1 + 5 files changed, 47 insertions(+), 32 deletions(-) diff --git a/IPython/core/interactiveshell.py b/IPython/core/interactiveshell.py index 0055887bfb0..a58e0a8f670 100644 --- a/IPython/core/interactiveshell.py +++ b/IPython/core/interactiveshell.py @@ -1401,8 +1401,10 @@ def _ofind(self, oname, namespaces=None): # Try to see if it's magic if not found: if oname.startswith(ESC_MAGIC): - oname = oname[1:] - obj = self.find_magic(oname) + oname = oname.lstrip(ESC_MAGIC) + obj = self.find_line_magic(oname) + if obj is None: + obj = self.find_cell_magic(oname) if obj is not None: found = True ospace = 'IPython internal' diff --git a/IPython/core/magic.py b/IPython/core/magic.py index fecd98b12c6..a7d4d371802 100644 --- a/IPython/core/magic.py +++ b/IPython/core/magic.py @@ -172,7 +172,6 @@ def mark(func, *a, **kw): def _function_magic_marker(magic_kind): """Decorator factory for standalone functions. """ - validate_type(magic_kind) # This is a closure to capture the magic_kind. We could also use a class, diff --git a/IPython/core/magics/namespace.py b/IPython/core/magics/namespace.py index 62df7e729b4..b9fa9b1345a 100644 --- a/IPython/core/magics/namespace.py +++ b/IPython/core/magics/namespace.py @@ -43,8 +43,6 @@ def pinfo(self, parameter_s='', namespaces=None): '%pinfo object' is just a synonym for object? or ?object.""" #print 'pinfo par: <%s>' % parameter_s # dbg - - # detail_level: 0 -> obj? , 1 -> obj?? detail_level = 0 # We need to detect if we got called as 'pinfo pinfo foo', which can diff --git a/IPython/core/tests/test_interactiveshell.py b/IPython/core/tests/test_interactiveshell.py index 2ae07cfe7da..fa5bf1bf931 100644 --- a/IPython/core/tests/test_interactiveshell.py +++ b/IPython/core/tests/test_interactiveshell.py @@ -22,15 +22,25 @@ # stdlib import os import shutil +import sys import tempfile import unittest from os.path import join -import sys from StringIO import StringIO +# third-party +import nose.tools as nt + +# Our own from IPython.testing.decorators import skipif from IPython.utils import io +#----------------------------------------------------------------------------- +# Globals +#----------------------------------------------------------------------------- +# This is used by every single test, no point repeating it ad nauseam +ip = get_ipython() + #----------------------------------------------------------------------------- # Tests #----------------------------------------------------------------------------- @@ -38,7 +48,6 @@ class InteractiveShellTestCase(unittest.TestCase): def test_naked_string_cells(self): """Test that cells with only naked strings are fully executed""" - ip = get_ipython() # First, single-line inputs ip.run_cell('"a"\n') self.assertEquals(ip.user_ns['_'], 'a') @@ -49,7 +58,6 @@ def test_naked_string_cells(self): def test_run_empty_cell(self): """Just make sure we don't get a horrible error with a blank cell of input. Yes, I did overlook that.""" - ip = get_ipython() old_xc = ip.execution_count ip.run_cell('') self.assertEquals(ip.execution_count, old_xc) @@ -57,7 +65,6 @@ def test_run_empty_cell(self): def test_run_cell_multiline(self): """Multi-block, multi-line cells must execute correctly. """ - ip = get_ipython() src = '\n'.join(["x=1", "y=2", "if 1:", @@ -69,7 +76,6 @@ def test_run_cell_multiline(self): def test_multiline_string_cells(self): "Code sprinkled with multiline strings should execute (GH-306)" - ip = get_ipython() ip.run_cell('tmp=0') self.assertEquals(ip.user_ns['tmp'], 0) ip.run_cell('tmp=1;"""a\nb"""\n') @@ -77,7 +83,6 @@ def test_multiline_string_cells(self): def test_dont_cache_with_semicolon(self): "Ending a line with semicolon should not cache the returned object (GH-307)" - ip = get_ipython() oldlen = len(ip.user_ns['Out']) a = ip.run_cell('1;', store_history=True) newlen = len(ip.user_ns['Out']) @@ -89,7 +94,6 @@ def test_dont_cache_with_semicolon(self): def test_In_variable(self): "Verify that In variable grows with user input (GH-284)" - ip = get_ipython() oldlen = len(ip.user_ns['In']) ip.run_cell('1;', store_history=True) newlen = len(ip.user_ns['In']) @@ -97,13 +101,11 @@ def test_In_variable(self): self.assertEquals(ip.user_ns['In'][-1],'1;') def test_magic_names_in_string(self): - ip = get_ipython() ip.run_cell('a = """\n%exit\n"""') self.assertEquals(ip.user_ns['a'], '\n%exit\n') def test_alias_crash(self): """Errors in prefilter can't crash IPython""" - ip = get_ipython() ip.run_cell('%alias parts echo first %s second %s') # capture stderr: save_err = io.stderr @@ -115,7 +117,6 @@ def test_alias_crash(self): def test_trailing_newline(self): """test that running !(command) does not raise a SyntaxError""" - ip = get_ipython() ip.run_cell('!(true)\n', False) ip.run_cell('!(true)\n\n\n', False) @@ -132,7 +133,6 @@ def __repr__(self): def test_future_flags(self): """Check that future flags are used for parsing code (gh-777)""" - ip = get_ipython() ip.run_cell('from __future__ import print_function') try: ip.run_cell('prfunc_return_val = print(1,2, sep=" ")') @@ -143,7 +143,6 @@ def test_future_flags(self): def test_future_unicode(self): """Check that unicode_literals is imported from __future__ (gh #786)""" - ip = get_ipython() try: ip.run_cell(u'byte_str = "a"') assert isinstance(ip.user_ns['byte_str'], str) # string literals are byte strings by default @@ -187,7 +186,6 @@ def test_global_ns(self): def test_bad_custom_tb(self): """Check that InteractiveShell is protected from bad custom exception handlers""" - ip = get_ipython() from IPython.utils import io save_stderr = io.stderr try: @@ -203,7 +201,6 @@ def test_bad_custom_tb(self): def test_bad_custom_tb_return(self): """Check that InteractiveShell is protected from bad return types in custom exception handlers""" - ip = get_ipython() from IPython.utils import io save_stderr = io.stderr try: @@ -218,7 +215,6 @@ def test_bad_custom_tb_return(self): io.stderr = save_stderr def test_drop_by_id(self): - ip = get_ipython() myvars = {"a":object(), "b":object(), "c": object()} ip.push(myvars, interactive=False) for name in myvars: @@ -233,7 +229,6 @@ def test_drop_by_id(self): ip.reset() def test_var_expand(self): - ip = get_ipython() ip.user_ns['f'] = u'Ca\xf1o' self.assertEqual(ip.var_expand(u'echo $f'), u'echo Ca\xf1o') self.assertEqual(ip.var_expand(u'echo {f}'), u'echo Ca\xf1o') @@ -246,8 +241,6 @@ def test_var_expand(self): def test_bad_var_expand(self): """var_expand on invalid formats shouldn't raise""" - ip = get_ipython() - # SyntaxError self.assertEqual(ip.var_expand(u"{'a':5}"), u"{'a':5}") # NameError @@ -257,8 +250,6 @@ def test_bad_var_expand(self): def test_silent_nopostexec(self): """run_cell(silent=True) doesn't invoke post-exec funcs""" - ip = get_ipython() - d = dict(called=False) def set_called(): d['called'] = True @@ -275,8 +266,6 @@ def set_called(): def test_silent_noadvance(self): """run_cell(silent=True) doesn't advance execution_count""" - ip = get_ipython() - ec = ip.execution_count # silent should force store_history=False ip.run_cell("1", store_history=True, silent=True) @@ -289,8 +278,6 @@ def test_silent_noadvance(self): def test_silent_nodisplayhook(self): """run_cell(silent=True) doesn't trigger displayhook""" - ip = get_ipython() - d = dict(called=False) trap = ip.display_trap @@ -322,6 +309,35 @@ def test_print_softspace(self): In [2]: print 1,; print 2 1 2 """ + + def test_ofind_line_magic(self): + from IPython.core.magic import register_line_magic + + @register_line_magic + def lmagic(line): + "A line magic" + + # Get info on line magic + lfind = ip._ofind('lmagic') + info = dict(found=True, isalias=False, ismagic=True, + namespace = 'IPython internal', obj= lmagic.__wrapped__, + parent = None) + nt.assert_equal(lfind, info) + + def test_ofind_cell_magic(self): + from IPython.core.magic import register_cell_magic + + @register_cell_magic + def cmagic(line, cell): + "A cell magic" + + # Get info on cell magic + find = ip._ofind('cmagic') + info = dict(found=True, isalias=False, ismagic=True, + namespace = 'IPython internal', obj= cmagic.__wrapped__, + parent = None) + nt.assert_equal(find, info) + class TestSafeExecfileNonAsciiPath(unittest.TestCase): @@ -335,7 +351,6 @@ def setUp(self): os.chdir(self.TESTDIR) self.fname = u"åäötestscript.py" - def tearDown(self): os.chdir(self.oldpath) shutil.rmtree(self.BASETESTDIR) @@ -343,7 +358,7 @@ def tearDown(self): def test_1(self): """Test safe_execfile with non-ascii path """ - _ip.safe_execfile(self.fname, {}, raise_exceptions=True) + ip.safe_execfile(self.fname, {}, raise_exceptions=True) class TestSystemRaw(unittest.TestCase): @@ -351,7 +366,7 @@ def test_1(self): """Test system_raw with non-ascii cmd """ cmd = ur'''python -c "'åäö'" ''' - _ip.system_raw(cmd) + ip.system_raw(cmd) def test__IPYTHON__(): diff --git a/IPython/core/tests/test_magic.py b/IPython/core/tests/test_magic.py index 362eb89746c..0b531334c81 100644 --- a/IPython/core/tests/test_magic.py +++ b/IPython/core/tests/test_magic.py @@ -544,3 +544,4 @@ def cellm33(self, line, cell): # Check that nothing is registered as 'cellm33' c33 = _ip.find_cell_magic('cellm33') nt.assert_equals(c33, None) + From d2a11a767ee792b50abe44c5d8f5f690e5ccfe1d Mon Sep 17 00:00:00 2001 From: Fernando Perez Date: Sat, 26 May 2012 19:40:47 -0700 Subject: [PATCH 092/103] Fix split_user_input to correctly handle %% escape for cell magics. --- IPython/core/splitinput.py | 2 +- IPython/core/tests/test_splitinput.py | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/IPython/core/splitinput.py b/IPython/core/splitinput.py index a8c680eb9aa..7b957726fb1 100644 --- a/IPython/core/splitinput.py +++ b/IPython/core/splitinput.py @@ -44,7 +44,7 @@ line_split = re.compile(""" ^(\s*) # any leading space ([,;/%]|!!?|\?\??)? # escape character or characters - \s*(%?[\w\.\*]*) # function/method, possibly with leading % + \s*(%{0,2}[\w\.\*]*) # function/method, possibly with leading % # to correctly treat things like '?%magic' (.*?$|$) # rest of line """, re.VERBOSE) diff --git a/IPython/core/tests/test_splitinput.py b/IPython/core/tests/test_splitinput.py index cfc720985f3..4c31b0e3f36 100644 --- a/IPython/core/tests/test_splitinput.py +++ b/IPython/core/tests/test_splitinput.py @@ -20,7 +20,10 @@ (' ;ls', (' ', ';', 'ls', '')), ('f.g(x)', ('', '', 'f.g', '(x)')), ('f.g (x)', ('', '', 'f.g', '(x)')), - ('?%hist', ('', '?', '%hist', '')), + ('?%hist1', ('', '?', '%hist1', '')), + ('?%%hist2', ('', '?', '%%hist2', '')), + ('??%hist3', ('', '??', '%hist3', '')), + ('??%%hist4', ('', '??', '%%hist4', '')), ('?x*', ('', '?', 'x*', '')), ] if py3compat.PY3: From 520fed4785a2b7bfefe1843a92b49ce23b44ab1c Mon Sep 17 00:00:00 2001 From: Fernando Perez Date: Sat, 26 May 2012 19:44:01 -0700 Subject: [PATCH 093/103] Fix handling of lines like '%%foo?' in cell magic logic. --- IPython/core/inputsplitter.py | 7 +++---- IPython/core/tests/test_inputsplitter.py | 8 ++++++-- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/IPython/core/inputsplitter.py b/IPython/core/inputsplitter.py index d4de3f67fcf..e78ed2ef0c8 100644 --- a/IPython/core/inputsplitter.py +++ b/IPython/core/inputsplitter.py @@ -60,7 +60,6 @@ # Distributed under the terms of the BSD License. The full license is in # the file COPYING, distributed as part of this software. #----------------------------------------------------------------------------- -from __future__ import print_function #----------------------------------------------------------------------------- # Imports @@ -598,7 +597,6 @@ def _make_help_call(target, esc, lspace, next_input=None): else 'psearch' if '*' in target \ else 'pinfo' arg = " ".join([method, target]) - if next_input is None: return '%sget_ipython().magic(%r)' % (lspace, arg) else: @@ -608,7 +606,7 @@ def _make_help_call(target, esc, lspace, next_input=None): _initial_space_re = re.compile(r'\s*') -_help_end_re = re.compile(r"""(%? +_help_end_re = re.compile(r"""(%{0,2} [a-zA-Z_*][\w*]* # Variable name (\.[a-zA-Z_*][\w*]*)* # .etc.etc ) @@ -841,7 +839,8 @@ def push(self, lines): # If the entire input block is a cell magic, return after handling it # as the rest of the transformation logic should be skipped. - if lines.startswith('%%'): + if lines.startswith('%%') and not \ + (len(lines.splitlines()) == 1 and lines.endswith('?')): return self._handle_cell_magic(lines) # In line mode, a cell magic can arrive in separate pieces diff --git a/IPython/core/tests/test_inputsplitter.py b/IPython/core/tests/test_inputsplitter.py index 1ddf97aa9f1..f5b6599bf83 100644 --- a/IPython/core/tests/test_inputsplitter.py +++ b/IPython/core/tests/test_inputsplitter.py @@ -455,7 +455,8 @@ def transform_checker(tests, func): (u'?x1', "get_ipython().magic({u}'pinfo x1')"), (u'??x2', "get_ipython().magic({u}'pinfo2 x2')"), (u'?a.*s', "get_ipython().magic({u}'psearch a.*s')"), - (u'?%hist', "get_ipython().magic({u}'pinfo %hist')"), + (u'?%hist1', "get_ipython().magic({u}'pinfo %hist1')"), + (u'?%%hist2', "get_ipython().magic({u}'pinfo %%hist2')"), (u'?abc = qwe', "get_ipython().magic({u}'pinfo abc')"), ]], @@ -463,7 +464,10 @@ def transform_checker(tests, func): [(i,py3compat.u_format(o)) for i,o in \ [ (u'x3?', "get_ipython().magic({u}'pinfo x3')"), (u'x4??', "get_ipython().magic({u}'pinfo2 x4')"), - (u'%hist?', "get_ipython().magic({u}'pinfo %hist')"), + (u'%hist1?', "get_ipython().magic({u}'pinfo %hist1')"), + (u'%hist2??', "get_ipython().magic({u}'pinfo2 %hist2')"), + (u'%%hist3?', "get_ipython().magic({u}'pinfo %%hist3')"), + (u'%%hist4??', "get_ipython().magic({u}'pinfo2 %%hist4')"), (u'f*?', "get_ipython().magic({u}'psearch f*')"), (u'ax.*aspe*?', "get_ipython().magic({u}'psearch ax.*aspe*')"), (u'a = abc?', "get_ipython().set_next_input({u}'a = abc');" From 904b6c7c075dafa38103ebbb7bfad43e5952e69b Mon Sep 17 00:00:00 2001 From: Fernando Perez Date: Sat, 26 May 2012 19:55:58 -0700 Subject: [PATCH 094/103] Fix '%%cellm?' case, make tests more stringent to catch error. --- IPython/core/inputsplitter.py | 2 +- IPython/core/tests/test_inputsplitter.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/IPython/core/inputsplitter.py b/IPython/core/inputsplitter.py index e78ed2ef0c8..68b5fc34907 100644 --- a/IPython/core/inputsplitter.py +++ b/IPython/core/inputsplitter.py @@ -840,7 +840,7 @@ def push(self, lines): # If the entire input block is a cell magic, return after handling it # as the rest of the transformation logic should be skipped. if lines.startswith('%%') and not \ - (len(lines.splitlines()) == 1 and lines.endswith('?')): + (len(lines.splitlines()) == 1 and lines.strip().endswith('?')): return self._handle_cell_magic(lines) # In line mode, a cell magic can arrive in separate pieces diff --git a/IPython/core/tests/test_inputsplitter.py b/IPython/core/tests/test_inputsplitter.py index f5b6599bf83..2eda04c799b 100644 --- a/IPython/core/tests/test_inputsplitter.py +++ b/IPython/core/tests/test_inputsplitter.py @@ -624,7 +624,7 @@ def test_syntax(self): if raw.startswith(' '): continue - isp.push(raw) + isp.push(raw+'\n') out, out_raw = isp.source_raw_reset() self.assertEqual(out.rstrip(), out_t, tt.pair_fail_msg.format("inputsplitter",raw, out_t, out)) From 3e1b62f6436d43676838e0f21b609ea90400d20b Mon Sep 17 00:00:00 2001 From: Fernando Perez Date: Sat, 26 May 2012 20:15:55 -0700 Subject: [PATCH 095/103] Add docstrings as per review. --- IPython/core/magic.py | 51 +++++++++++++++++++++++++++++--- IPython/extensions/storemagic.py | 18 ++++++++++- 2 files changed, 64 insertions(+), 5 deletions(-) diff --git a/IPython/core/magic.py b/IPython/core/magic.py index a7d4d371802..e5e2ceab8ef 100644 --- a/IPython/core/magic.py +++ b/IPython/core/magic.py @@ -61,6 +61,11 @@ def on_off(tag): def compress_dhist(dh): + """Compress a directory history into a new one with at most 20 entries. + + Return a new list made from the first and last 10 elements of dhist after + removal of duplicates. + """ head, tail = dh[:-10], dh[-10:] newhead = [] @@ -84,6 +89,23 @@ def needs_local_scope(func): #----------------------------------------------------------------------------- def magics_class(cls): + """Class decorator for all subclasses of the main Magics class. + + Any class that subclasses Magics *must* also apply this decorator, to + ensure that all the methods that have been decorated as line/cell magics + get correctly registered in the class instance. This is necessary because + when method decorators run, the class does not exist yet, so they + temporarily store their information into a module global. Application of + this class decorator copies that global data to the class instance and + clears the global. + + Obviously, this mechanism is not thread-safe, which means that the + *creation* of subclasses of Magic should only be done in a single-thread + context. Instantiation of the classes has no restrictions. Given that + these classes are typically created at IPython startup time and before user + application code becomes active, in practice this should not pose any + problems. + """ cls.registered = True cls.magics = dict(line = magics['line'], cell = magics['cell']) @@ -92,14 +114,35 @@ def magics_class(cls): return cls -def record_magic(dct, mtype, mname, func): - if mtype == 'line_cell': - dct['line'][mname] = dct['cell'][mname] = func +def record_magic(dct, magic_kind, magic_name, func): + """Utility function to store a function as a magic of a specific kind. + + Parameters + ---------- + dct : dict + A dictionary with 'line' and 'cell' subdicts. + + magic_kind : str + Kind of magic to be stored. + + magic_name : str + Key to store the magic as. + + func : function + Callable object to store. + """ + if magic_kind == 'line_cell': + dct['line'][magic_name] = dct['cell'][magic_name] = func else: - dct[mtype][mname] = func + dct[magic_kind][magic_name] = func def validate_type(magic_kind): + """Ensure that the given magic_kind is valid. + + Check that the given magic_kind is one of the accepted spec types (stored + in the global `magic_spec`), raise ValueError otherwise. + """ if magic_kind not in magic_spec: raise ValueError('magic_kind must be one of %s, %s given' % magic_kinds, magic_kind) diff --git a/IPython/extensions/storemagic.py b/IPython/extensions/storemagic.py index 0a603308a04..a30e1e2ea87 100644 --- a/IPython/extensions/storemagic.py +++ b/IPython/extensions/storemagic.py @@ -9,9 +9,22 @@ c.StoreMagic.autorestore = True """ - +#----------------------------------------------------------------------------- +# Copyright (c) 2012, The IPython Development Team. +# +# Distributed under the terms of the Modified BSD License. +# +# The full license is in the file COPYING.txt, distributed with this software. +#----------------------------------------------------------------------------- + +#----------------------------------------------------------------------------- +# Imports +#----------------------------------------------------------------------------- + +# Stdlib import inspect, os, sys, textwrap +# Our own from IPython.core.error import UsageError from IPython.core.fakemodule import FakeModule from IPython.core.magic import Magics, magics_class, line_magic @@ -19,6 +32,9 @@ from IPython.testing.skipdoctest import skip_doctest from IPython.utils.traitlets import Bool, Instance +#----------------------------------------------------------------------------- +# Functions and classes +#----------------------------------------------------------------------------- def restore_aliases(ip): staliases = ip.db.get('stored_aliases', {}) From fef39283eb6653871304e4a900258c699830e120 Mon Sep 17 00:00:00 2001 From: Fernando Perez Date: Sat, 26 May 2012 20:37:51 -0700 Subject: [PATCH 096/103] Complete documenting magic module, as per review. Added full docstrings to all methods and functions, and restored the original docstring for the deprecated `define_magic` method. --- IPython/core/magic.py | 60 ++++++++++++++++++++++++++++++++++++++----- 1 file changed, 54 insertions(+), 6 deletions(-) diff --git a/IPython/core/magic.py b/IPython/core/magic.py index e5e2ceab8ef..803b343220a 100644 --- a/IPython/core/magic.py +++ b/IPython/core/magic.py @@ -299,8 +299,9 @@ class MagicsManager(Configurable): shell = Instance('IPython.core.interactiveshell.InteractiveShellABC') - auto_magic = Bool - + auto_magic = Bool(True, config=True, help= + "Automatically call line magics without requiring explicit % prefix") + _auto_status = [ 'Automagic is OFF, % prefix IS needed for line magics.', 'Automagic is ON, % prefix IS NOT needed for line magics.'] @@ -330,6 +331,23 @@ def lsmagic(self): def register(self, *magic_objects): """Register one or more instances of Magics. + + Take one or more classes or instances of classes that subclass the main + `core.Magic` class, and register them with IPython to use the magic + functions they provide. The registration process will then ensure that + any methods that have decorated to provide line and/or cell magics will + be recognized with the `%x`/`%%x` syntax as a line/cell magic + respectively. + + If classes are given, they will be instantiated with the default + constructor. If your classes need a custom constructor, you should + instanitate them first and pass the instance. + + The provided arguments can be an arbitrary mix of classes and instances. + + Parameters + ---------- + magic_objects : one or more classes or instances """ # Start by validating them to ensure they have all had their magic # methods registered at the instance level @@ -348,7 +366,30 @@ def register(self, *magic_objects): self.magics[mtype].update(m.magics[mtype]) def register_function(self, func, magic_kind='line', magic_name=None): - """Expose a standalone function as magic function for ipython. + """Expose a standalone function as magic function for IPython. + + This will create an IPython magic (line, cell or both) from a + standalone function. The functions should have the following + signatures: + + * For line magics: `def f(line)` + * For cell magics: `def f(line, cell)` + * For a function that does both: `def f(line, cell=None)` + + In the latter case, the function will be called with `cell==None` when + invoked as `%f`, and with cell as a string when invoked as `%%f`. + + Parameters + ---------- + func : callable + Function to be registered as a magic. + + magic_kind : str + Kind of magic, one of 'line', 'cell' or 'line_cell' + + magic_name : optional str + If given, the name the magic will have in the IPython namespace. By + default, the name of the function itself is used. """ # Create the new method in the user_magics and register it in the @@ -359,10 +400,17 @@ def register_function(self, func, magic_kind='line', magic_name=None): record_magic(self.magics, magic_kind, magic_name, func) def define_magic(self, name, func): - """Support for deprecated API. + """[Deprecated] Expose own function as magic function for IPython. + + Example:: + + def foo_impl(self, parameter_s=''): + 'My very own magic!. (Use docstrings, IPython reads them).' + print 'Magic function. Passed parameter is between < >:' + print '<%s>' % parameter_s + print 'The self object is:', self - This method exists only to support the old-style definition of magics. - It will eventually be removed. Deliberately not documented further. + ip.define_magic('foo',foo_impl) """ meth = types.MethodType(func, self.user_magics) setattr(self.user_magics, name, meth) From 84ad7d21ac1957370e4d86431ce9f93440532a6e Mon Sep 17 00:00:00 2001 From: Fernando Perez Date: Sat, 26 May 2012 20:54:09 -0700 Subject: [PATCH 097/103] Rename a few methods as per review, also complete some docstrings. --- IPython/core/inputsplitter.py | 2 +- IPython/core/interactiveshell.py | 23 +++++++++++++++++------ IPython/core/tests/test_inputsplitter.py | 4 ++-- IPython/core/tests/test_magic.py | 2 +- 4 files changed, 21 insertions(+), 10 deletions(-) diff --git a/IPython/core/inputsplitter.py b/IPython/core/inputsplitter.py index 68b5fc34907..c0183d1b95a 100644 --- a/IPython/core/inputsplitter.py +++ b/IPython/core/inputsplitter.py @@ -775,7 +775,7 @@ def _handle_cell_magic(self, lines): # get it all working, as right now changing the return API of our # methods would require major refactoring. self.cell_magic_parts = [body] - tpl = 'get_ipython()._cell_magic(%r, %r)' + tpl = 'get_ipython()._run_cached_cell_magic(%r, %r)' tlines = tpl % (magic_name, line) self._store(tlines) self._store(lines, self._buffer_raw, 'source_raw') diff --git a/IPython/core/interactiveshell.py b/IPython/core/interactiveshell.py index a58e0a8f670..d062b75f299 100644 --- a/IPython/core/interactiveshell.py +++ b/IPython/core/interactiveshell.py @@ -2021,7 +2021,7 @@ def init_magics(self): # even need a centralize colors management object. self.magic('colors %s' % self.colors) - def line_magic(self, magic_name, line): + def run_line_magic(self, magic_name, line): """Execute the given line magic. Parameters @@ -2054,8 +2054,19 @@ def line_magic(self, magic_name, line): result = fn(*args) return result - def cell_magic(self, magic_name, line, cell): + def run_cell_magic(self, magic_name, line, cell): """Execute the given cell magic. + + Parameters + ---------- + magic_name : str + Name of the desired magic function, without '%' prefix. + + line : str + The rest of the first input line as a single string. + + cell : str + The body of the cell as a (possibly multiline) string. """ fn = self.find_cell_magic(magic_name) if fn is None: @@ -2093,7 +2104,7 @@ def find_magic(self, magic_name, magic_kind='line'): return self.magics_manager.magics[magic_kind].get(magic_name) def magic(self, arg_s): - """DEPRECATED. Use line_magic() instead. + """DEPRECATED. Use run_line_magic() instead. Call a magic function by name. @@ -2114,7 +2125,7 @@ def magic(self, arg_s): # TODO: should we issue a loud deprecation warning here? magic_name, _, magic_arg_s = arg_s.partition(' ') magic_name = magic_name.lstrip(prefilter.ESC_MAGIC) - return self.line_magic(magic_name, magic_arg_s) + return self.run_line_magic(magic_name, magic_arg_s) #------------------------------------------------------------------------- # Things related to macros @@ -2484,12 +2495,12 @@ def safe_run_module(self, mod_name, where): self.showtraceback() warn('Unknown failure executing module: <%s>' % mod_name) - def _cell_magic(self, magic_name, line): + def _run_cached_cell_magic(self, magic_name, line): """Special method to call a cell magic with the data stored in self. """ cell = self._current_cell_magic_body self._current_cell_magic_body = None - return self.cell_magic(magic_name, line, cell) + return self.run_cell_magic(magic_name, line, cell) def run_cell(self, raw_cell, store_history=False, silent=False): """Run a complete IPython cell. diff --git a/IPython/core/tests/test_inputsplitter.py b/IPython/core/tests/test_inputsplitter.py index 2eda04c799b..20054d2205b 100644 --- a/IPython/core/tests/test_inputsplitter.py +++ b/IPython/core/tests/test_inputsplitter.py @@ -759,7 +759,7 @@ def test_whole_cell(self): sp.push(src) nt.assert_equal(sp.cell_magic_parts, ['body\n']) out = sp.source - ref = u"get_ipython()._cell_magic(u'cellm', u'line')\n" + ref = u"get_ipython()._run_cached_cell_magic(u'cellm', u'line')\n" nt.assert_equal(out, ref) def test_incremental(self): @@ -793,7 +793,7 @@ def test_whole_cell(self): sp.push(src) nt.assert_equal(sp.cell_magic_parts, ['body\n']) out = sp.source - ref = u"get_ipython()._cell_magic(u'cellm', u'line')\n" + ref = u"get_ipython()._run_cached_cell_magic(u'cellm', u'line')\n" nt.assert_equal(out, ref) def test_incremental(self): diff --git a/IPython/core/tests/test_magic.py b/IPython/core/tests/test_magic.py index 0b531334c81..296d8953469 100644 --- a/IPython/core/tests/test_magic.py +++ b/IPython/core/tests/test_magic.py @@ -496,7 +496,7 @@ class CellMagicTestCase(TestCase): def check_ident(self, magic): # Manually called, we get the result - out = _ip.cell_magic(magic, 'a', 'b') + out = _ip.run_cell_magic(magic, 'a', 'b') nt.assert_equals(out, ('a','b')) # Via run_cell, it goes into the user's namespace via displayhook _ip.run_cell('%%' + magic +' c\nd') From c28b7cff9dff8021592bf78cc503a48b7f35761c Mon Sep 17 00:00:00 2001 From: Fernando Perez Date: Sat, 26 May 2012 21:00:55 -0700 Subject: [PATCH 098/103] Fix test failures under Python 3. Refactored the test code a bit for better reuse of common functionality. --- IPython/core/tests/test_inputsplitter.py | 33 +++++++++--------------- 1 file changed, 12 insertions(+), 21 deletions(-) diff --git a/IPython/core/tests/test_inputsplitter.py b/IPython/core/tests/test_inputsplitter.py index 20054d2205b..4f8d491e5f2 100644 --- a/IPython/core/tests/test_inputsplitter.py +++ b/IPython/core/tests/test_inputsplitter.py @@ -750,17 +750,23 @@ def test_last_two_blanks(): nt.assert_true(isp.last_two_blanks('abc\nd\ne\nf\n\n\n')) -class CellModeCellMagics(unittest.TestCase): - sp = isp.IPythonInputSplitter(input_mode='cell') - +class CellMagicsCommon(object): + def test_whole_cell(self): src = "%%cellm line\nbody\n" sp = self.sp sp.push(src) nt.assert_equal(sp.cell_magic_parts, ['body\n']) out = sp.source - ref = u"get_ipython()._run_cached_cell_magic(u'cellm', u'line')\n" - nt.assert_equal(out, ref) + ref = u"get_ipython()._run_cached_cell_magic({u}'cellm', {u}'line')\n" + nt.assert_equal(out, py3compat.u_format(ref)) + + def tearDown(self): + self.sp.reset() + + +class CellModeCellMagics(CellMagicsCommon, unittest.TestCase): + sp = isp.IPythonInputSplitter(input_mode='cell') def test_incremental(self): sp = self.sp @@ -780,28 +786,13 @@ def test_incremental(self): sp.push(src) nt.assert_false(sp.push_accepts_more()) #3 - def tearDown(self): - self.sp.reset() - -class LineModeCellMagics(unittest.TestCase): +class LineModeCellMagics(CellMagicsCommon, unittest.TestCase): sp = isp.IPythonInputSplitter(input_mode='line') - def test_whole_cell(self): - src = "%%cellm line\nbody\n" - sp = self.sp - sp.push(src) - nt.assert_equal(sp.cell_magic_parts, ['body\n']) - out = sp.source - ref = u"get_ipython()._run_cached_cell_magic(u'cellm', u'line')\n" - nt.assert_equal(out, ref) - def test_incremental(self): sp = self.sp sp.push('%%cellm line2\n') nt.assert_true(sp.push_accepts_more()) #1 sp.push('\n') nt.assert_false(sp.push_accepts_more()) #2 - - def tearDown(self): - self.sp.reset() From f50a0abfee0778ff6f9ae85635774a5cbdb88ef8 Mon Sep 17 00:00:00 2001 From: Fernando Perez Date: Sat, 26 May 2012 21:24:48 -0700 Subject: [PATCH 099/103] Document cell magics in %magic. %magic is our main interactive explanation of the magic system, so it's a good place to explain the system. --- IPython/core/magics/basic.py | 31 ++++++++++++++++++++++++++++--- 1 file changed, 28 insertions(+), 3 deletions(-) diff --git a/IPython/core/magics/basic.py b/IPython/core/magics/basic.py index 7f062c12463..d1702535d87 100644 --- a/IPython/core/magics/basic.py +++ b/IPython/core/magics/basic.py @@ -120,11 +120,36 @@ def magic(self, parameter_s=''): The magic function system provides a series of functions which allow you to control the behavior of IPython itself, plus a lot of system-type -features. All these functions are prefixed with a % character, but parameters -are given without parentheses or quotes. +features. There are two kinds of magics, line-oriented and cell-oriented. + +Line magics are prefixed with the % character and work much like OS +command-line calls: they get as an argument the rest of the line, where +arguments are passed without parentheses or quotes. For example, this will +time the given statement:: + + %timeit range(1000) + +Cell magics are prefixed with a double %%, and they are functions that get as +an argument not only the rest of the line, but also the lines below it in a +separate argument. These magics are called with two arguments: the rest of the +call line and the body of the cell, consisting of the lines below the first. +For example:: + + %%timeit x = numpy.random.randn((100, 100)) + numpy.linalg.svd(x) + +will time the execution of the numpy svd routine, running the assignment of x +as part of the setup phase, which is not timed. + +In a line-oriented client (the terminal or Qt console IPython), starting a new +input with %% will automatically enter cell mode, and IPython will continue +reading input until a blank line is given. In the notebook, simply type the +whole cell as one entity, but keep in mind that the %% escape can only be at +the very start of the cell. NOTE: If you have 'automagic' enabled (via the command line option or with the -%automagic function), you don't need to type in the % explicitly. By default, +%automagic function), you don't need to type in the % explicitly for line +magics; cell magics always require an explicit '%%' escape. By default, IPython ships with automagic on, so you should only rarely need the % escape. Example: typing '%cd mydir' (without the quotes) changes you working directory From 16171eaca67d5b4e1676662d8c15a109f6a24f5b Mon Sep 17 00:00:00 2001 From: Fernando Perez Date: Sat, 26 May 2012 21:29:20 -0700 Subject: [PATCH 100/103] Update interactive usage message (used by %quickref and others). --- IPython/core/usage.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/IPython/core/usage.py b/IPython/core/usage.py index 90506b31672..309d1e21324 100644 --- a/IPython/core/usage.py +++ b/IPython/core/usage.py @@ -258,8 +258,9 @@ ?foo.*abc* : List names in 'foo' containing 'abc' in them. %magic : Information about IPython's 'magic' % functions. -Magic functions are prefixed by %, and typically take their arguments without -parentheses, quotes or even commas for convenience. +Magic functions are prefixed by % or %%, and typically take their arguments +without parentheses, quotes or even commas for convenience. Line magics take a +single % and cell magics are prefixed with two %%. Example magic function calls: @@ -268,6 +269,10 @@ alist = %alias : Get list of aliases to 'alist' cd /usr/share : Obvious. cd - to choose from visited dirs. %cd?? : See help AND source for magic %cd +%timeit x=10 : time the 'x=10' statement with high precision. +%%timeit x=2**100 +x**100 : time 'x*100' with a setup of 'x=2**100'; setup code is not + counted. This is an example of a cell magic. System commands: From 787e286a3cc85447365e86c1cf3419880d20beca Mon Sep 17 00:00:00 2001 From: Fernando Perez Date: Sat, 26 May 2012 21:36:39 -0700 Subject: [PATCH 101/103] Implement %%timeit as a cell level magic. --- IPython/core/magics/execution.py | 36 ++++++++++++++++++++++++-------- 1 file changed, 27 insertions(+), 9 deletions(-) diff --git a/IPython/core/magics/execution.py b/IPython/core/magics/execution.py index 7ddf6db82c4..f3b9c32bcba 100644 --- a/IPython/core/magics/execution.py +++ b/IPython/core/magics/execution.py @@ -36,8 +36,8 @@ from IPython.core import page from IPython.core.error import UsageError from IPython.core.macro import Macro -from IPython.core.magic import (Magics, magics_class, line_magic, - on_off, needs_local_scope) +from IPython.core.magic import (Magics, magics_class, line_magic, + line_cell_magic, on_off, needs_local_scope) from IPython.testing.skipdoctest import skip_doctest from IPython.utils import py3compat from IPython.utils.ipstruct import Struct @@ -641,15 +641,26 @@ def run(self, parameter_s='', runner=None, return stats @skip_doctest - @line_magic - def timeit(self, parameter_s=''): + @line_cell_magic + def timeit(self, line='', cell=None): """Time execution of a Python statement or expression - Usage:\\ + Usage, in line mode: %timeit [-n -r [-t|-c]] statement + or in cell mode: + %%timeit [-n -r [-t|-c]] setup_code + code + code... Time execution of a Python statement or expression using the timeit - module. + module. This function can be used both as a line and cell magic: + + - In line mode you can time a single-line statement (though multiple + ones can be chained with using semicolons). + + - In cell mode, the statement in the first line is used as setup code + (executed but not timed) and the body of the cell is timed. The cell + body has access to any variables created in the setup code. Options: -n: execute the given statement times in a loop. If this value @@ -725,7 +736,7 @@ def timeit(self, parameter_s=''): scaling = [1, 1e3, 1e6, 1e9] - opts, stmt = self.parse_options(parameter_s,'n:r:tcp:', + opts, stmt = self.parse_options(line,'n:r:tcp:', posix=False, strict=False) if stmt == "": return @@ -743,8 +754,15 @@ def timeit(self, parameter_s=''): # but is there a better way to achieve that the code stmt has access # to the shell namespace? - src = timeit.template % {'stmt': timeit.reindent(stmt, 8), - 'setup': "pass"} + if cell is None: + # called as line magic + setup = 'pass' + stmt = timeit.reindent(stmt, 8) + else: + setup = timeit.reindent(stmt, 4) + stmt = timeit.reindent(cell, 8) + + src = timeit.template % dict(stmt=stmt, setup=setup) # Track compilation time so it can be reported if too long # Minimum time above which compilation time will be reported tc_min = 0.1 From 8045334b849f6fdee6cb45d4360d03311b279f65 Mon Sep 17 00:00:00 2001 From: Fernando Perez Date: Sat, 26 May 2012 21:51:30 -0700 Subject: [PATCH 102/103] Implement %%prun as a cell magic too. --- IPython/core/magics/execution.py | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/IPython/core/magics/execution.py b/IPython/core/magics/execution.py index f3b9c32bcba..3dab02bffc3 100644 --- a/IPython/core/magics/execution.py +++ b/IPython/core/magics/execution.py @@ -70,15 +70,25 @@ def profile_missing_notice(self, *args, **kwargs): python-profiler package from non-free.""") @skip_doctest - @line_magic - def prun(self, parameter_s='',user_mode=1, + @line_cell_magic + def prun(self, parameter_s='', cell=None, user_mode=True, opts=None,arg_lst=None,prog_ns=None): """Run a statement through the python code profiler. - Usage: + Usage, in line mode: %prun [options] statement + Usage, in cell mode: + %%prun [options] [statement] + code... + code... + + In cell mode, the additional code lines are appended to the (possibly + empty) statement in the first line. Cell mode allows you to easily + profile multiline blocks without having to put them in a separate + function. + The given statement (which doesn't require quote marks) is run via the python profiler in a manner similar to the profile.run() function. Namespaces are internally managed to work correctly; profile.run @@ -167,8 +177,10 @@ def prun(self, parameter_s='',user_mode=1, if user_mode: # regular user call opts,arg_str = self.parse_options(parameter_s,'D:l:rs:T:q', - list_all=1, posix=False) + list_all=True, posix=False) namespace = self.shell.user_ns + if cell is not None: + arg_str += '\n' + cell else: # called to run a program by %run -p try: filename = get_py_filename(arg_lst[0]) @@ -516,7 +528,7 @@ def run(self, parameter_s='', runner=None, stats = None with self.shell.readline_no_record: if 'p' in opts: - stats = self.prun('', 0, opts, arg_lst, prog_ns) + stats = self.prun('', None, False, opts, arg_lst, prog_ns) else: if 'd' in opts: deb = debugger.Pdb(self.shell.colors) From 0876e92fbca0a4e0155b4888e7d69942f7025525 Mon Sep 17 00:00:00 2001 From: Fernando Perez Date: Sat, 26 May 2012 23:17:07 -0700 Subject: [PATCH 103/103] Update the main documentation with new magics API. Added detailed description to the docs, as well as comprehensive examples of magic creation with the new APIs. --- docs/source/interactive/reference.txt | 185 +++++++++++++++++++++++--- docs/source/interactive/tutorial.txt | 36 ++++- 2 files changed, 194 insertions(+), 27 deletions(-) diff --git a/docs/source/interactive/reference.txt b/docs/source/interactive/reference.txt index 9a78c8c29a8..225ed3cbefc 100644 --- a/docs/source/interactive/reference.txt +++ b/docs/source/interactive/reference.txt @@ -100,17 +100,45 @@ IPython itself, plus a lot of system-type features. They are all prefixed with a % character, but parameters are given without parentheses or quotes. -Example: typing ``%cd mydir`` changes your working directory to 'mydir', if it -exists. - -If you have 'automagic' enabled (as it by default), you don't need -to type in the % explicitly. IPython will scan its internal list of -magic functions and call one if it exists. With automagic on you can -then just type ``cd mydir`` to go to directory 'mydir'. The automagic -system has the lowest possible precedence in name searches, so defining -an identifier with the same name as an existing magic function will -shadow it for automagic use. You can still access the shadowed magic -function by explicitly using the % character at the beginning of the line. +Lines that begin with ``%%`` signal a *cell magic*: they take as arguments not +only the rest of the current line, but all lines below them as well, in the +current execution block. Cell magics can in fact make arbitrary modifications +to the input they receive, which need not even be valid Python code at all. +They receive the whole block as a single string. + +As a line magic example, the ``%cd`` magic works just like the OS command of +the same name:: + + In [8]: %cd + /home/fperez + +The following uses the builtin ``timeit`` in cell mode:: + + In [10]: %%timeit x = range(10000) + ...: min(x) + ...: max(x) + ...: + 1000 loops, best of 3: 438 us per loop + +In this case, ``x = range(10000)`` is called as the line argument, and the +block with ``min(x)`` and ``max(x)`` is called as the cell body. The +``timeit`` magic receives both. + +If you have 'automagic' enabled (as it by default), you don't need to type in +the single ``%`` explicitly for line magics; IPython will scan its internal +list of magic functions and call one if it exists. With automagic on you can +then just type ``cd mydir`` to go to directory 'mydir':: + + In [9]: cd mydir + /home/fperez/mydir + +Note that cell magics *always* require an explicit ``%%`` prefix, automagic +calling only works for line magics. + +The automagic system has the lowest possible precedence in name searches, so +defining an identifier with the same name as an existing magic function will +shadow it for automagic use. You can still access the shadowed magic function +by explicitly using the ``%`` character at the beginning of the line. An example (with automagic on) should clarify all this: @@ -137,24 +165,141 @@ An example (with automagic on) should clarify all this: /home/fperez/ipython -You can define your own magic functions to extend the system. The -following example defines a new magic command, %impall: +Defining your own magics +~~~~~~~~~~~~~~~~~~~~~~~~ + +There are two main ways to define your own magic functions: from standalone +functions and by inheriting from a base class provided by IPython: +:class:`IPython.core.magic.Magics`. Below we show code you can place in a file +that you load from your configuration, such as any file in the ``startup`` +subdirectory of your default IPython profile. + +First, let us see the simplest case. The following shows how to create a line +magic, a cell one and one that works in both modes, using just plain functions: .. sourcecode:: python + from IPython.core.magic import (register_line_magic, register_cell_magic, + register_line_cell_magic) + + @register_line_magic + def lmagic(line): + "my line magic" + return line + + @register_cell_magic + def cmagic(line, cell): + "my cell magic" + return line, cell + + @register_line_cell_magic + def lcmagic(line, cell=None): + "Magic that works both as %lcmagic and as %%lcmagic" + if cell is None: + print "Called as line magic" + return line + else: + print "Called as cell magic" + return line, cell + + # We delete these to avoid name conflicts for automagic to work + del lmagic, lcmagic + + +You can also create magics of all three kinds by inheriting from the +:class:`IPython.core.magic.Magics` class. This lets you create magics that can +potentially hold state in between calls, and that have full access to the main +IPython object: + +.. sourcecode:: python + + # This code can be put in any Python module, it does not require IPython + # itself to be running already. It only creates the magics subclass but + # doesn't instantiate it yet. + from IPython.core.magic import (Magics, magics_class, line_magic, + cell_magic, line_cell_magic) + + # The class MUST call this class decorator at creation time + @magics_class + class MyMagics(Magics): + + @line_magic + def lmagic(self, line): + "my line magic" + print "Full access to the main IPython object:", self.shell + print "Variables in the user namespace:", self.user_ns.keys() + return line + + @cell_magic + def cmagic(self, line, cell): + "my cell magic" + return line, cell + + @line_cell_magic + def lcmagic(self, line, cell=None): + "Magic that works both as %lcmagic and as %%lcmagic" + if cell is None: + print "Called as line magic" + return line + else: + print "Called as cell magic" + return line, cell + + + # In order to actually use these magics, you must register them with a + # running IPython. This code must be placed in a file that is loaded once + # IPython is up and running: ip = get_ipython() + # You can register the class itself without instantiating it. IPython will + # call the default constructor on it. + ip.register_magics(MyMagics) - def doimp(self, arg): - ip = self.api - ip.ex("import %s; reload(%s); from %s import *" % (arg,arg,arg) ) +If you want to create a class with a different constructor that holds +additional state, then you should always call the parent constructor and +instantiate the class yourself before registration: - ip.define_magic('impall', doimp) +.. sourcecode:: python + + @magics_class + class StatefulMagics(Magics): + "Magics that hold additional state" + + def __init__(self, shell, data): + # You must call the parent constructor + super(StatefulMagics, self).__init__(shell) + self.data = data + + # etc... + + # This class must then be registered with a manually created instance, + # since its constructor has different arguments from the default: + ip = get_ipython() + magics = StatefulMagics(ip, some_data) + ip.register_magics(magics) + + +In earlier versions, IPython had an API for the creation of line magics (cell +magics did not exist at the time) that required you to create functions with a +method-looking signature and to manually pass both the function and the name. +While this API is no longer recommended, it remains indefinitely supported for +backwards compatibility purposes. With the old API, you'd create a magic as +follows: + +.. sourcecode:: python + + def func(self, line): + print "Line magic called with line:", line + print "IPython object:", self.shell + + ip = get_ipython() + # Declare this function as the magic %mycommand + ip.define_magic('mycommand', func) Type ``%magic`` for more information, including a list of all available magic functions at any time and their docstrings. You can also type -``%magic_function_name?`` (see :ref:`below ` for information on -the '?' system) to get information about any particular magic function you are -interested in. +``%magic_function_name?`` (see :ref:`below ` for +information on the '?' system) to get information about any particular magic +function you are interested in. The API documentation for the :mod:`IPython.core.magic` module contains the full docstrings of all currently available magic commands. diff --git a/docs/source/interactive/tutorial.txt b/docs/source/interactive/tutorial.txt index 107a290d81d..011beae92f0 100644 --- a/docs/source/interactive/tutorial.txt +++ b/docs/source/interactive/tutorial.txt @@ -35,20 +35,42 @@ Magic functions =============== IPython has a set of predefined 'magic functions' that you can call with a -command line style syntax. These include: +command line style syntax. There are two kinds of magics, line-oriented and +cell-oriented. Line magics are prefixed with the ``%`` character and work much +like OS command-line calls: they get as an argument the rest of the line, where +arguments are passed without parentheses or quotes. Cell magics are prefixed +with a double ``%%``, and they are functions that get as an argument not only +the rest of the line, but also the lines below it in a separate argument. + +The following examples show how to call the builtin ``timeit`` magic, both in +line and cell mode:: + + In [1]: %timeit range(1000) + 100000 loops, best of 3: 7.76 us per loop + + In [2]: %%timeit x = range(10000) + ...: max(x) + ...: + 1000 loops, best of 3: 223 us per loop + +The builtin magics include: - Functions that work with code: ``%run``, ``%edit``, ``%save``, ``%macro``, ``%recall``, etc. -- Functions which affect the shell: ``%colors``, ``%xmode``, ``%autoindent``, etc. +- Functions which affect the shell: ``%colors``, ``%xmode``, ``%autoindent``, + etc. - Other functions such as ``%reset``, ``%timeit`` or ``%paste``. -You can always call these using the % prefix, and if you're typing one on a line -by itself, you can omit even that:: +You can always call them using the % prefix, and if you're calling a line magic +on a line by itself, you can omit even that (cell magics must always have the +``%%`` prefix):: run thescript.py - -For more details on any magic function, call ``%somemagic?`` to read its -docstring. To see all the available magic functions, call ``%lsmagic``. + +A more detailed explanation of the magic system can be obtained by calling +``%magic``, and for more details on any magic function, call ``%somemagic?`` to +read its docstring. To see all the available magic functions, call +``%lsmagic``. Running and Editing -------------------