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