Repository: ipython/ipython

Issue:
namespace changes broke `foo.*bar*?` syntax
On master we now have:

```
In [2]: sys.*pt*?
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
/home/fperez/usr/lib/python2.7/site-packages/IPython/core/oinspect.pyc in psearch(self, pattern, ns_table, ns_search, ignore_case, show_all)
    760             if name not in ns_table:
    761                 raise ValueError('invalid namespace <%s>. Valid names: %s' %
--> 762                                  (name,ns_table.keys()))
    763 
    764         #print 'type_pattern:',type_pattern # dbg


ValueError: invalid namespace <user>. Valid names: ['alias', 'user_global', 'builtin', 'user_local']
```

We should add a test for this form of call before fixing it, so that we're sure the bug doesn't come back.



Discussion:
Should be a tiny fix, but should it be user_local or user_global?

user_local, I think.  Sorry I didn't fix it right away, I caught it during the lecture today and just moved on :)


Resolving patch:
diff --git a/IPython/core/interactiveshell.py b/IPython/core/interactiveshell.py
--- a/IPython/core/interactiveshell.py
+++ b/IPython/core/interactiveshell.py
@@ -923,7 +923,7 @@ def init_create_namespaces(self, user_module=None, user_ns=None):
         # A table holding all the namespaces IPython deals with, so that
         # introspection facilities can search easily.
         self.ns_table = {'user_global':self.user_module.__dict__,
-                         'user_local':user_ns,
+                         'user_local':self.user_ns,
                          'builtin':builtin_mod.__dict__
                          }
     
diff --git a/IPython/core/magic.py b/IPython/core/magic.py
--- a/IPython/core/magic.py
+++ b/IPython/core/magic.py
@@ -693,7 +693,7 @@ def magic_psearch(self, parameter_s=''):
             return
 
         # default namespaces to be searched
-        def_search = ['user','builtin']
+        def_search = ['user_local', 'user_global', 'builtin']
 
         # Process options/args
         opts,args = self.parse_options(parameter_s,'cias:e:',list_all=True)
diff --git a/IPython/core/oinspect.py b/IPython/core/oinspect.py
--- a/IPython/core/oinspect.py
+++ b/IPython/core/oinspect.py
@@ -762,13 +762,15 @@ def psearch(self,pattern,ns_table,ns_search=[],
                                  (name,ns_table.keys()))
 
         #print 'type_pattern:',type_pattern # dbg
-        search_result = []
+        search_result, namespaces_seen = set(), set()
         for ns_name in ns_search:
             ns = ns_table[ns_name]
-            tmp_res = list(list_namespace(ns,type_pattern,filter,
-                                          ignore_case=ignore_case,
-                                          show_all=show_all))
-            search_result.extend(tmp_res)
-        search_result.sort()
-
-        page.page('\n'.join(search_result))
+            # Normally, locals and globals are the same, so we just check one.
+            if id(ns) in namespaces_seen:
+                continue
+            namespaces_seen.add(id(ns))
+            tmp_res = list_namespace(ns, type_pattern, filter,
+                                    ignore_case=ignore_case, show_all=show_all)
+            search_result.update(tmp_res)
+
+        page.page('\n'.join(sorted(search_result)))


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
@@ -340,3 +340,7 @@ def doctest_precision():
     Out[5]: {u}'3.141593e+00'
     """
 
+def test_psearch():
+    with tt.AssertPrints("dict.fromkeys"):
+        _ip.run_cell("dict.fr*?")
+