Repository: ipython/ipython

Issue:
%magic doesn't work
```
In [48]: %magic
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
<ipython-input-48-99e14b2858dd> in <module>()
----> 1 get_ipython().magic(u'magic')

/Users/aaronmeurer/Documents/ipython/IPython/core/interactiveshell.pyc in magic(self, arg_s)
   2180         magic_name, _, magic_arg_s = arg_s.partition(' ')
   2181         magic_name = magic_name.lstrip(prefilter.ESC_MAGIC)
-> 2182         return self.run_line_magic(magic_name, magic_arg_s)
   2183
   2184     #-------------------------------------------------------------------------

/Users/aaronmeurer/Documents/ipython/IPython/core/interactiveshell.pyc in run_line_magic(self, magic_name, line)
   2101                 kwargs['local_ns'] = sys._getframe(stack_depth).f_locals
   2102             with self.builtin_trap:
-> 2103                 result = fn(*args,**kwargs)
   2104             return result
   2105

/Users/aaronmeurer/Documents/ipython/IPython/core/magics/basic.pyc in magic(self, parameter_s)

/Users/aaronmeurer/Documents/ipython/IPython/core/magic.pyc in <lambda>(f, *a, **k)
    190     # but it's overkill for just that one bit of state.
    191     def magic_deco(arg):
--> 192         call = lambda f, *a, **k: f(*a, **k)
    193
    194         if callable(arg):

/Users/aaronmeurer/Documents/ipython/IPython/core/magics/basic.pyc in magic(self, parameter_s)
    272        magic_docs,
    273        "Summary of magic functions (from %slsmagic):" % magic_escapes['line'],
--> 274        self._lsmagic(),
    275        ]
    276         page.page('\n'.join(out))

AttributeError: 'BasicMagics' object has no attribute '_lsmagic'
```



Discussion:
Probably due to #3319 


Resolving patch:
diff --git a/IPython/core/magics/basic.py b/IPython/core/magics/basic.py
--- a/IPython/core/magics/basic.py
+++ b/IPython/core/magics/basic.py
@@ -271,7 +271,7 @@ def magic(self, parameter_s=''):
 Currently the magic system has the following functions:""",
        magic_docs,
        "Summary of magic functions (from %slsmagic):" % magic_escapes['line'],
-       self._lsmagic(),
+       str(self.lsmagic()),
        ]
         page.page('\n'.join(out))
 


Test patch:
diff --git a/IPython/core/tests/test_magic.py b/IPython/core/tests/test_magic.py
--- a/IPython/core/tests/test_magic.py
+++ b/IPython/core/tests/test_magic.py
@@ -34,6 +34,7 @@ def invalidate_caches():
 from IPython.testing import decorators as dec
 from IPython.testing import tools as tt
 from IPython.utils import py3compat
+from IPython.utils.io import capture_output
 from IPython.utils.tempdir import TemporaryDirectory
 from IPython.utils.process import find_cmd
 
@@ -53,14 +54,14 @@ def test_rehashx():
     _ip.magic('rehashx')
     # Practically ALL ipython development systems will have more than 10 aliases
 
-    yield (nt.assert_true, len(_ip.alias_manager.alias_table) > 10)
+    nt.assert_true(len(_ip.alias_manager.alias_table) > 10)
     for key, val in _ip.alias_manager.alias_table.iteritems():
         # we must strip dots from alias names
-        nt.assert_true('.' not in key)
+        nt.assert_not_in('.', key)
 
     # rehashx must fill up syscmdlist
     scoms = _ip.db['syscmdlist']
-    yield (nt.assert_true, len(scoms) > 10)
+    nt.assert_true(len(scoms) > 10)
 
 
 def test_magic_parse_options():
@@ -81,9 +82,9 @@ def test_magic_parse_long_options():
     ip = get_ipython()
     m = DummyMagics(ip)
     opts, _ = m.parse_options('--foo --bar=bubble', 'a', 'foo', 'bar=')
-    nt.assert_true('foo' in opts)
-    nt.assert_true('bar' in opts)
-    nt.assert_true(opts['bar'], "bubble")
+    nt.assert_in('foo', opts)
+    nt.assert_in('bar', opts)
+    nt.assert_equal(opts['bar'], "bubble")
 
 
 @dec.skip_without('sqlite3')
@@ -197,8 +198,8 @@ def test_macro():
     ip.magic("macro test 1-3")
     nt.assert_equal(ip.user_ns["test"].value, "\n".join(cmds)+"\n")
     
-    # List macros.
-    assert "test" in ip.magic("macro")
+    # List macros
+    nt.assert_in("test", ip.magic("macro"))
 
 
 @dec.skip_without('sqlite3')
@@ -218,14 +219,26 @@ def test_macro_run():
         ip.run_cell("test")
 
 
+def test_magic_magic():
+    """Test %magic"""
+    ip = get_ipython()
+    with capture_output() as captured:
+        ip.magic("magic")
+    
+    stdout = captured.stdout
+    nt.assert_in('%magic', stdout)
+    nt.assert_in('IPython', stdout)
+    nt.assert_in('Available', stdout)
+
+
 @dec.skipif_not_numpy
 def test_numpy_reset_array_undec():
     "Test '%reset array' functionality"
     _ip.ex('import numpy as np')
     _ip.ex('a = np.empty(2)')
-    yield (nt.assert_true, 'a' in _ip.user_ns)
+    nt.assert_in('a', _ip.user_ns)
     _ip.magic('reset -f array')
-    yield (nt.assert_false, 'a' in _ip.user_ns)
+    nt.assert_not_in('a', _ip.user_ns)
 
 def test_reset_out():
     "Test '%reset out' magic"
@@ -235,7 +248,7 @@ def test_reset_out():
     nt.assert_true('dead' in [_ip.user_ns[x] for x in '_','__','___'])
     _ip.magic('reset -f out')
     nt.assert_false('dead' in [_ip.user_ns[x] for x in '_','__','___'])
-    nt.assert_true(len(_ip.user_ns['Out']) == 0)
+    nt.assert_equal(len(_ip.user_ns['Out']), 0)
 
 def test_reset_in():
     "Test '%reset in' magic"
@@ -244,7 +257,7 @@ def test_reset_in():
     nt.assert_true('parrot' in [_ip.user_ns[x] for x in '_i','_ii','_iii'])
     _ip.magic('%reset -f in')
     nt.assert_false('parrot' in [_ip.user_ns[x] for x in '_i','_ii','_iii'])
-    nt.assert_true(len(set(_ip.user_ns['In'])) == 1)
+    nt.assert_equal(len(set(_ip.user_ns['In'])), 1)
 
 def test_reset_dhist():
     "Test '%reset dhist' magic"
@@ -253,14 +266,14 @@ def test_reset_dhist():
     _ip.magic('cd -')
     nt.assert_true(len(_ip.user_ns['_dh']) > 0)
     _ip.magic('reset -f dhist')
-    nt.assert_true(len(_ip.user_ns['_dh']) == 0)
+    nt.assert_equal(len(_ip.user_ns['_dh']), 0)
     _ip.run_cell("_dh = [d for d in tmp]") #restore
 
 def test_reset_in_length():
     "Test that '%reset in' preserves In[] length"
     _ip.run_cell("print 'foo'")
     _ip.run_cell("reset -f in")
-    nt.assert_true(len(_ip.user_ns['In']) == _ip.displayhook.prompt_count+1)
+    nt.assert_equal(len(_ip.user_ns['In']), _ip.displayhook.prompt_count+1)
 
 def test_tb_syntaxerror():
     """test %tb after a SyntaxError"""
@@ -756,13 +769,13 @@ def test_alias_magic():
 
     # Basic operation: both cell and line magics are created, if possible.
     ip.run_line_magic('alias_magic', 'timeit_alias timeit')
-    nt.assert_true('timeit_alias' in mm.magics['line'])
-    nt.assert_true('timeit_alias' in mm.magics['cell'])
+    nt.assert_in('timeit_alias', mm.magics['line'])
+    nt.assert_in('timeit_alias', mm.magics['cell'])
 
     # --cell is specified, line magic not created.
     ip.run_line_magic('alias_magic', '--cell timeit_cell_alias timeit')
-    nt.assert_false('timeit_cell_alias' in mm.magics['line'])
-    nt.assert_true('timeit_cell_alias' in mm.magics['cell'])
+    nt.assert_not_in('timeit_cell_alias', mm.magics['line'])
+    nt.assert_in('timeit_cell_alias', mm.magics['cell'])
 
     # Test that line alias is created successfully.
     ip.run_line_magic('alias_magic', '--line env_alias env')
@@ -782,12 +795,12 @@ def test_save():
         with open(file) as f:
             content = f.read()
             nt.assert_equal(content.count(cmds[0]), 1)
-            nt.assert_true('coding: utf-8' in content)
+            nt.assert_in('coding: utf-8', content)
         ip.run_line_magic("save", "-a %s 1-10" % file)
         with open(file) as f:
             content = f.read()
             nt.assert_equal(content.count(cmds[0]), 2)
-            nt.assert_true('coding: utf-8' in content)
+            nt.assert_in('coding: utf-8', content)
 
 
 def test_store():