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