##// END OF EJS Templates
Merge remote-tracking branch 'rossjones/defect-1107' into rossjones-fix-import-completion
Thomas Kluyver -
r6180:17d34f56 merge
parent child Browse files
Show More
@@ -1,324 +1,329 b''
1 """Implementations for various useful completers.
1 """Implementations for various useful completers.
2
2
3 These are all loaded by default by IPython.
3 These are all loaded by default by IPython.
4 """
4 """
5 #-----------------------------------------------------------------------------
5 #-----------------------------------------------------------------------------
6 # Copyright (C) 2010-2011 The IPython Development Team.
6 # Copyright (C) 2010-2011 The IPython Development Team.
7 #
7 #
8 # Distributed under the terms of the BSD License.
8 # Distributed under the terms of the BSD License.
9 #
9 #
10 # The full license is in the file COPYING.txt, distributed with this software.
10 # The full license is in the file COPYING.txt, distributed with this software.
11 #-----------------------------------------------------------------------------
11 #-----------------------------------------------------------------------------
12
12
13 #-----------------------------------------------------------------------------
13 #-----------------------------------------------------------------------------
14 # Imports
14 # Imports
15 #-----------------------------------------------------------------------------
15 #-----------------------------------------------------------------------------
16 from __future__ import print_function
16 from __future__ import print_function
17
17
18 # Stdlib imports
18 # Stdlib imports
19 import glob
19 import glob
20 import inspect
20 import inspect
21 import os
21 import os
22 import re
22 import re
23 import sys
23 import sys
24
24
25 # Third-party imports
25 # Third-party imports
26 from time import time
26 from time import time
27 from zipimport import zipimporter
27 from zipimport import zipimporter
28
28
29 # Our own imports
29 # Our own imports
30 from IPython.core.completer import expand_user, compress_user
30 from IPython.core.completer import expand_user, compress_user
31 from IPython.core.error import TryNext
31 from IPython.core.error import TryNext
32 from IPython.utils import py3compat
32 from IPython.utils import py3compat
33 from IPython.utils._process_common import arg_split
33 from IPython.utils._process_common import arg_split
34
34
35 # FIXME: this should be pulled in with the right call via the component system
35 # FIXME: this should be pulled in with the right call via the component system
36 from IPython.core.ipapi import get as get_ipython
36 from IPython.core.ipapi import get as get_ipython
37
37
38 #-----------------------------------------------------------------------------
38 #-----------------------------------------------------------------------------
39 # Globals and constants
39 # Globals and constants
40 #-----------------------------------------------------------------------------
40 #-----------------------------------------------------------------------------
41
41
42 # Time in seconds after which the rootmodules will be stored permanently in the
42 # Time in seconds after which the rootmodules will be stored permanently in the
43 # ipython ip.db database (kept in the user's .ipython dir).
43 # ipython ip.db database (kept in the user's .ipython dir).
44 TIMEOUT_STORAGE = 2
44 TIMEOUT_STORAGE = 2
45
45
46 # Time in seconds after which we give up
46 # Time in seconds after which we give up
47 TIMEOUT_GIVEUP = 20
47 TIMEOUT_GIVEUP = 20
48
48
49 # Regular expression for the python import statement
49 # Regular expression for the python import statement
50 import_re = re.compile(r'.*(\.so|\.py[cod]?)$')
50 import_re = re.compile(r'.*(\.so|\.py[cod]?)$')
51
51
52 # RE for the ipython %run command (python + ipython scripts)
52 # RE for the ipython %run command (python + ipython scripts)
53 magic_run_re = re.compile(r'.*(\.ipy|\.py[w]?)$')
53 magic_run_re = re.compile(r'.*(\.ipy|\.py[w]?)$')
54
54
55 #-----------------------------------------------------------------------------
55 #-----------------------------------------------------------------------------
56 # Local utilities
56 # Local utilities
57 #-----------------------------------------------------------------------------
57 #-----------------------------------------------------------------------------
58
58
59 def module_list(path):
59 def module_list(path):
60 """
60 """
61 Return the list containing the names of the modules available in the given
61 Return the list containing the names of the modules available in the given
62 folder.
62 folder.
63 """
63 """
64 # sys.path has the cwd as an empty string, but isdir/listdir need it as '.'
64 # sys.path has the cwd as an empty string, but isdir/listdir need it as '.'
65 if path == '':
65 if path == '':
66 path = '.'
66 path = '.'
67
67
68 if os.path.isdir(path):
68 if os.path.isdir(path):
69 folder_list = os.listdir(path)
69 folder_list = os.listdir(path)
70 elif path.endswith('.egg'):
70 elif path.endswith('.egg'):
71 try:
71 try:
72 folder_list = [f for f in zipimporter(path)._files]
72 folder_list = [f for f in zipimporter(path)._files]
73 except:
73 except:
74 folder_list = []
74 folder_list = []
75 else:
75 else:
76 folder_list = []
76 folder_list = []
77
77
78 if not folder_list:
78 if not folder_list:
79 return []
79 return []
80
80
81 # A few local constants to be used in loops below
81 # A few local constants to be used in loops below
82 isfile = os.path.isfile
82 isfile = os.path.isfile
83 pjoin = os.path.join
83 pjoin = os.path.join
84 basename = os.path.basename
84 basename = os.path.basename
85
85
86 def is_importable_file(path):
87 """Returns True if the provided path is a valid importable module"""
88 name, extension = os.path.splitext( path )
89 return import_re.match(path) and py3compat.isidentifier(name)
90
86 # Now find actual path matches for packages or modules
91 # Now find actual path matches for packages or modules
87 folder_list = [p for p in folder_list
92 folder_list = [p for p in folder_list
88 if isfile(pjoin(path, p,'__init__.py'))
93 if isfile(pjoin(path, p,'__init__.py'))
89 or import_re.match(p) ]
94 or is_importable_file(p) ]
90
95
91 return [basename(p).split('.')[0] for p in folder_list]
96 return [basename(p).split('.')[0] for p in folder_list]
92
97
93 def get_root_modules():
98 def get_root_modules():
94 """
99 """
95 Returns a list containing the names of all the modules available in the
100 Returns a list containing the names of all the modules available in the
96 folders of the pythonpath.
101 folders of the pythonpath.
97 """
102 """
98 ip = get_ipython()
103 ip = get_ipython()
99
104
100 if 'rootmodules' in ip.db:
105 if 'rootmodules' in ip.db:
101 return ip.db['rootmodules']
106 return ip.db['rootmodules']
102
107
103 t = time()
108 t = time()
104 store = False
109 store = False
105 modules = list(sys.builtin_module_names)
110 modules = list(sys.builtin_module_names)
106 for path in sys.path:
111 for path in sys.path:
107 modules += module_list(path)
112 modules += module_list(path)
108 if time() - t >= TIMEOUT_STORAGE and not store:
113 if time() - t >= TIMEOUT_STORAGE and not store:
109 store = True
114 store = True
110 print("\nCaching the list of root modules, please wait!")
115 print("\nCaching the list of root modules, please wait!")
111 print("(This will only be done once - type '%rehashx' to "
116 print("(This will only be done once - type '%rehashx' to "
112 "reset cache!)\n")
117 "reset cache!)\n")
113 sys.stdout.flush()
118 sys.stdout.flush()
114 if time() - t > TIMEOUT_GIVEUP:
119 if time() - t > TIMEOUT_GIVEUP:
115 print("This is taking too long, we give up.\n")
120 print("This is taking too long, we give up.\n")
116 ip.db['rootmodules'] = []
121 ip.db['rootmodules'] = []
117 return []
122 return []
118
123
119 modules = set(modules)
124 modules = set(modules)
120 if '__init__' in modules:
125 if '__init__' in modules:
121 modules.remove('__init__')
126 modules.remove('__init__')
122 modules = list(modules)
127 modules = list(modules)
123 if store:
128 if store:
124 ip.db['rootmodules'] = modules
129 ip.db['rootmodules'] = modules
125 return modules
130 return modules
126
131
127
132
128 def is_importable(module, attr, only_modules):
133 def is_importable(module, attr, only_modules):
129 if only_modules:
134 if only_modules:
130 return inspect.ismodule(getattr(module, attr))
135 return inspect.ismodule(getattr(module, attr))
131 else:
136 else:
132 return not(attr[:2] == '__' and attr[-2:] == '__')
137 return not(attr[:2] == '__' and attr[-2:] == '__')
133
138
134
139
135 def try_import(mod, only_modules=False):
140 def try_import(mod, only_modules=False):
136 try:
141 try:
137 m = __import__(mod)
142 m = __import__(mod)
138 except:
143 except:
139 return []
144 return []
140 mods = mod.split('.')
145 mods = mod.split('.')
141 for module in mods[1:]:
146 for module in mods[1:]:
142 m = getattr(m, module)
147 m = getattr(m, module)
143
148
144 m_is_init = hasattr(m, '__file__') and '__init__' in m.__file__
149 m_is_init = hasattr(m, '__file__') and '__init__' in m.__file__
145
150
146 completions = []
151 completions = []
147 if (not hasattr(m, '__file__')) or (not only_modules) or m_is_init:
152 if (not hasattr(m, '__file__')) or (not only_modules) or m_is_init:
148 completions.extend( [attr for attr in dir(m) if
153 completions.extend( [attr for attr in dir(m) if
149 is_importable(m, attr, only_modules)])
154 is_importable(m, attr, only_modules)])
150
155
151 completions.extend(getattr(m, '__all__', []))
156 completions.extend(getattr(m, '__all__', []))
152 if m_is_init:
157 if m_is_init:
153 completions.extend(module_list(os.path.dirname(m.__file__)))
158 completions.extend(module_list(os.path.dirname(m.__file__)))
154 completions = set(completions)
159 completions = set(completions)
155 if '__init__' in completions:
160 if '__init__' in completions:
156 completions.remove('__init__')
161 completions.remove('__init__')
157 return list(completions)
162 return list(completions)
158
163
159
164
160 #-----------------------------------------------------------------------------
165 #-----------------------------------------------------------------------------
161 # Completion-related functions.
166 # Completion-related functions.
162 #-----------------------------------------------------------------------------
167 #-----------------------------------------------------------------------------
163
168
164 def quick_completer(cmd, completions):
169 def quick_completer(cmd, completions):
165 """ Easily create a trivial completer for a command.
170 """ Easily create a trivial completer for a command.
166
171
167 Takes either a list of completions, or all completions in string (that will
172 Takes either a list of completions, or all completions in string (that will
168 be split on whitespace).
173 be split on whitespace).
169
174
170 Example::
175 Example::
171
176
172 [d:\ipython]|1> import ipy_completers
177 [d:\ipython]|1> import ipy_completers
173 [d:\ipython]|2> ipy_completers.quick_completer('foo', ['bar','baz'])
178 [d:\ipython]|2> ipy_completers.quick_completer('foo', ['bar','baz'])
174 [d:\ipython]|3> foo b<TAB>
179 [d:\ipython]|3> foo b<TAB>
175 bar baz
180 bar baz
176 [d:\ipython]|3> foo ba
181 [d:\ipython]|3> foo ba
177 """
182 """
178
183
179 if isinstance(completions, basestring):
184 if isinstance(completions, basestring):
180 completions = completions.split()
185 completions = completions.split()
181
186
182 def do_complete(self, event):
187 def do_complete(self, event):
183 return completions
188 return completions
184
189
185 get_ipython().set_hook('complete_command',do_complete, str_key = cmd)
190 get_ipython().set_hook('complete_command',do_complete, str_key = cmd)
186
191
187 def module_completion(line):
192 def module_completion(line):
188 """
193 """
189 Returns a list containing the completion possibilities for an import line.
194 Returns a list containing the completion possibilities for an import line.
190
195
191 The line looks like this :
196 The line looks like this :
192 'import xml.d'
197 'import xml.d'
193 'from xml.dom import'
198 'from xml.dom import'
194 """
199 """
195
200
196 words = line.split(' ')
201 words = line.split(' ')
197 nwords = len(words)
202 nwords = len(words)
198
203
199 # from whatever <tab> -> 'import '
204 # from whatever <tab> -> 'import '
200 if nwords == 3 and words[0] == 'from':
205 if nwords == 3 and words[0] == 'from':
201 return ['import ']
206 return ['import ']
202
207
203 # 'from xy<tab>' or 'import xy<tab>'
208 # 'from xy<tab>' or 'import xy<tab>'
204 if nwords < 3 and (words[0] in ['import','from']) :
209 if nwords < 3 and (words[0] in ['import','from']) :
205 if nwords == 1:
210 if nwords == 1:
206 return get_root_modules()
211 return get_root_modules()
207 mod = words[1].split('.')
212 mod = words[1].split('.')
208 if len(mod) < 2:
213 if len(mod) < 2:
209 return get_root_modules()
214 return get_root_modules()
210 completion_list = try_import('.'.join(mod[:-1]), True)
215 completion_list = try_import('.'.join(mod[:-1]), True)
211 return ['.'.join(mod[:-1] + [el]) for el in completion_list]
216 return ['.'.join(mod[:-1] + [el]) for el in completion_list]
212
217
213 # 'from xyz import abc<tab>'
218 # 'from xyz import abc<tab>'
214 if nwords >= 3 and words[0] == 'from':
219 if nwords >= 3 and words[0] == 'from':
215 mod = words[1]
220 mod = words[1]
216 return try_import(mod)
221 return try_import(mod)
217
222
218 #-----------------------------------------------------------------------------
223 #-----------------------------------------------------------------------------
219 # Completers
224 # Completers
220 #-----------------------------------------------------------------------------
225 #-----------------------------------------------------------------------------
221 # These all have the func(self, event) signature to be used as custom
226 # These all have the func(self, event) signature to be used as custom
222 # completers
227 # completers
223
228
224 def module_completer(self,event):
229 def module_completer(self,event):
225 """Give completions after user has typed 'import ...' or 'from ...'"""
230 """Give completions after user has typed 'import ...' or 'from ...'"""
226
231
227 # This works in all versions of python. While 2.5 has
232 # This works in all versions of python. While 2.5 has
228 # pkgutil.walk_packages(), that particular routine is fairly dangerous,
233 # pkgutil.walk_packages(), that particular routine is fairly dangerous,
229 # since it imports *EVERYTHING* on sys.path. That is: a) very slow b) full
234 # since it imports *EVERYTHING* on sys.path. That is: a) very slow b) full
230 # of possibly problematic side effects.
235 # of possibly problematic side effects.
231 # This search the folders in the sys.path for available modules.
236 # This search the folders in the sys.path for available modules.
232
237
233 return module_completion(event.line)
238 return module_completion(event.line)
234
239
235 # FIXME: there's a lot of logic common to the run, cd and builtin file
240 # FIXME: there's a lot of logic common to the run, cd and builtin file
236 # completers, that is currently reimplemented in each.
241 # completers, that is currently reimplemented in each.
237
242
238 def magic_run_completer(self, event):
243 def magic_run_completer(self, event):
239 """Complete files that end in .py or .ipy for the %run command.
244 """Complete files that end in .py or .ipy for the %run command.
240 """
245 """
241 comps = arg_split(event.line, strict=False)
246 comps = arg_split(event.line, strict=False)
242 relpath = (len(comps) > 1 and comps[-1] or '').strip("'\"")
247 relpath = (len(comps) > 1 and comps[-1] or '').strip("'\"")
243
248
244 #print("\nev=", event) # dbg
249 #print("\nev=", event) # dbg
245 #print("rp=", relpath) # dbg
250 #print("rp=", relpath) # dbg
246 #print('comps=', comps) # dbg
251 #print('comps=', comps) # dbg
247
252
248 lglob = glob.glob
253 lglob = glob.glob
249 isdir = os.path.isdir
254 isdir = os.path.isdir
250 relpath, tilde_expand, tilde_val = expand_user(relpath)
255 relpath, tilde_expand, tilde_val = expand_user(relpath)
251
256
252 dirs = [f.replace('\\','/') + "/" for f in lglob(relpath+'*') if isdir(f)]
257 dirs = [f.replace('\\','/') + "/" for f in lglob(relpath+'*') if isdir(f)]
253
258
254 # Find if the user has already typed the first filename, after which we
259 # Find if the user has already typed the first filename, after which we
255 # should complete on all files, since after the first one other files may
260 # should complete on all files, since after the first one other files may
256 # be arguments to the input script.
261 # be arguments to the input script.
257
262
258 if filter(magic_run_re.match, comps):
263 if filter(magic_run_re.match, comps):
259 pys = [f.replace('\\','/') for f in lglob('*')]
264 pys = [f.replace('\\','/') for f in lglob('*')]
260 else:
265 else:
261 pys = [f.replace('\\','/')
266 pys = [f.replace('\\','/')
262 for f in lglob(relpath+'*.py') + lglob(relpath+'*.ipy') +
267 for f in lglob(relpath+'*.py') + lglob(relpath+'*.ipy') +
263 lglob(relpath + '*.pyw')]
268 lglob(relpath + '*.pyw')]
264 #print('run comp:', dirs+pys) # dbg
269 #print('run comp:', dirs+pys) # dbg
265 return [compress_user(p, tilde_expand, tilde_val) for p in dirs+pys]
270 return [compress_user(p, tilde_expand, tilde_val) for p in dirs+pys]
266
271
267
272
268 def cd_completer(self, event):
273 def cd_completer(self, event):
269 """Completer function for cd, which only returns directories."""
274 """Completer function for cd, which only returns directories."""
270 ip = get_ipython()
275 ip = get_ipython()
271 relpath = event.symbol
276 relpath = event.symbol
272
277
273 #print(event) # dbg
278 #print(event) # dbg
274 if event.line.endswith('-b') or ' -b ' in event.line:
279 if event.line.endswith('-b') or ' -b ' in event.line:
275 # return only bookmark completions
280 # return only bookmark completions
276 bkms = self.db.get('bookmarks', None)
281 bkms = self.db.get('bookmarks', None)
277 if bkms:
282 if bkms:
278 return bkms.keys()
283 return bkms.keys()
279 else:
284 else:
280 return []
285 return []
281
286
282 if event.symbol == '-':
287 if event.symbol == '-':
283 width_dh = str(len(str(len(ip.user_ns['_dh']) + 1)))
288 width_dh = str(len(str(len(ip.user_ns['_dh']) + 1)))
284 # jump in directory history by number
289 # jump in directory history by number
285 fmt = '-%0' + width_dh +'d [%s]'
290 fmt = '-%0' + width_dh +'d [%s]'
286 ents = [ fmt % (i,s) for i,s in enumerate(ip.user_ns['_dh'])]
291 ents = [ fmt % (i,s) for i,s in enumerate(ip.user_ns['_dh'])]
287 if len(ents) > 1:
292 if len(ents) > 1:
288 return ents
293 return ents
289 return []
294 return []
290
295
291 if event.symbol.startswith('--'):
296 if event.symbol.startswith('--'):
292 return ["--" + os.path.basename(d) for d in ip.user_ns['_dh']]
297 return ["--" + os.path.basename(d) for d in ip.user_ns['_dh']]
293
298
294 # Expand ~ in path and normalize directory separators.
299 # Expand ~ in path and normalize directory separators.
295 relpath, tilde_expand, tilde_val = expand_user(relpath)
300 relpath, tilde_expand, tilde_val = expand_user(relpath)
296 relpath = relpath.replace('\\','/')
301 relpath = relpath.replace('\\','/')
297
302
298 found = []
303 found = []
299 for d in [f.replace('\\','/') + '/' for f in glob.glob(relpath+'*')
304 for d in [f.replace('\\','/') + '/' for f in glob.glob(relpath+'*')
300 if os.path.isdir(f)]:
305 if os.path.isdir(f)]:
301 if ' ' in d:
306 if ' ' in d:
302 # we don't want to deal with any of that, complex code
307 # we don't want to deal with any of that, complex code
303 # for this is elsewhere
308 # for this is elsewhere
304 raise TryNext
309 raise TryNext
305
310
306 found.append(d)
311 found.append(d)
307
312
308 if not found:
313 if not found:
309 if os.path.isdir(relpath):
314 if os.path.isdir(relpath):
310 return [compress_user(relpath, tilde_expand, tilde_val)]
315 return [compress_user(relpath, tilde_expand, tilde_val)]
311
316
312 # if no completions so far, try bookmarks
317 # if no completions so far, try bookmarks
313 bks = self.db.get('bookmarks',{}).iterkeys()
318 bks = self.db.get('bookmarks',{}).iterkeys()
314 bkmatches = [s for s in bks if s.startswith(event.symbol)]
319 bkmatches = [s for s in bks if s.startswith(event.symbol)]
315 if bkmatches:
320 if bkmatches:
316 return bkmatches
321 return bkmatches
317
322
318 raise TryNext
323 raise TryNext
319
324
320 return [compress_user(p, tilde_expand, tilde_val) for p in found]
325 return [compress_user(p, tilde_expand, tilde_val) for p in found]
321
326
322 def reset_completer(self, event):
327 def reset_completer(self, event):
323 "A completer for %reset magic"
328 "A completer for %reset magic"
324 return '-f -s in out array dhist'.split()
329 return '-f -s in out array dhist'.split()
@@ -1,67 +1,80 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """Tests for completerlib.
2 """Tests for completerlib.
3
3
4 """
4 """
5 from __future__ import absolute_import
5 from __future__ import absolute_import
6
6
7 #-----------------------------------------------------------------------------
7 #-----------------------------------------------------------------------------
8 # Imports
8 # Imports
9 #-----------------------------------------------------------------------------
9 #-----------------------------------------------------------------------------
10
10
11 import os
11 import os
12 import shutil
12 import shutil
13 import sys
13 import sys
14 import tempfile
14 import tempfile
15 import unittest
15 import unittest
16 from os.path import join
16 from os.path import join
17
17
18 import nose.tools as nt
18 import nose.tools as nt
19 from nose import SkipTest
19 from nose import SkipTest
20
20
21 from IPython.core.completerlib import magic_run_completer
21 from IPython.core.completerlib import magic_run_completer, module_completion
22 from IPython.utils import py3compat
22 from IPython.utils import py3compat
23 from IPython.utils.tempdir import TemporaryDirectory
23
24
24
25
25 class MockEvent(object):
26 class MockEvent(object):
26 def __init__(self, line):
27 def __init__(self, line):
27 self.line = line
28 self.line = line
28
29
29 #-----------------------------------------------------------------------------
30 #-----------------------------------------------------------------------------
30 # Test functions begin
31 # Test functions begin
31 #-----------------------------------------------------------------------------
32 #-----------------------------------------------------------------------------
32 class Test_magic_run_completer(unittest.TestCase):
33 class Test_magic_run_completer(unittest.TestCase):
33 def setUp(self):
34 def setUp(self):
34 self.BASETESTDIR = tempfile.mkdtemp()
35 self.BASETESTDIR = tempfile.mkdtemp()
35 for fil in [u"aaø.py", u"a.py", u"b.py"]:
36 for fil in [u"aaø.py", u"a.py", u"b.py"]:
36 with open(join(self.BASETESTDIR, fil), "w") as sfile:
37 with open(join(self.BASETESTDIR, fil), "w") as sfile:
37 sfile.write("pass\n")
38 sfile.write("pass\n")
38 self.oldpath = os.getcwdu()
39 self.oldpath = os.getcwdu()
39 os.chdir(self.BASETESTDIR)
40 os.chdir(self.BASETESTDIR)
40
41
41 def tearDown(self):
42 def tearDown(self):
42 os.chdir(self.oldpath)
43 os.chdir(self.oldpath)
43 shutil.rmtree(self.BASETESTDIR)
44 shutil.rmtree(self.BASETESTDIR)
44
45
45 def test_1(self):
46 def test_1(self):
46 """Test magic_run_completer, should match two alterntives
47 """Test magic_run_completer, should match two alterntives
47 """
48 """
48 event = MockEvent(u"%run a")
49 event = MockEvent(u"%run a")
49 mockself = None
50 mockself = None
50 match = set(magic_run_completer(mockself, event))
51 match = set(magic_run_completer(mockself, event))
51 self.assertEqual(match, set([u"a.py", u"aaø.py"]))
52 self.assertEqual(match, set([u"a.py", u"aaø.py"]))
52
53
53 def test_2(self):
54 def test_2(self):
54 """Test magic_run_completer, should match one alterntive
55 """Test magic_run_completer, should match one alterntive
55 """
56 """
56 event = MockEvent(u"%run aa")
57 event = MockEvent(u"%run aa")
57 mockself = None
58 mockself = None
58 match = set(magic_run_completer(mockself, event))
59 match = set(magic_run_completer(mockself, event))
59 self.assertEqual(match, set([u"aaø.py"]))
60 self.assertEqual(match, set([u"aaø.py"]))
60
61
61 def test_3(self):
62 def test_3(self):
62 """Test magic_run_completer with unterminated " """
63 """Test magic_run_completer with unterminated " """
63 event = MockEvent(u'%run "a')
64 event = MockEvent(u'%run "a')
64 mockself = None
65 mockself = None
65 match = set(magic_run_completer(mockself, event))
66 match = set(magic_run_completer(mockself, event))
66 self.assertEqual(match, set([u"a.py", u"aaø.py"]))
67 self.assertEqual(match, set([u"a.py", u"aaø.py"]))
67
68
69 def test_import_invalid_module(self):
70 """Testing of issue https://github.com/ipython/ipython/issues/1107"""
71 invalid_module_names = set(['foo-bar', 'foo:bar', '10foo'])
72 with TemporaryDirectory() as tmpdir:
73 sys.path.insert( 0, tmpdir )
74 for name in invalid_module_names:
75 filename = os.path.join(tmpdir, name + '.py')
76 open(filename, 'w').close()
77
78 s = set( module_completion('import foo') )
79 intersection = s.intersection(invalid_module_names)
80 self.assertFalse(intersection, intersection)
General Comments 0
You need to be logged in to leave comments. Login now