diff --git a/IPython/core/completerlib.py b/IPython/core/completerlib.py index 8bece55..f739ca0 100644 --- a/IPython/core/completerlib.py +++ b/IPython/core/completerlib.py @@ -83,10 +83,15 @@ def module_list(path): pjoin = os.path.join basename = os.path.basename + def is_importable_file(path): + """Returns True if the provided path is a valid importable module""" + name, extension = os.path.splitext( path ) + return import_re.match(path) and py3compat.isidentifier(name) + # Now find actual path matches for packages or modules folder_list = [p for p in folder_list if isfile(pjoin(path, p,'__init__.py')) - or import_re.match(p) ] + or is_importable_file(p) ] return [basename(p).split('.')[0] for p in folder_list] diff --git a/IPython/core/tests/test_completerlib.py b/IPython/core/tests/test_completerlib.py index d50c122..745f838 100644 --- a/IPython/core/tests/test_completerlib.py +++ b/IPython/core/tests/test_completerlib.py @@ -18,8 +18,9 @@ from os.path import join import nose.tools as nt from nose import SkipTest -from IPython.core.completerlib import magic_run_completer +from IPython.core.completerlib import magic_run_completer, module_completion from IPython.utils import py3compat +from IPython.utils.tempdir import TemporaryDirectory class MockEvent(object): @@ -65,3 +66,15 @@ class Test_magic_run_completer(unittest.TestCase): match = set(magic_run_completer(mockself, event)) self.assertEqual(match, set([u"a.py", u"aaø.py"])) + def test_import_invalid_module(self): + """Testing of issue https://github.com/ipython/ipython/issues/1107""" + invalid_module_names = set(['foo-bar', 'foo:bar', '10foo']) + with TemporaryDirectory() as tmpdir: + sys.path.insert( 0, tmpdir ) + for name in invalid_module_names: + filename = os.path.join(tmpdir, name + '.py') + open(filename, 'w').close() + + s = set( module_completion('import foo') ) + intersection = s.intersection(invalid_module_names) + self.assertFalse(intersection, intersection)