diff --git a/IPython/core/interactiveshell.py b/IPython/core/interactiveshell.py index e11eef1..c2bbdd4 100644 --- a/IPython/core/interactiveshell.py +++ b/IPython/core/interactiveshell.py @@ -2053,6 +2053,12 @@ class InteractiveShell(SingletonConfigurable): m.NamespaceMagics, m.OSMagics, m.PylabMagics, m.ScriptMagics, ) + # Register Magic Aliases + mman = self.magics_manager + mman.register_alias('ed', 'edit') + mman.register_alias('hist', 'history') + mman.register_alias('rep', 'recall') + # 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. diff --git a/IPython/core/magic.py b/IPython/core/magic.py index 1be6d12..010f311 100644 --- a/IPython/core/magic.py +++ b/IPython/core/magic.py @@ -452,6 +452,36 @@ class MagicsManager(Configurable): setattr(self.user_magics, name, meth) record_magic(self.magics, 'line', name, meth) + def register_alias(self, alias_name, magic_name, magic_kind='line'): + """Register an alias to a magic function. + + The alias is an instance of :class:`MagicAlias`, which holds the + name and kind of the magic it should call. Binding is done at + call time, so if the underlying magic function is changed the alias + will call the new function. + + Parameters + ---------- + alias_name : str + The name of the magic to be registered. + + magic_name : str + The name of an existing magic. + + magic_kind : str + Kind of magic, one of 'line' or 'cell' + """ + + # `validate_type` is too permissive, as it allows 'line_cell' + # which we do not handle. + if magic_kind not in magic_kinds: + raise ValueError('magic_kind must be one of %s, %s given' % + magic_kinds, magic_kind) + + alias = MagicAlias(self.shell, magic_name, magic_kind) + setattr(self.user_magics, alias_name, alias) + record_magic(self.magics, magic_kind, alias_name, alias) + # Key base class that provides the central functionality for magics. class Magics(object): @@ -615,3 +645,45 @@ class Magics(object): if fn not in self.lsmagic(): error("%s is not a magic function" % fn) self.options_table[fn] = optstr + +class MagicAlias(object): + """Store a magic alias. + + An alias is determined by its magic name and magic kind. Lookup + is done at call time, so if the underlying magic changes the alias + will call the new function. + + Use the :meth:`MagicsManager.register_alias` method or the + `%alias_magic` magic function to create and register a new alias. + """ + def __init__(self, shell, magic_name, magic_kind): + self.shell = shell + self.magic_name = magic_name + self.magic_kind = magic_kind + + self._in_call = False + + @property + def pretty_target(self): + """A formatted version of the target of the alias.""" + return '%s%s' % (magic_escapes[self.magic_kind], self.magic_name) + + @property + def __doc__(self): + return "Alias for `%s`." % self.pretty_target + + def __call__(self, *args, **kwargs): + """Call the magic alias.""" + fn = self.shell.find_magic(self.magic_name, self.magic_kind) + if fn is None: + raise UsageError("Magic `%s` not found." % self.pretty_target) + + # Protect against infinite recursion. + if self._in_call: + raise UsageError("Infinite recursion detected; " + "magic aliases cannot call themselves.") + self._in_call = True + try: + return fn(*args, **kwargs) + finally: + self._in_call = False diff --git a/IPython/core/magics/basic.py b/IPython/core/magics/basic.py index 6b75f17..5825640 100644 --- a/IPython/core/magics/basic.py +++ b/IPython/core/magics/basic.py @@ -65,6 +65,8 @@ class BasicMagics(Magics): -------- :: In [1]: %alias_magic t timeit + Created `%t` as an alias for `%timeit`. + Created `%%t` as an alias for `%%timeit`. In [2]: %t -n1 pass 1 loops, best of 3: 954 ns per loop @@ -77,12 +79,14 @@ class BasicMagics(Magics): In [4]: %alias_magic --cell whereami pwd UsageError: Cell magic function `%%pwd` not found. In [5]: %alias_magic --line whereami pwd + Created `%whereami` as an alias for `%pwd`. In [6]: %whereami Out[6]: u'/home/testuser' """ args = magic_arguments.parse_argstring(self.alias_magic, line) shell = self.shell + mman = self.shell.magics_manager escs = ''.join(magic_escapes.values()) target = args.target.lstrip(escs) @@ -109,18 +113,16 @@ class BasicMagics(Magics): args.cell = bool(m_cell) if args.line: - def wrapper(line): return m_line(line) - wrapper.__name__ = str(name) - wrapper.__doc__ = "Alias for `%s%s`." % \ - (magic_escapes['line'], target) - shell.register_magic_function(wrapper, 'line', name) + mman.register_alias(name, target, 'line') + print('Created `%s%s` as an alias for `%s%s`.' % ( + magic_escapes['line'], name, + magic_escapes['line'], target)) if args.cell: - def wrapper(line, cell): return m_cell(line, cell) - wrapper.__name__ = str(name) - wrapper.__doc__ = "Alias for `%s%s`." % \ - (magic_escapes['cell'], target) - shell.register_magic_function(wrapper, 'cell', name) + mman.register_alias(name, target, 'cell') + print('Created `%s%s` as an alias for `%s%s`.' % ( + magic_escapes['cell'], name, + magic_escapes['cell'], target)) def _lsmagic(self): mesc = magic_escapes['line'] diff --git a/IPython/core/magics/code.py b/IPython/core/magics/code.py index 5414f4f..47df10f 100644 --- a/IPython/core/magics/code.py +++ b/IPython/core/magics/code.py @@ -333,11 +333,6 @@ class CodeMagics(Magics): 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=['','']): @@ -431,7 +426,7 @@ class CodeMagics(Magics): This is an example of creating a simple function inside the editor and then modifying it. First, start up the editor:: - In [1]: ed + In [1]: edit Editing... done. Executing edited code... Out[1]: 'def foo():\\n print "foo() was defined in an editing session"\\n' @@ -444,7 +439,7 @@ class CodeMagics(Magics): Now we edit foo. IPython automatically loads the editor with the (temporary) file where foo() was previously defined:: - In [3]: ed foo + In [3]: edit foo Editing... done. Executing edited code... And if we call foo() again we get the modified version:: @@ -455,21 +450,21 @@ class CodeMagics(Magics): Here is an example of how to edit a code snippet successive times. First we call the editor:: - In [5]: ed + In [5]: edit 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 _ + In [6]: edit _ 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 + In [7]: edit _8 Editing... done. Executing edited code... hello again Out[7]: "print 'hello again'\\n" diff --git a/IPython/core/magics/history.py b/IPython/core/magics/history.py index f5076b6..501ba62 100644 --- a/IPython/core/magics/history.py +++ b/IPython/core/magics/history.py @@ -91,10 +91,10 @@ class HistoryMagics(Magics): -------- :: - In [6]: %hist -n 4-6 + In [6]: %history -n 4-6 4:a = 12 5:print a**2 - 6:%hist -n 4-6 + 6:%history -n 4-6 """ @@ -187,15 +187,8 @@ class HistoryMagics(Magics): 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): + def recall(self, arg): r"""Repeat a command, or get command to input line for editing. %recall and %rep are equivalent. @@ -209,7 +202,7 @@ class HistoryMagics(Magics): In[1]: l = ["hei", "vaan"] In[2]: "".join(l) Out[2]: heivaan - In[3]: %rep + In[3]: %recall In[4]: heivaan_ <== cursor blinking %recall 45 @@ -244,7 +237,7 @@ class HistoryMagics(Magics): 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: + if 'recall' in h or 'rep' in h: continue self.shell.set_next_input(h.rstrip()) return @@ -292,10 +285,3 @@ class HistoryMagics(Magics): print(histlines) print("=== Output: ===") self.shell.run_cell("\n".join(hist), store_history=False) - - @line_magic - def recall(self,arg): - self.rep(arg) - - recall.__doc__ = rep.__doc__ - diff --git a/IPython/zmq/zmqshell.py b/IPython/zmq/zmqshell.py index 60fa06d..0691ef6 100644 --- a/IPython/zmq/zmqshell.py +++ b/IPython/zmq/zmqshell.py @@ -575,7 +575,7 @@ class ZMQInteractiveShell(InteractiveShell): def init_magics(self): super(ZMQInteractiveShell, self).init_magics() self.register_magics(KernelMagics) - self.run_line_magic('alias_magic', 'ed edit') + self.magics_manager.register_alias('ed', 'edit')