From 12e05563fce19803bd8b146a57505a7120150bb4 2016-06-30 09:55:48
From: Thomas Kluyver <thomas@kluyver.me.uk>
Date: 2016-06-30 09:55:48
Subject: [PATCH] Handle bad __all__ for module completions

Closes gh-9678

---

diff --git a/IPython/core/completerlib.py b/IPython/core/completerlib.py
index 57dd2b5..3fbc7e6 100644
--- a/IPython/core/completerlib.py
+++ b/IPython/core/completerlib.py
@@ -153,7 +153,6 @@ def is_importable(module, attr, only_modules):
     else:
         return not(attr[:2] == '__' and attr[-2:] == '__')
 
-
 def try_import(mod, only_modules=False):
     try:
         m = __import__(mod)
@@ -173,9 +172,8 @@ def try_import(mod, only_modules=False):
     completions.extend(getattr(m, '__all__', []))
     if m_is_init:
         completions.extend(module_list(os.path.dirname(m.__file__)))
-    completions = set(completions)
-    if '__init__' in completions:
-        completions.remove('__init__')
+    completions = {c for c in completions if isinstance(c, string_types)}
+    completions.discard('__init__')
     return list(completions)
 
 
diff --git a/IPython/core/tests/bad_all.py b/IPython/core/tests/bad_all.py
new file mode 100644
index 0000000..a7716ab
--- /dev/null
+++ b/IPython/core/tests/bad_all.py
@@ -0,0 +1,14 @@
+"""Module with bad __all__
+
+To test https://github.com/ipython/ipython/issues/9678
+"""
+
+def evil():
+    pass
+
+def puppies():
+    pass
+
+__all__ = [evil,       # Bad
+           'puppies',  # Good
+          ]
diff --git a/IPython/core/tests/test_completerlib.py b/IPython/core/tests/test_completerlib.py
index 9cc4609..71a6cd2 100644
--- a/IPython/core/tests/test_completerlib.py
+++ b/IPython/core/tests/test_completerlib.py
@@ -145,3 +145,19 @@ def test_import_invalid_module():
         nt.assert_equal(intersection, set())
 
         assert valid_module_names.issubset(s), valid_module_names.intersection(s)
+
+
+def test_bad_module_all():
+    """Test module with invalid __all__
+
+    https://github.com/ipython/ipython/issues/9678
+    """
+    testsdir = os.path.dirname(__file__)
+    sys.path.insert(0, testsdir)
+    try:
+        results = module_completion('from bad_all import ')
+        nt.assert_in('puppies', results)
+        for r in results:
+            nt.assert_is_instance(r, py3compat.string_types)
+    finally:
+        sys.path.remove(testsdir)