1 # -*- coding: utf-8 -*- 2 """Tests for completerlib. 3 4 """ 5 6 #----------------------------------------------------------------------------- 7 # Imports 8 #----------------------------------------------------------------------------- 9 10 import os 11 import shutil 12 import sys 13 import tempfile 14 import unittest 15 from os.path import join 16 17 from tempfile import TemporaryDirectory 18 19 from IPython.core.completerlib import magic_run_completer, module_completion, try_import 20 from IPython.testing.decorators import onlyif_unicode_paths 21 22 23 class MockEvent(object): 24 def __init__(self, line): 25 self.line = line 26 27 #----------------------------------------------------------------------------- 28 # Test functions begin 29 #----------------------------------------------------------------------------- 30 class Test_magic_run_completer(unittest.TestCase): 31 files = [u"aao.py", u"a.py", u"b.py", u"aao.txt"] 32 dirs = [u"adir/", "bdir/"] 33 34 def setUp(self): 35 self.BASETESTDIR = tempfile.mkdtemp() 36 for fil in self.files: 37 with open(join(self.BASETESTDIR, fil), "w", encoding="utf-8") as sfile: 38 sfile.write("pass\n") 39 for d in self.dirs: 40 os.mkdir(join(self.BASETESTDIR, d)) 41 42 self.oldpath = os.getcwd() 43 os.chdir(self.BASETESTDIR) 44 45 def tearDown(self): 46 os.chdir(self.oldpath) 47 shutil.rmtree(self.BASETESTDIR) 48 49 def test_1(self): 50 """Test magic_run_completer, should match two alternatives 51 """ 52 event = MockEvent(u"%run a") 53 mockself = None 54 match = set(magic_run_completer(mockself, event)) 55 self.assertEqual(match, {u"a.py", u"aao.py", u"adir/"}) 56 57 def test_2(self): 58 """Test magic_run_completer, should match one alternative 59 """ 60 event = MockEvent(u"%run aa") 61 mockself = None 62 match = set(magic_run_completer(mockself, event)) 63 self.assertEqual(match, {u"aao.py"}) 64 65 def test_3(self): 66 """Test magic_run_completer with unterminated " """ 67 event = MockEvent(u'%run "a') 68 mockself = None 69 match = set(magic_run_completer(mockself, event)) 70 self.assertEqual(match, {u"a.py", u"aao.py", u"adir/"}) 71 72 def test_completion_more_args(self): 73 event = MockEvent(u'%run a.py ') 74 match = set(magic_run_completer(None, event)) 75 self.assertEqual(match, set(self.files + self.dirs)) 76 77 def test_completion_in_dir(self): 78 # Github issue #3459 79 event = MockEvent(u'%run a.py {}'.format(join(self.BASETESTDIR, 'a'))) 80 print(repr(event.line)) 81 match = set(magic_run_completer(None, event)) 82 # We specifically use replace here rather than normpath, because 83 # at one point there were duplicates 'adir' and 'adir/', and normpath 84 # would hide the failure for that. 85 self.assertEqual(match, {join(self.BASETESTDIR, f).replace('\\','/') 86 for f in (u'a.py', u'aao.py', u'aao.txt', u'adir/')}) 87 88 class Test_magic_run_completer_nonascii(unittest.TestCase): 89 @onlyif_unicode_paths 90 def setUp(self): 91 self.BASETESTDIR = tempfile.mkdtemp() 92 for fil in [u"aaø.py", u"a.py", u"b.py"]: 93 with open(join(self.BASETESTDIR, fil), "w", encoding="utf-8") as sfile: 94 sfile.write("pass\n") 95 self.oldpath = os.getcwd() 96 os.chdir(self.BASETESTDIR) 97 98 def tearDown(self): 99 os.chdir(self.oldpath) 100 shutil.rmtree(self.BASETESTDIR) 101 102 @onlyif_unicode_paths 103 def test_1(self): 104 """Test magic_run_completer, should match two alternatives 105 """ 106 event = MockEvent(u"%run a") 107 mockself = None 108 match = set(magic_run_completer(mockself, event)) 109 self.assertEqual(match, {u"a.py", u"aaø.py"}) 110 111 @onlyif_unicode_paths 112 def test_2(self): 113 """Test magic_run_completer, should match one alternative 114 """ 115 event = MockEvent(u"%run aa") 116 mockself = None 117 match = set(magic_run_completer(mockself, event)) 118 self.assertEqual(match, {u"aaø.py"}) 119 120 @onlyif_unicode_paths 121 def test_3(self): 122 """Test magic_run_completer with unterminated " """ 123 event = MockEvent(u'%run "a') 124 mockself = None 125 match = set(magic_run_completer(mockself, event)) 126 self.assertEqual(match, {u"a.py", u"aaø.py"}) 127 128 # module_completer: 129 130 def test_import_invalid_module(): 131 """Testing of issue https://github.com/ipython/ipython/issues/1107""" 132 invalid_module_names = {'foo-bar', 'foo:bar', '10foo'} 133 valid_module_names = {'foobar'} 134 with TemporaryDirectory() as tmpdir: 135 sys.path.insert( 0, tmpdir ) 136 for name in invalid_module_names | valid_module_names: 137 filename = os.path.join(tmpdir, name + ".py") 138 open(filename, "w", encoding="utf-8").close() 139 140 s = set( module_completion('import foo') ) 141 intersection = s.intersection(invalid_module_names) 142 assert intersection == set() 143 144 assert valid_module_names.issubset(s), valid_module_names.intersection(s) 145 146 147 def test_bad_module_all(): 148 """Test module with invalid __all__ 149 150 https://github.com/ipython/ipython/issues/9678 151 """ 152 testsdir = os.path.dirname(__file__) 153 sys.path.insert(0, testsdir) 154 try: 155 results = module_completion("from bad_all import ") 156 assert "puppies" in results 157 for r in results: 158 assert isinstance(r, str) 159 160 # bad_all doesn't contain submodules, but this completion 161 # should finish without raising an exception: 162 results = module_completion("import bad_all.") 163 assert results == [] 164 finally: 165 sys.path.remove(testsdir) 166 167 168 def test_module_without_init(): 169 """ 170 Test module without __init__.py. 171 172 https://github.com/ipython/ipython/issues/11226 173 """ 174 fake_module_name = "foo" 175 with TemporaryDirectory() as tmpdir: 176 sys.path.insert(0, tmpdir) 177 try: 178 os.makedirs(os.path.join(tmpdir, fake_module_name)) 179 s = try_import(mod=fake_module_name) 180 assert s == [], f"for module {fake_module_name}" 181 finally: 182 sys.path.remove(tmpdir) 183 184 185 def test_valid_exported_submodules(): 186 """ 187 Test checking exported (__all__) objects are submodules 188 """ 189 results = module_completion("import os.pa") 190 # ensure we get a valid submodule: 191 assert "os.path" in results 192 # ensure we don't get objects that aren't submodules: 193 assert "os.pathconf" not in results 194