##// END OF EJS Templates
Merge pull request #11393 from hugovk/replace-simplegeneric...
Matthias Bussonnier -
r24716:6d9a28a2 merge
parent child Browse files
Show More
@@ -1,1029 +1,1029 b''
1 # encoding: utf-8
1 # encoding: utf-8
2 """Tests for the IPython tab-completion machinery."""
2 """Tests for the IPython tab-completion machinery."""
3
3
4 # Copyright (c) IPython Development Team.
4 # Copyright (c) IPython Development Team.
5 # Distributed under the terms of the Modified BSD License.
5 # Distributed under the terms of the Modified BSD License.
6
6
7 import os
7 import os
8 import sys
8 import sys
9 import textwrap
9 import textwrap
10 import unittest
10 import unittest
11
11
12 from contextlib import contextmanager
12 from contextlib import contextmanager
13
13
14 import nose.tools as nt
14 import nose.tools as nt
15
15
16 from traitlets.config.loader import Config
16 from traitlets.config.loader import Config
17 from IPython import get_ipython
17 from IPython import get_ipython
18 from IPython.core import completer
18 from IPython.core import completer
19 from IPython.external.decorators import knownfailureif
19 from IPython.external.decorators import knownfailureif
20 from IPython.utils.tempdir import TemporaryDirectory, TemporaryWorkingDirectory
20 from IPython.utils.tempdir import TemporaryDirectory, TemporaryWorkingDirectory
21 from IPython.utils.generics import complete_object
21 from IPython.utils.generics import complete_object
22 from IPython.testing import decorators as dec
22 from IPython.testing import decorators as dec
23
23
24 from IPython.core.completer import (
24 from IPython.core.completer import (
25 Completion, provisionalcompleter, match_dict_keys, _deduplicate_completions)
25 Completion, provisionalcompleter, match_dict_keys, _deduplicate_completions)
26 from nose.tools import assert_in, assert_not_in
26 from nose.tools import assert_in, assert_not_in
27
27
28 #-----------------------------------------------------------------------------
28 #-----------------------------------------------------------------------------
29 # Test functions
29 # Test functions
30 #-----------------------------------------------------------------------------
30 #-----------------------------------------------------------------------------
31
31
32 @contextmanager
32 @contextmanager
33 def greedy_completion():
33 def greedy_completion():
34 ip = get_ipython()
34 ip = get_ipython()
35 greedy_original = ip.Completer.greedy
35 greedy_original = ip.Completer.greedy
36 try:
36 try:
37 ip.Completer.greedy = True
37 ip.Completer.greedy = True
38 yield
38 yield
39 finally:
39 finally:
40 ip.Completer.greedy = greedy_original
40 ip.Completer.greedy = greedy_original
41
41
42 def test_protect_filename():
42 def test_protect_filename():
43 if sys.platform == 'win32':
43 if sys.platform == 'win32':
44 pairs = [('abc','abc'),
44 pairs = [('abc','abc'),
45 (' abc','" abc"'),
45 (' abc','" abc"'),
46 ('a bc','"a bc"'),
46 ('a bc','"a bc"'),
47 ('a bc','"a bc"'),
47 ('a bc','"a bc"'),
48 (' bc','" bc"'),
48 (' bc','" bc"'),
49 ]
49 ]
50 else:
50 else:
51 pairs = [('abc','abc'),
51 pairs = [('abc','abc'),
52 (' abc',r'\ abc'),
52 (' abc',r'\ abc'),
53 ('a bc',r'a\ bc'),
53 ('a bc',r'a\ bc'),
54 ('a bc',r'a\ \ bc'),
54 ('a bc',r'a\ \ bc'),
55 (' bc',r'\ \ bc'),
55 (' bc',r'\ \ bc'),
56 # On posix, we also protect parens and other special characters.
56 # On posix, we also protect parens and other special characters.
57 ('a(bc',r'a\(bc'),
57 ('a(bc',r'a\(bc'),
58 ('a)bc',r'a\)bc'),
58 ('a)bc',r'a\)bc'),
59 ('a( )bc',r'a\(\ \)bc'),
59 ('a( )bc',r'a\(\ \)bc'),
60 ('a[1]bc', r'a\[1\]bc'),
60 ('a[1]bc', r'a\[1\]bc'),
61 ('a{1}bc', r'a\{1\}bc'),
61 ('a{1}bc', r'a\{1\}bc'),
62 ('a#bc', r'a\#bc'),
62 ('a#bc', r'a\#bc'),
63 ('a?bc', r'a\?bc'),
63 ('a?bc', r'a\?bc'),
64 ('a=bc', r'a\=bc'),
64 ('a=bc', r'a\=bc'),
65 ('a\\bc', r'a\\bc'),
65 ('a\\bc', r'a\\bc'),
66 ('a|bc', r'a\|bc'),
66 ('a|bc', r'a\|bc'),
67 ('a;bc', r'a\;bc'),
67 ('a;bc', r'a\;bc'),
68 ('a:bc', r'a\:bc'),
68 ('a:bc', r'a\:bc'),
69 ("a'bc", r"a\'bc"),
69 ("a'bc", r"a\'bc"),
70 ('a*bc', r'a\*bc'),
70 ('a*bc', r'a\*bc'),
71 ('a"bc', r'a\"bc'),
71 ('a"bc', r'a\"bc'),
72 ('a^bc', r'a\^bc'),
72 ('a^bc', r'a\^bc'),
73 ('a&bc', r'a\&bc'),
73 ('a&bc', r'a\&bc'),
74 ]
74 ]
75 # run the actual tests
75 # run the actual tests
76 for s1, s2 in pairs:
76 for s1, s2 in pairs:
77 s1p = completer.protect_filename(s1)
77 s1p = completer.protect_filename(s1)
78 nt.assert_equal(s1p, s2)
78 nt.assert_equal(s1p, s2)
79
79
80
80
81 def check_line_split(splitter, test_specs):
81 def check_line_split(splitter, test_specs):
82 for part1, part2, split in test_specs:
82 for part1, part2, split in test_specs:
83 cursor_pos = len(part1)
83 cursor_pos = len(part1)
84 line = part1+part2
84 line = part1+part2
85 out = splitter.split_line(line, cursor_pos)
85 out = splitter.split_line(line, cursor_pos)
86 nt.assert_equal(out, split)
86 nt.assert_equal(out, split)
87
87
88
88
89 def test_line_split():
89 def test_line_split():
90 """Basic line splitter test with default specs."""
90 """Basic line splitter test with default specs."""
91 sp = completer.CompletionSplitter()
91 sp = completer.CompletionSplitter()
92 # The format of the test specs is: part1, part2, expected answer. Parts 1
92 # The format of the test specs is: part1, part2, expected answer. Parts 1
93 # and 2 are joined into the 'line' sent to the splitter, as if the cursor
93 # and 2 are joined into the 'line' sent to the splitter, as if the cursor
94 # was at the end of part1. So an empty part2 represents someone hitting
94 # was at the end of part1. So an empty part2 represents someone hitting
95 # tab at the end of the line, the most common case.
95 # tab at the end of the line, the most common case.
96 t = [('run some/scrip', '', 'some/scrip'),
96 t = [('run some/scrip', '', 'some/scrip'),
97 ('run scripts/er', 'ror.py foo', 'scripts/er'),
97 ('run scripts/er', 'ror.py foo', 'scripts/er'),
98 ('echo $HOM', '', 'HOM'),
98 ('echo $HOM', '', 'HOM'),
99 ('print sys.pa', '', 'sys.pa'),
99 ('print sys.pa', '', 'sys.pa'),
100 ('print(sys.pa', '', 'sys.pa'),
100 ('print(sys.pa', '', 'sys.pa'),
101 ("execfile('scripts/er", '', 'scripts/er'),
101 ("execfile('scripts/er", '', 'scripts/er'),
102 ('a[x.', '', 'x.'),
102 ('a[x.', '', 'x.'),
103 ('a[x.', 'y', 'x.'),
103 ('a[x.', 'y', 'x.'),
104 ('cd "some_file/', '', 'some_file/'),
104 ('cd "some_file/', '', 'some_file/'),
105 ]
105 ]
106 check_line_split(sp, t)
106 check_line_split(sp, t)
107 # Ensure splitting works OK with unicode by re-running the tests with
107 # Ensure splitting works OK with unicode by re-running the tests with
108 # all inputs turned into unicode
108 # all inputs turned into unicode
109 check_line_split(sp, [ map(str, p) for p in t] )
109 check_line_split(sp, [ map(str, p) for p in t] )
110
110
111
111
112 def test_custom_completion_error():
112 def test_custom_completion_error():
113 """Test that errors from custom attribute completers are silenced."""
113 """Test that errors from custom attribute completers are silenced."""
114 ip = get_ipython()
114 ip = get_ipython()
115 class A(object): pass
115 class A(object): pass
116 ip.user_ns['a'] = A()
116 ip.user_ns['a'] = A()
117
117
118 @complete_object.when_type(A)
118 @complete_object.register(A)
119 def complete_A(a, existing_completions):
119 def complete_A(a, existing_completions):
120 raise TypeError("this should be silenced")
120 raise TypeError("this should be silenced")
121
121
122 ip.complete("a.")
122 ip.complete("a.")
123
123
124
124
125 def test_unicode_completions():
125 def test_unicode_completions():
126 ip = get_ipython()
126 ip = get_ipython()
127 # Some strings that trigger different types of completion. Check them both
127 # Some strings that trigger different types of completion. Check them both
128 # in str and unicode forms
128 # in str and unicode forms
129 s = ['ru', '%ru', 'cd /', 'floa', 'float(x)/']
129 s = ['ru', '%ru', 'cd /', 'floa', 'float(x)/']
130 for t in s + list(map(str, s)):
130 for t in s + list(map(str, s)):
131 # We don't need to check exact completion values (they may change
131 # We don't need to check exact completion values (they may change
132 # depending on the state of the namespace, but at least no exceptions
132 # depending on the state of the namespace, but at least no exceptions
133 # should be thrown and the return value should be a pair of text, list
133 # should be thrown and the return value should be a pair of text, list
134 # values.
134 # values.
135 text, matches = ip.complete(t)
135 text, matches = ip.complete(t)
136 nt.assert_true(isinstance(text, str))
136 nt.assert_true(isinstance(text, str))
137 nt.assert_true(isinstance(matches, list))
137 nt.assert_true(isinstance(matches, list))
138
138
139 def test_latex_completions():
139 def test_latex_completions():
140 from IPython.core.latex_symbols import latex_symbols
140 from IPython.core.latex_symbols import latex_symbols
141 import random
141 import random
142 ip = get_ipython()
142 ip = get_ipython()
143 # Test some random unicode symbols
143 # Test some random unicode symbols
144 keys = random.sample(latex_symbols.keys(), 10)
144 keys = random.sample(latex_symbols.keys(), 10)
145 for k in keys:
145 for k in keys:
146 text, matches = ip.complete(k)
146 text, matches = ip.complete(k)
147 nt.assert_equal(len(matches),1)
147 nt.assert_equal(len(matches),1)
148 nt.assert_equal(text, k)
148 nt.assert_equal(text, k)
149 nt.assert_equal(matches[0], latex_symbols[k])
149 nt.assert_equal(matches[0], latex_symbols[k])
150 # Test a more complex line
150 # Test a more complex line
151 text, matches = ip.complete(u'print(\\alpha')
151 text, matches = ip.complete(u'print(\\alpha')
152 nt.assert_equal(text, u'\\alpha')
152 nt.assert_equal(text, u'\\alpha')
153 nt.assert_equal(matches[0], latex_symbols['\\alpha'])
153 nt.assert_equal(matches[0], latex_symbols['\\alpha'])
154 # Test multiple matching latex symbols
154 # Test multiple matching latex symbols
155 text, matches = ip.complete(u'\\al')
155 text, matches = ip.complete(u'\\al')
156 nt.assert_in('\\alpha', matches)
156 nt.assert_in('\\alpha', matches)
157 nt.assert_in('\\aleph', matches)
157 nt.assert_in('\\aleph', matches)
158
158
159
159
160
160
161
161
162 def test_back_latex_completion():
162 def test_back_latex_completion():
163 ip = get_ipython()
163 ip = get_ipython()
164
164
165 # do not return more than 1 matches fro \beta, only the latex one.
165 # do not return more than 1 matches fro \beta, only the latex one.
166 name, matches = ip.complete('\\Ξ²')
166 name, matches = ip.complete('\\Ξ²')
167 nt.assert_equal(len(matches), 1)
167 nt.assert_equal(len(matches), 1)
168 nt.assert_equal(matches[0], '\\beta')
168 nt.assert_equal(matches[0], '\\beta')
169
169
170 def test_back_unicode_completion():
170 def test_back_unicode_completion():
171 ip = get_ipython()
171 ip = get_ipython()
172
172
173 name, matches = ip.complete('\\β…€')
173 name, matches = ip.complete('\\β…€')
174 nt.assert_equal(len(matches), 1)
174 nt.assert_equal(len(matches), 1)
175 nt.assert_equal(matches[0], '\\ROMAN NUMERAL FIVE')
175 nt.assert_equal(matches[0], '\\ROMAN NUMERAL FIVE')
176
176
177
177
178 def test_forward_unicode_completion():
178 def test_forward_unicode_completion():
179 ip = get_ipython()
179 ip = get_ipython()
180
180
181 name, matches = ip.complete('\\ROMAN NUMERAL FIVE')
181 name, matches = ip.complete('\\ROMAN NUMERAL FIVE')
182 nt.assert_equal(len(matches), 1)
182 nt.assert_equal(len(matches), 1)
183 nt.assert_equal(matches[0], 'β…€')
183 nt.assert_equal(matches[0], 'β…€')
184
184
185 @dec.knownfailureif(sys.platform == 'win32', 'Fails if there is a C:\\j... path')
185 @dec.knownfailureif(sys.platform == 'win32', 'Fails if there is a C:\\j... path')
186 def test_no_ascii_back_completion():
186 def test_no_ascii_back_completion():
187 ip = get_ipython()
187 ip = get_ipython()
188 with TemporaryWorkingDirectory(): # Avoid any filename completions
188 with TemporaryWorkingDirectory(): # Avoid any filename completions
189 # single ascii letter that don't have yet completions
189 # single ascii letter that don't have yet completions
190 for letter in 'jJ' :
190 for letter in 'jJ' :
191 name, matches = ip.complete('\\'+letter)
191 name, matches = ip.complete('\\'+letter)
192 nt.assert_equal(matches, [])
192 nt.assert_equal(matches, [])
193
193
194
194
195
195
196
196
197 class CompletionSplitterTestCase(unittest.TestCase):
197 class CompletionSplitterTestCase(unittest.TestCase):
198 def setUp(self):
198 def setUp(self):
199 self.sp = completer.CompletionSplitter()
199 self.sp = completer.CompletionSplitter()
200
200
201 def test_delim_setting(self):
201 def test_delim_setting(self):
202 self.sp.delims = ' '
202 self.sp.delims = ' '
203 nt.assert_equal(self.sp.delims, ' ')
203 nt.assert_equal(self.sp.delims, ' ')
204 nt.assert_equal(self.sp._delim_expr, '[\ ]')
204 nt.assert_equal(self.sp._delim_expr, '[\ ]')
205
205
206 def test_spaces(self):
206 def test_spaces(self):
207 """Test with only spaces as split chars."""
207 """Test with only spaces as split chars."""
208 self.sp.delims = ' '
208 self.sp.delims = ' '
209 t = [('foo', '', 'foo'),
209 t = [('foo', '', 'foo'),
210 ('run foo', '', 'foo'),
210 ('run foo', '', 'foo'),
211 ('run foo', 'bar', 'foo'),
211 ('run foo', 'bar', 'foo'),
212 ]
212 ]
213 check_line_split(self.sp, t)
213 check_line_split(self.sp, t)
214
214
215
215
216 def test_has_open_quotes1():
216 def test_has_open_quotes1():
217 for s in ["'", "'''", "'hi' '"]:
217 for s in ["'", "'''", "'hi' '"]:
218 nt.assert_equal(completer.has_open_quotes(s), "'")
218 nt.assert_equal(completer.has_open_quotes(s), "'")
219
219
220
220
221 def test_has_open_quotes2():
221 def test_has_open_quotes2():
222 for s in ['"', '"""', '"hi" "']:
222 for s in ['"', '"""', '"hi" "']:
223 nt.assert_equal(completer.has_open_quotes(s), '"')
223 nt.assert_equal(completer.has_open_quotes(s), '"')
224
224
225
225
226 def test_has_open_quotes3():
226 def test_has_open_quotes3():
227 for s in ["''", "''' '''", "'hi' 'ipython'"]:
227 for s in ["''", "''' '''", "'hi' 'ipython'"]:
228 nt.assert_false(completer.has_open_quotes(s))
228 nt.assert_false(completer.has_open_quotes(s))
229
229
230
230
231 def test_has_open_quotes4():
231 def test_has_open_quotes4():
232 for s in ['""', '""" """', '"hi" "ipython"']:
232 for s in ['""', '""" """', '"hi" "ipython"']:
233 nt.assert_false(completer.has_open_quotes(s))
233 nt.assert_false(completer.has_open_quotes(s))
234
234
235
235
236 @knownfailureif(sys.platform == 'win32', "abspath completions fail on Windows")
236 @knownfailureif(sys.platform == 'win32', "abspath completions fail on Windows")
237 def test_abspath_file_completions():
237 def test_abspath_file_completions():
238 ip = get_ipython()
238 ip = get_ipython()
239 with TemporaryDirectory() as tmpdir:
239 with TemporaryDirectory() as tmpdir:
240 prefix = os.path.join(tmpdir, 'foo')
240 prefix = os.path.join(tmpdir, 'foo')
241 suffixes = ['1', '2']
241 suffixes = ['1', '2']
242 names = [prefix+s for s in suffixes]
242 names = [prefix+s for s in suffixes]
243 for n in names:
243 for n in names:
244 open(n, 'w').close()
244 open(n, 'w').close()
245
245
246 # Check simple completion
246 # Check simple completion
247 c = ip.complete(prefix)[1]
247 c = ip.complete(prefix)[1]
248 nt.assert_equal(c, names)
248 nt.assert_equal(c, names)
249
249
250 # Now check with a function call
250 # Now check with a function call
251 cmd = 'a = f("%s' % prefix
251 cmd = 'a = f("%s' % prefix
252 c = ip.complete(prefix, cmd)[1]
252 c = ip.complete(prefix, cmd)[1]
253 comp = [prefix+s for s in suffixes]
253 comp = [prefix+s for s in suffixes]
254 nt.assert_equal(c, comp)
254 nt.assert_equal(c, comp)
255
255
256
256
257 def test_local_file_completions():
257 def test_local_file_completions():
258 ip = get_ipython()
258 ip = get_ipython()
259 with TemporaryWorkingDirectory():
259 with TemporaryWorkingDirectory():
260 prefix = './foo'
260 prefix = './foo'
261 suffixes = ['1', '2']
261 suffixes = ['1', '2']
262 names = [prefix+s for s in suffixes]
262 names = [prefix+s for s in suffixes]
263 for n in names:
263 for n in names:
264 open(n, 'w').close()
264 open(n, 'w').close()
265
265
266 # Check simple completion
266 # Check simple completion
267 c = ip.complete(prefix)[1]
267 c = ip.complete(prefix)[1]
268 nt.assert_equal(c, names)
268 nt.assert_equal(c, names)
269
269
270 # Now check with a function call
270 # Now check with a function call
271 cmd = 'a = f("%s' % prefix
271 cmd = 'a = f("%s' % prefix
272 c = ip.complete(prefix, cmd)[1]
272 c = ip.complete(prefix, cmd)[1]
273 comp = set(prefix+s for s in suffixes)
273 comp = set(prefix+s for s in suffixes)
274 nt.assert_true(comp.issubset(set(c)))
274 nt.assert_true(comp.issubset(set(c)))
275
275
276
276
277 def test_quoted_file_completions():
277 def test_quoted_file_completions():
278 ip = get_ipython()
278 ip = get_ipython()
279 with TemporaryWorkingDirectory():
279 with TemporaryWorkingDirectory():
280 name = "foo'bar"
280 name = "foo'bar"
281 open(name, 'w').close()
281 open(name, 'w').close()
282
282
283 # Don't escape Windows
283 # Don't escape Windows
284 escaped = name if sys.platform == "win32" else "foo\\'bar"
284 escaped = name if sys.platform == "win32" else "foo\\'bar"
285
285
286 # Single quote matches embedded single quote
286 # Single quote matches embedded single quote
287 text = "open('foo"
287 text = "open('foo"
288 c = ip.Completer._complete(cursor_line=0,
288 c = ip.Completer._complete(cursor_line=0,
289 cursor_pos=len(text),
289 cursor_pos=len(text),
290 full_text=text)[1]
290 full_text=text)[1]
291 nt.assert_equal(c, [escaped])
291 nt.assert_equal(c, [escaped])
292
292
293 # Double quote requires no escape
293 # Double quote requires no escape
294 text = 'open("foo'
294 text = 'open("foo'
295 c = ip.Completer._complete(cursor_line=0,
295 c = ip.Completer._complete(cursor_line=0,
296 cursor_pos=len(text),
296 cursor_pos=len(text),
297 full_text=text)[1]
297 full_text=text)[1]
298 nt.assert_equal(c, [name])
298 nt.assert_equal(c, [name])
299
299
300 # No quote requires an escape
300 # No quote requires an escape
301 text = '%ls foo'
301 text = '%ls foo'
302 c = ip.Completer._complete(cursor_line=0,
302 c = ip.Completer._complete(cursor_line=0,
303 cursor_pos=len(text),
303 cursor_pos=len(text),
304 full_text=text)[1]
304 full_text=text)[1]
305 nt.assert_equal(c, [escaped])
305 nt.assert_equal(c, [escaped])
306
306
307
307
308 def test_jedi():
308 def test_jedi():
309 """
309 """
310 A couple of issue we had with Jedi
310 A couple of issue we had with Jedi
311 """
311 """
312 ip = get_ipython()
312 ip = get_ipython()
313
313
314 def _test_complete(reason, s, comp, start=None, end=None):
314 def _test_complete(reason, s, comp, start=None, end=None):
315 l = len(s)
315 l = len(s)
316 start = start if start is not None else l
316 start = start if start is not None else l
317 end = end if end is not None else l
317 end = end if end is not None else l
318 with provisionalcompleter():
318 with provisionalcompleter():
319 ip.Completer.use_jedi = True
319 ip.Completer.use_jedi = True
320 completions = set(ip.Completer.completions(s, l))
320 completions = set(ip.Completer.completions(s, l))
321 ip.Completer.use_jedi = False
321 ip.Completer.use_jedi = False
322 assert_in(Completion(start, end, comp), completions, reason)
322 assert_in(Completion(start, end, comp), completions, reason)
323
323
324 def _test_not_complete(reason, s, comp):
324 def _test_not_complete(reason, s, comp):
325 l = len(s)
325 l = len(s)
326 with provisionalcompleter():
326 with provisionalcompleter():
327 ip.Completer.use_jedi = True
327 ip.Completer.use_jedi = True
328 completions = set(ip.Completer.completions(s, l))
328 completions = set(ip.Completer.completions(s, l))
329 ip.Completer.use_jedi = False
329 ip.Completer.use_jedi = False
330 assert_not_in(Completion(l, l, comp), completions, reason)
330 assert_not_in(Completion(l, l, comp), completions, reason)
331
331
332 import jedi
332 import jedi
333 jedi_version = tuple(int(i) for i in jedi.__version__.split('.')[:3])
333 jedi_version = tuple(int(i) for i in jedi.__version__.split('.')[:3])
334 if jedi_version > (0, 10):
334 if jedi_version > (0, 10):
335 yield _test_complete, 'jedi >0.9 should complete and not crash', 'a=1;a.', 'real'
335 yield _test_complete, 'jedi >0.9 should complete and not crash', 'a=1;a.', 'real'
336 yield _test_complete, 'can infer first argument', 'a=(1,"foo");a[0].', 'real'
336 yield _test_complete, 'can infer first argument', 'a=(1,"foo");a[0].', 'real'
337 yield _test_complete, 'can infer second argument', 'a=(1,"foo");a[1].', 'capitalize'
337 yield _test_complete, 'can infer second argument', 'a=(1,"foo");a[1].', 'capitalize'
338 yield _test_complete, 'cover duplicate completions', 'im', 'import', 0, 2
338 yield _test_complete, 'cover duplicate completions', 'im', 'import', 0, 2
339
339
340 yield _test_not_complete, 'does not mix types', 'a=(1,"foo");a[0].', 'capitalize'
340 yield _test_not_complete, 'does not mix types', 'a=(1,"foo");a[0].', 'capitalize'
341
341
342 def test_completion_have_signature():
342 def test_completion_have_signature():
343 """
343 """
344 Lets make sure jedi is capable of pulling out the signature of the function we are completing.
344 Lets make sure jedi is capable of pulling out the signature of the function we are completing.
345 """
345 """
346 ip = get_ipython()
346 ip = get_ipython()
347 with provisionalcompleter():
347 with provisionalcompleter():
348 ip.Completer.use_jedi = True
348 ip.Completer.use_jedi = True
349 completions = ip.Completer.completions('ope', 3)
349 completions = ip.Completer.completions('ope', 3)
350 c = next(completions) # should be `open`
350 c = next(completions) # should be `open`
351 ip.Completer.use_jedi = False
351 ip.Completer.use_jedi = False
352 assert 'file' in c.signature, "Signature of function was not found by completer"
352 assert 'file' in c.signature, "Signature of function was not found by completer"
353 assert 'encoding' in c.signature, "Signature of function was not found by completer"
353 assert 'encoding' in c.signature, "Signature of function was not found by completer"
354
354
355
355
356 def test_deduplicate_completions():
356 def test_deduplicate_completions():
357 """
357 """
358 Test that completions are correctly deduplicated (even if ranges are not the same)
358 Test that completions are correctly deduplicated (even if ranges are not the same)
359 """
359 """
360 ip = get_ipython()
360 ip = get_ipython()
361 ip.ex(textwrap.dedent('''
361 ip.ex(textwrap.dedent('''
362 class Z:
362 class Z:
363 zoo = 1
363 zoo = 1
364 '''))
364 '''))
365 with provisionalcompleter():
365 with provisionalcompleter():
366 ip.Completer.use_jedi = True
366 ip.Completer.use_jedi = True
367 l = list(_deduplicate_completions('Z.z', ip.Completer.completions('Z.z', 3)))
367 l = list(_deduplicate_completions('Z.z', ip.Completer.completions('Z.z', 3)))
368 ip.Completer.use_jedi = False
368 ip.Completer.use_jedi = False
369
369
370 assert len(l) == 1, 'Completions (Z.z<tab>) correctly deduplicate: %s ' % l
370 assert len(l) == 1, 'Completions (Z.z<tab>) correctly deduplicate: %s ' % l
371 assert l[0].text == 'zoo' # and not `it.accumulate`
371 assert l[0].text == 'zoo' # and not `it.accumulate`
372
372
373
373
374 def test_greedy_completions():
374 def test_greedy_completions():
375 """
375 """
376 Test the capability of the Greedy completer.
376 Test the capability of the Greedy completer.
377
377
378 Most of the test here does not really show off the greedy completer, for proof
378 Most of the test here does not really show off the greedy completer, for proof
379 each of the text below now pass with Jedi. The greedy completer is capable of more.
379 each of the text below now pass with Jedi. The greedy completer is capable of more.
380
380
381 See the :any:`test_dict_key_completion_contexts`
381 See the :any:`test_dict_key_completion_contexts`
382
382
383 """
383 """
384 ip = get_ipython()
384 ip = get_ipython()
385 ip.ex('a=list(range(5))')
385 ip.ex('a=list(range(5))')
386 _,c = ip.complete('.',line='a[0].')
386 _,c = ip.complete('.',line='a[0].')
387 nt.assert_false('.real' in c,
387 nt.assert_false('.real' in c,
388 "Shouldn't have completed on a[0]: %s"%c)
388 "Shouldn't have completed on a[0]: %s"%c)
389 with greedy_completion(), provisionalcompleter():
389 with greedy_completion(), provisionalcompleter():
390 def _(line, cursor_pos, expect, message, completion):
390 def _(line, cursor_pos, expect, message, completion):
391 ip.Completer.use_jedi = False
391 ip.Completer.use_jedi = False
392 _,c = ip.complete('.', line=line, cursor_pos=cursor_pos)
392 _,c = ip.complete('.', line=line, cursor_pos=cursor_pos)
393 nt.assert_in(expect, c, message % c)
393 nt.assert_in(expect, c, message % c)
394
394
395 ip.Completer.use_jedi = True
395 ip.Completer.use_jedi = True
396 with provisionalcompleter():
396 with provisionalcompleter():
397 completions = ip.Completer.completions(line, cursor_pos)
397 completions = ip.Completer.completions(line, cursor_pos)
398 nt.assert_in(completion, completions)
398 nt.assert_in(completion, completions)
399
399
400 yield _, 'a[0].', 5, 'a[0].real', "Should have completed on a[0].: %s", Completion(5,5, 'real')
400 yield _, 'a[0].', 5, 'a[0].real', "Should have completed on a[0].: %s", Completion(5,5, 'real')
401 yield _, 'a[0].r', 6, 'a[0].real', "Should have completed on a[0].r: %s", Completion(5,6, 'real')
401 yield _, 'a[0].r', 6, 'a[0].real', "Should have completed on a[0].r: %s", Completion(5,6, 'real')
402
402
403 if sys.version_info > (3, 4):
403 if sys.version_info > (3, 4):
404 yield _, 'a[0].from_', 10, 'a[0].from_bytes', "Should have completed on a[0].from_: %s", Completion(5, 10, 'from_bytes')
404 yield _, 'a[0].from_', 10, 'a[0].from_bytes', "Should have completed on a[0].from_: %s", Completion(5, 10, 'from_bytes')
405
405
406
406
407 def test_omit__names():
407 def test_omit__names():
408 # also happens to test IPCompleter as a configurable
408 # also happens to test IPCompleter as a configurable
409 ip = get_ipython()
409 ip = get_ipython()
410 ip._hidden_attr = 1
410 ip._hidden_attr = 1
411 ip._x = {}
411 ip._x = {}
412 c = ip.Completer
412 c = ip.Completer
413 ip.ex('ip=get_ipython()')
413 ip.ex('ip=get_ipython()')
414 cfg = Config()
414 cfg = Config()
415 cfg.IPCompleter.omit__names = 0
415 cfg.IPCompleter.omit__names = 0
416 c.update_config(cfg)
416 c.update_config(cfg)
417 with provisionalcompleter():
417 with provisionalcompleter():
418 c.use_jedi = False
418 c.use_jedi = False
419 s,matches = c.complete('ip.')
419 s,matches = c.complete('ip.')
420 nt.assert_in('ip.__str__', matches)
420 nt.assert_in('ip.__str__', matches)
421 nt.assert_in('ip._hidden_attr', matches)
421 nt.assert_in('ip._hidden_attr', matches)
422
422
423 # c.use_jedi = True
423 # c.use_jedi = True
424 # completions = set(c.completions('ip.', 3))
424 # completions = set(c.completions('ip.', 3))
425 # nt.assert_in(Completion(3, 3, '__str__'), completions)
425 # nt.assert_in(Completion(3, 3, '__str__'), completions)
426 # nt.assert_in(Completion(3,3, "_hidden_attr"), completions)
426 # nt.assert_in(Completion(3,3, "_hidden_attr"), completions)
427
427
428
428
429 cfg = Config()
429 cfg = Config()
430 cfg.IPCompleter.omit__names = 1
430 cfg.IPCompleter.omit__names = 1
431 c.update_config(cfg)
431 c.update_config(cfg)
432 with provisionalcompleter():
432 with provisionalcompleter():
433 c.use_jedi = False
433 c.use_jedi = False
434 s,matches = c.complete('ip.')
434 s,matches = c.complete('ip.')
435 nt.assert_not_in('ip.__str__', matches)
435 nt.assert_not_in('ip.__str__', matches)
436 # nt.assert_in('ip._hidden_attr', matches)
436 # nt.assert_in('ip._hidden_attr', matches)
437
437
438 # c.use_jedi = True
438 # c.use_jedi = True
439 # completions = set(c.completions('ip.', 3))
439 # completions = set(c.completions('ip.', 3))
440 # nt.assert_not_in(Completion(3,3,'__str__'), completions)
440 # nt.assert_not_in(Completion(3,3,'__str__'), completions)
441 # nt.assert_in(Completion(3,3, "_hidden_attr"), completions)
441 # nt.assert_in(Completion(3,3, "_hidden_attr"), completions)
442
442
443 cfg = Config()
443 cfg = Config()
444 cfg.IPCompleter.omit__names = 2
444 cfg.IPCompleter.omit__names = 2
445 c.update_config(cfg)
445 c.update_config(cfg)
446 with provisionalcompleter():
446 with provisionalcompleter():
447 c.use_jedi = False
447 c.use_jedi = False
448 s,matches = c.complete('ip.')
448 s,matches = c.complete('ip.')
449 nt.assert_not_in('ip.__str__', matches)
449 nt.assert_not_in('ip.__str__', matches)
450 nt.assert_not_in('ip._hidden_attr', matches)
450 nt.assert_not_in('ip._hidden_attr', matches)
451
451
452 # c.use_jedi = True
452 # c.use_jedi = True
453 # completions = set(c.completions('ip.', 3))
453 # completions = set(c.completions('ip.', 3))
454 # nt.assert_not_in(Completion(3,3,'__str__'), completions)
454 # nt.assert_not_in(Completion(3,3,'__str__'), completions)
455 # nt.assert_not_in(Completion(3,3, "_hidden_attr"), completions)
455 # nt.assert_not_in(Completion(3,3, "_hidden_attr"), completions)
456
456
457 with provisionalcompleter():
457 with provisionalcompleter():
458 c.use_jedi = False
458 c.use_jedi = False
459 s,matches = c.complete('ip._x.')
459 s,matches = c.complete('ip._x.')
460 nt.assert_in('ip._x.keys', matches)
460 nt.assert_in('ip._x.keys', matches)
461
461
462 # c.use_jedi = True
462 # c.use_jedi = True
463 # completions = set(c.completions('ip._x.', 6))
463 # completions = set(c.completions('ip._x.', 6))
464 # nt.assert_in(Completion(6,6, "keys"), completions)
464 # nt.assert_in(Completion(6,6, "keys"), completions)
465
465
466 del ip._hidden_attr
466 del ip._hidden_attr
467 del ip._x
467 del ip._x
468
468
469
469
470 def test_limit_to__all__False_ok():
470 def test_limit_to__all__False_ok():
471 """
471 """
472 Limit to all is deprecated, once we remove it this test can go away.
472 Limit to all is deprecated, once we remove it this test can go away.
473 """
473 """
474 ip = get_ipython()
474 ip = get_ipython()
475 c = ip.Completer
475 c = ip.Completer
476 c.use_jedi = False
476 c.use_jedi = False
477 ip.ex('class D: x=24')
477 ip.ex('class D: x=24')
478 ip.ex('d=D()')
478 ip.ex('d=D()')
479 cfg = Config()
479 cfg = Config()
480 cfg.IPCompleter.limit_to__all__ = False
480 cfg.IPCompleter.limit_to__all__ = False
481 c.update_config(cfg)
481 c.update_config(cfg)
482 s, matches = c.complete('d.')
482 s, matches = c.complete('d.')
483 nt.assert_in('d.x', matches)
483 nt.assert_in('d.x', matches)
484
484
485
485
486 def test_get__all__entries_ok():
486 def test_get__all__entries_ok():
487 class A(object):
487 class A(object):
488 __all__ = ['x', 1]
488 __all__ = ['x', 1]
489 words = completer.get__all__entries(A())
489 words = completer.get__all__entries(A())
490 nt.assert_equal(words, ['x'])
490 nt.assert_equal(words, ['x'])
491
491
492
492
493 def test_get__all__entries_no__all__ok():
493 def test_get__all__entries_no__all__ok():
494 class A(object):
494 class A(object):
495 pass
495 pass
496 words = completer.get__all__entries(A())
496 words = completer.get__all__entries(A())
497 nt.assert_equal(words, [])
497 nt.assert_equal(words, [])
498
498
499
499
500 def test_func_kw_completions():
500 def test_func_kw_completions():
501 ip = get_ipython()
501 ip = get_ipython()
502 c = ip.Completer
502 c = ip.Completer
503 c.use_jedi = False
503 c.use_jedi = False
504 ip.ex('def myfunc(a=1,b=2): return a+b')
504 ip.ex('def myfunc(a=1,b=2): return a+b')
505 s, matches = c.complete(None, 'myfunc(1,b')
505 s, matches = c.complete(None, 'myfunc(1,b')
506 nt.assert_in('b=', matches)
506 nt.assert_in('b=', matches)
507 # Simulate completing with cursor right after b (pos==10):
507 # Simulate completing with cursor right after b (pos==10):
508 s, matches = c.complete(None, 'myfunc(1,b)', 10)
508 s, matches = c.complete(None, 'myfunc(1,b)', 10)
509 nt.assert_in('b=', matches)
509 nt.assert_in('b=', matches)
510 s, matches = c.complete(None, 'myfunc(a="escaped\\")string",b')
510 s, matches = c.complete(None, 'myfunc(a="escaped\\")string",b')
511 nt.assert_in('b=', matches)
511 nt.assert_in('b=', matches)
512 #builtin function
512 #builtin function
513 s, matches = c.complete(None, 'min(k, k')
513 s, matches = c.complete(None, 'min(k, k')
514 nt.assert_in('key=', matches)
514 nt.assert_in('key=', matches)
515
515
516
516
517 def test_default_arguments_from_docstring():
517 def test_default_arguments_from_docstring():
518 ip = get_ipython()
518 ip = get_ipython()
519 c = ip.Completer
519 c = ip.Completer
520 kwd = c._default_arguments_from_docstring(
520 kwd = c._default_arguments_from_docstring(
521 'min(iterable[, key=func]) -> value')
521 'min(iterable[, key=func]) -> value')
522 nt.assert_equal(kwd, ['key'])
522 nt.assert_equal(kwd, ['key'])
523 #with cython type etc
523 #with cython type etc
524 kwd = c._default_arguments_from_docstring(
524 kwd = c._default_arguments_from_docstring(
525 'Minuit.migrad(self, int ncall=10000, resume=True, int nsplit=1)\n')
525 'Minuit.migrad(self, int ncall=10000, resume=True, int nsplit=1)\n')
526 nt.assert_equal(kwd, ['ncall', 'resume', 'nsplit'])
526 nt.assert_equal(kwd, ['ncall', 'resume', 'nsplit'])
527 #white spaces
527 #white spaces
528 kwd = c._default_arguments_from_docstring(
528 kwd = c._default_arguments_from_docstring(
529 '\n Minuit.migrad(self, int ncall=10000, resume=True, int nsplit=1)\n')
529 '\n Minuit.migrad(self, int ncall=10000, resume=True, int nsplit=1)\n')
530 nt.assert_equal(kwd, ['ncall', 'resume', 'nsplit'])
530 nt.assert_equal(kwd, ['ncall', 'resume', 'nsplit'])
531
531
532 def test_line_magics():
532 def test_line_magics():
533 ip = get_ipython()
533 ip = get_ipython()
534 c = ip.Completer
534 c = ip.Completer
535 s, matches = c.complete(None, 'lsmag')
535 s, matches = c.complete(None, 'lsmag')
536 nt.assert_in('%lsmagic', matches)
536 nt.assert_in('%lsmagic', matches)
537 s, matches = c.complete(None, '%lsmag')
537 s, matches = c.complete(None, '%lsmag')
538 nt.assert_in('%lsmagic', matches)
538 nt.assert_in('%lsmagic', matches)
539
539
540
540
541 def test_cell_magics():
541 def test_cell_magics():
542 from IPython.core.magic import register_cell_magic
542 from IPython.core.magic import register_cell_magic
543
543
544 @register_cell_magic
544 @register_cell_magic
545 def _foo_cellm(line, cell):
545 def _foo_cellm(line, cell):
546 pass
546 pass
547
547
548 ip = get_ipython()
548 ip = get_ipython()
549 c = ip.Completer
549 c = ip.Completer
550
550
551 s, matches = c.complete(None, '_foo_ce')
551 s, matches = c.complete(None, '_foo_ce')
552 nt.assert_in('%%_foo_cellm', matches)
552 nt.assert_in('%%_foo_cellm', matches)
553 s, matches = c.complete(None, '%%_foo_ce')
553 s, matches = c.complete(None, '%%_foo_ce')
554 nt.assert_in('%%_foo_cellm', matches)
554 nt.assert_in('%%_foo_cellm', matches)
555
555
556
556
557 def test_line_cell_magics():
557 def test_line_cell_magics():
558 from IPython.core.magic import register_line_cell_magic
558 from IPython.core.magic import register_line_cell_magic
559
559
560 @register_line_cell_magic
560 @register_line_cell_magic
561 def _bar_cellm(line, cell):
561 def _bar_cellm(line, cell):
562 pass
562 pass
563
563
564 ip = get_ipython()
564 ip = get_ipython()
565 c = ip.Completer
565 c = ip.Completer
566
566
567 # The policy here is trickier, see comments in completion code. The
567 # The policy here is trickier, see comments in completion code. The
568 # returned values depend on whether the user passes %% or not explicitly,
568 # returned values depend on whether the user passes %% or not explicitly,
569 # and this will show a difference if the same name is both a line and cell
569 # and this will show a difference if the same name is both a line and cell
570 # magic.
570 # magic.
571 s, matches = c.complete(None, '_bar_ce')
571 s, matches = c.complete(None, '_bar_ce')
572 nt.assert_in('%_bar_cellm', matches)
572 nt.assert_in('%_bar_cellm', matches)
573 nt.assert_in('%%_bar_cellm', matches)
573 nt.assert_in('%%_bar_cellm', matches)
574 s, matches = c.complete(None, '%_bar_ce')
574 s, matches = c.complete(None, '%_bar_ce')
575 nt.assert_in('%_bar_cellm', matches)
575 nt.assert_in('%_bar_cellm', matches)
576 nt.assert_in('%%_bar_cellm', matches)
576 nt.assert_in('%%_bar_cellm', matches)
577 s, matches = c.complete(None, '%%_bar_ce')
577 s, matches = c.complete(None, '%%_bar_ce')
578 nt.assert_not_in('%_bar_cellm', matches)
578 nt.assert_not_in('%_bar_cellm', matches)
579 nt.assert_in('%%_bar_cellm', matches)
579 nt.assert_in('%%_bar_cellm', matches)
580
580
581
581
582 def test_magic_completion_order():
582 def test_magic_completion_order():
583 ip = get_ipython()
583 ip = get_ipython()
584 c = ip.Completer
584 c = ip.Completer
585
585
586 # Test ordering of line and cell magics.
586 # Test ordering of line and cell magics.
587 text, matches = c.complete("timeit")
587 text, matches = c.complete("timeit")
588 nt.assert_equal(matches, ["%timeit", "%%timeit"])
588 nt.assert_equal(matches, ["%timeit", "%%timeit"])
589
589
590
590
591 def test_magic_completion_shadowing():
591 def test_magic_completion_shadowing():
592 ip = get_ipython()
592 ip = get_ipython()
593 c = ip.Completer
593 c = ip.Completer
594 c.use_jedi = False
594 c.use_jedi = False
595
595
596 # Before importing matplotlib, %matplotlib magic should be the only option.
596 # Before importing matplotlib, %matplotlib magic should be the only option.
597 text, matches = c.complete("mat")
597 text, matches = c.complete("mat")
598 nt.assert_equal(matches, ["%matplotlib"])
598 nt.assert_equal(matches, ["%matplotlib"])
599
599
600 # The newly introduced name should shadow the magic.
600 # The newly introduced name should shadow the magic.
601 ip.run_cell("matplotlib = 1")
601 ip.run_cell("matplotlib = 1")
602 text, matches = c.complete("mat")
602 text, matches = c.complete("mat")
603 nt.assert_equal(matches, ["matplotlib"])
603 nt.assert_equal(matches, ["matplotlib"])
604
604
605 # After removing matplotlib from namespace, the magic should again be
605 # After removing matplotlib from namespace, the magic should again be
606 # the only option.
606 # the only option.
607 del ip.user_ns["matplotlib"]
607 del ip.user_ns["matplotlib"]
608 text, matches = c.complete("mat")
608 text, matches = c.complete("mat")
609 nt.assert_equal(matches, ["%matplotlib"])
609 nt.assert_equal(matches, ["%matplotlib"])
610
610
611 def test_magic_completion_shadowing_explicit():
611 def test_magic_completion_shadowing_explicit():
612 """
612 """
613 If the user try to complete a shadowed magic, and explicit % start should
613 If the user try to complete a shadowed magic, and explicit % start should
614 still return the completions.
614 still return the completions.
615 """
615 """
616 ip = get_ipython()
616 ip = get_ipython()
617 c = ip.Completer
617 c = ip.Completer
618
618
619 # Before importing matplotlib, %matplotlib magic should be the only option.
619 # Before importing matplotlib, %matplotlib magic should be the only option.
620 text, matches = c.complete("%mat")
620 text, matches = c.complete("%mat")
621 nt.assert_equal(matches, ["%matplotlib"])
621 nt.assert_equal(matches, ["%matplotlib"])
622
622
623 ip.run_cell("matplotlib = 1")
623 ip.run_cell("matplotlib = 1")
624
624
625 # After removing matplotlib from namespace, the magic should still be
625 # After removing matplotlib from namespace, the magic should still be
626 # the only option.
626 # the only option.
627 text, matches = c.complete("%mat")
627 text, matches = c.complete("%mat")
628 nt.assert_equal(matches, ["%matplotlib"])
628 nt.assert_equal(matches, ["%matplotlib"])
629
629
630 def test_magic_config():
630 def test_magic_config():
631 ip = get_ipython()
631 ip = get_ipython()
632 c = ip.Completer
632 c = ip.Completer
633
633
634 s, matches = c.complete(None, 'conf')
634 s, matches = c.complete(None, 'conf')
635 nt.assert_in('%config', matches)
635 nt.assert_in('%config', matches)
636 s, matches = c.complete(None, 'conf')
636 s, matches = c.complete(None, 'conf')
637 nt.assert_not_in('AliasManager', matches)
637 nt.assert_not_in('AliasManager', matches)
638 s, matches = c.complete(None, 'config ')
638 s, matches = c.complete(None, 'config ')
639 nt.assert_in('AliasManager', matches)
639 nt.assert_in('AliasManager', matches)
640 s, matches = c.complete(None, '%config ')
640 s, matches = c.complete(None, '%config ')
641 nt.assert_in('AliasManager', matches)
641 nt.assert_in('AliasManager', matches)
642 s, matches = c.complete(None, 'config Ali')
642 s, matches = c.complete(None, 'config Ali')
643 nt.assert_list_equal(['AliasManager'], matches)
643 nt.assert_list_equal(['AliasManager'], matches)
644 s, matches = c.complete(None, '%config Ali')
644 s, matches = c.complete(None, '%config Ali')
645 nt.assert_list_equal(['AliasManager'], matches)
645 nt.assert_list_equal(['AliasManager'], matches)
646 s, matches = c.complete(None, 'config AliasManager')
646 s, matches = c.complete(None, 'config AliasManager')
647 nt.assert_list_equal(['AliasManager'], matches)
647 nt.assert_list_equal(['AliasManager'], matches)
648 s, matches = c.complete(None, '%config AliasManager')
648 s, matches = c.complete(None, '%config AliasManager')
649 nt.assert_list_equal(['AliasManager'], matches)
649 nt.assert_list_equal(['AliasManager'], matches)
650 s, matches = c.complete(None, 'config AliasManager.')
650 s, matches = c.complete(None, 'config AliasManager.')
651 nt.assert_in('AliasManager.default_aliases', matches)
651 nt.assert_in('AliasManager.default_aliases', matches)
652 s, matches = c.complete(None, '%config AliasManager.')
652 s, matches = c.complete(None, '%config AliasManager.')
653 nt.assert_in('AliasManager.default_aliases', matches)
653 nt.assert_in('AliasManager.default_aliases', matches)
654 s, matches = c.complete(None, 'config AliasManager.de')
654 s, matches = c.complete(None, 'config AliasManager.de')
655 nt.assert_list_equal(['AliasManager.default_aliases'], matches)
655 nt.assert_list_equal(['AliasManager.default_aliases'], matches)
656 s, matches = c.complete(None, 'config AliasManager.de')
656 s, matches = c.complete(None, 'config AliasManager.de')
657 nt.assert_list_equal(['AliasManager.default_aliases'], matches)
657 nt.assert_list_equal(['AliasManager.default_aliases'], matches)
658
658
659
659
660 def test_magic_color():
660 def test_magic_color():
661 ip = get_ipython()
661 ip = get_ipython()
662 c = ip.Completer
662 c = ip.Completer
663
663
664 s, matches = c.complete(None, 'colo')
664 s, matches = c.complete(None, 'colo')
665 nt.assert_in('%colors', matches)
665 nt.assert_in('%colors', matches)
666 s, matches = c.complete(None, 'colo')
666 s, matches = c.complete(None, 'colo')
667 nt.assert_not_in('NoColor', matches)
667 nt.assert_not_in('NoColor', matches)
668 s, matches = c.complete(None, '%colors') # No trailing space
668 s, matches = c.complete(None, '%colors') # No trailing space
669 nt.assert_not_in('NoColor', matches)
669 nt.assert_not_in('NoColor', matches)
670 s, matches = c.complete(None, 'colors ')
670 s, matches = c.complete(None, 'colors ')
671 nt.assert_in('NoColor', matches)
671 nt.assert_in('NoColor', matches)
672 s, matches = c.complete(None, '%colors ')
672 s, matches = c.complete(None, '%colors ')
673 nt.assert_in('NoColor', matches)
673 nt.assert_in('NoColor', matches)
674 s, matches = c.complete(None, 'colors NoCo')
674 s, matches = c.complete(None, 'colors NoCo')
675 nt.assert_list_equal(['NoColor'], matches)
675 nt.assert_list_equal(['NoColor'], matches)
676 s, matches = c.complete(None, '%colors NoCo')
676 s, matches = c.complete(None, '%colors NoCo')
677 nt.assert_list_equal(['NoColor'], matches)
677 nt.assert_list_equal(['NoColor'], matches)
678
678
679
679
680 def test_match_dict_keys():
680 def test_match_dict_keys():
681 """
681 """
682 Test that match_dict_keys works on a couple of use case does return what
682 Test that match_dict_keys works on a couple of use case does return what
683 expected, and does not crash
683 expected, and does not crash
684 """
684 """
685 delims = ' \t\n`!@#$^&*()=+[{]}\\|;:\'",<>?'
685 delims = ' \t\n`!@#$^&*()=+[{]}\\|;:\'",<>?'
686
686
687
687
688 keys = ['foo', b'far']
688 keys = ['foo', b'far']
689 assert match_dict_keys(keys, "b'", delims=delims) == ("'", 2 ,['far'])
689 assert match_dict_keys(keys, "b'", delims=delims) == ("'", 2 ,['far'])
690 assert match_dict_keys(keys, "b'f", delims=delims) == ("'", 2 ,['far'])
690 assert match_dict_keys(keys, "b'f", delims=delims) == ("'", 2 ,['far'])
691 assert match_dict_keys(keys, 'b"', delims=delims) == ('"', 2 ,['far'])
691 assert match_dict_keys(keys, 'b"', delims=delims) == ('"', 2 ,['far'])
692 assert match_dict_keys(keys, 'b"f', delims=delims) == ('"', 2 ,['far'])
692 assert match_dict_keys(keys, 'b"f', delims=delims) == ('"', 2 ,['far'])
693
693
694 assert match_dict_keys(keys, "'", delims=delims) == ("'", 1 ,['foo'])
694 assert match_dict_keys(keys, "'", delims=delims) == ("'", 1 ,['foo'])
695 assert match_dict_keys(keys, "'f", delims=delims) == ("'", 1 ,['foo'])
695 assert match_dict_keys(keys, "'f", delims=delims) == ("'", 1 ,['foo'])
696 assert match_dict_keys(keys, '"', delims=delims) == ('"', 1 ,['foo'])
696 assert match_dict_keys(keys, '"', delims=delims) == ('"', 1 ,['foo'])
697 assert match_dict_keys(keys, '"f', delims=delims) == ('"', 1 ,['foo'])
697 assert match_dict_keys(keys, '"f', delims=delims) == ('"', 1 ,['foo'])
698
698
699 match_dict_keys
699 match_dict_keys
700
700
701
701
702 def test_dict_key_completion_string():
702 def test_dict_key_completion_string():
703 """Test dictionary key completion for string keys"""
703 """Test dictionary key completion for string keys"""
704 ip = get_ipython()
704 ip = get_ipython()
705 complete = ip.Completer.complete
705 complete = ip.Completer.complete
706
706
707 ip.user_ns['d'] = {'abc': None}
707 ip.user_ns['d'] = {'abc': None}
708
708
709 # check completion at different stages
709 # check completion at different stages
710 _, matches = complete(line_buffer="d[")
710 _, matches = complete(line_buffer="d[")
711 nt.assert_in("'abc'", matches)
711 nt.assert_in("'abc'", matches)
712 nt.assert_not_in("'abc']", matches)
712 nt.assert_not_in("'abc']", matches)
713
713
714 _, matches = complete(line_buffer="d['")
714 _, matches = complete(line_buffer="d['")
715 nt.assert_in("abc", matches)
715 nt.assert_in("abc", matches)
716 nt.assert_not_in("abc']", matches)
716 nt.assert_not_in("abc']", matches)
717
717
718 _, matches = complete(line_buffer="d['a")
718 _, matches = complete(line_buffer="d['a")
719 nt.assert_in("abc", matches)
719 nt.assert_in("abc", matches)
720 nt.assert_not_in("abc']", matches)
720 nt.assert_not_in("abc']", matches)
721
721
722 # check use of different quoting
722 # check use of different quoting
723 _, matches = complete(line_buffer="d[\"")
723 _, matches = complete(line_buffer="d[\"")
724 nt.assert_in("abc", matches)
724 nt.assert_in("abc", matches)
725 nt.assert_not_in('abc\"]', matches)
725 nt.assert_not_in('abc\"]', matches)
726
726
727 _, matches = complete(line_buffer="d[\"a")
727 _, matches = complete(line_buffer="d[\"a")
728 nt.assert_in("abc", matches)
728 nt.assert_in("abc", matches)
729 nt.assert_not_in('abc\"]', matches)
729 nt.assert_not_in('abc\"]', matches)
730
730
731 # check sensitivity to following context
731 # check sensitivity to following context
732 _, matches = complete(line_buffer="d[]", cursor_pos=2)
732 _, matches = complete(line_buffer="d[]", cursor_pos=2)
733 nt.assert_in("'abc'", matches)
733 nt.assert_in("'abc'", matches)
734
734
735 _, matches = complete(line_buffer="d['']", cursor_pos=3)
735 _, matches = complete(line_buffer="d['']", cursor_pos=3)
736 nt.assert_in("abc", matches)
736 nt.assert_in("abc", matches)
737 nt.assert_not_in("abc'", matches)
737 nt.assert_not_in("abc'", matches)
738 nt.assert_not_in("abc']", matches)
738 nt.assert_not_in("abc']", matches)
739
739
740 # check multiple solutions are correctly returned and that noise is not
740 # check multiple solutions are correctly returned and that noise is not
741 ip.user_ns['d'] = {'abc': None, 'abd': None, 'bad': None, object(): None,
741 ip.user_ns['d'] = {'abc': None, 'abd': None, 'bad': None, object(): None,
742 5: None}
742 5: None}
743
743
744 _, matches = complete(line_buffer="d['a")
744 _, matches = complete(line_buffer="d['a")
745 nt.assert_in("abc", matches)
745 nt.assert_in("abc", matches)
746 nt.assert_in("abd", matches)
746 nt.assert_in("abd", matches)
747 nt.assert_not_in("bad", matches)
747 nt.assert_not_in("bad", matches)
748 assert not any(m.endswith((']', '"', "'")) for m in matches), matches
748 assert not any(m.endswith((']', '"', "'")) for m in matches), matches
749
749
750 # check escaping and whitespace
750 # check escaping and whitespace
751 ip.user_ns['d'] = {'a\nb': None, 'a\'b': None, 'a"b': None, 'a word': None}
751 ip.user_ns['d'] = {'a\nb': None, 'a\'b': None, 'a"b': None, 'a word': None}
752 _, matches = complete(line_buffer="d['a")
752 _, matches = complete(line_buffer="d['a")
753 nt.assert_in("a\\nb", matches)
753 nt.assert_in("a\\nb", matches)
754 nt.assert_in("a\\'b", matches)
754 nt.assert_in("a\\'b", matches)
755 nt.assert_in("a\"b", matches)
755 nt.assert_in("a\"b", matches)
756 nt.assert_in("a word", matches)
756 nt.assert_in("a word", matches)
757 assert not any(m.endswith((']', '"', "'")) for m in matches), matches
757 assert not any(m.endswith((']', '"', "'")) for m in matches), matches
758
758
759 # - can complete on non-initial word of the string
759 # - can complete on non-initial word of the string
760 _, matches = complete(line_buffer="d['a w")
760 _, matches = complete(line_buffer="d['a w")
761 nt.assert_in("word", matches)
761 nt.assert_in("word", matches)
762
762
763 # - understands quote escaping
763 # - understands quote escaping
764 _, matches = complete(line_buffer="d['a\\'")
764 _, matches = complete(line_buffer="d['a\\'")
765 nt.assert_in("b", matches)
765 nt.assert_in("b", matches)
766
766
767 # - default quoting should work like repr
767 # - default quoting should work like repr
768 _, matches = complete(line_buffer="d[")
768 _, matches = complete(line_buffer="d[")
769 nt.assert_in("\"a'b\"", matches)
769 nt.assert_in("\"a'b\"", matches)
770
770
771 # - when opening quote with ", possible to match with unescaped apostrophe
771 # - when opening quote with ", possible to match with unescaped apostrophe
772 _, matches = complete(line_buffer="d[\"a'")
772 _, matches = complete(line_buffer="d[\"a'")
773 nt.assert_in("b", matches)
773 nt.assert_in("b", matches)
774
774
775 # need to not split at delims that readline won't split at
775 # need to not split at delims that readline won't split at
776 if '-' not in ip.Completer.splitter.delims:
776 if '-' not in ip.Completer.splitter.delims:
777 ip.user_ns['d'] = {'before-after': None}
777 ip.user_ns['d'] = {'before-after': None}
778 _, matches = complete(line_buffer="d['before-af")
778 _, matches = complete(line_buffer="d['before-af")
779 nt.assert_in('before-after', matches)
779 nt.assert_in('before-after', matches)
780
780
781 def test_dict_key_completion_contexts():
781 def test_dict_key_completion_contexts():
782 """Test expression contexts in which dict key completion occurs"""
782 """Test expression contexts in which dict key completion occurs"""
783 ip = get_ipython()
783 ip = get_ipython()
784 complete = ip.Completer.complete
784 complete = ip.Completer.complete
785 d = {'abc': None}
785 d = {'abc': None}
786 ip.user_ns['d'] = d
786 ip.user_ns['d'] = d
787
787
788 class C:
788 class C:
789 data = d
789 data = d
790 ip.user_ns['C'] = C
790 ip.user_ns['C'] = C
791 ip.user_ns['get'] = lambda: d
791 ip.user_ns['get'] = lambda: d
792
792
793 def assert_no_completion(**kwargs):
793 def assert_no_completion(**kwargs):
794 _, matches = complete(**kwargs)
794 _, matches = complete(**kwargs)
795 nt.assert_not_in('abc', matches)
795 nt.assert_not_in('abc', matches)
796 nt.assert_not_in('abc\'', matches)
796 nt.assert_not_in('abc\'', matches)
797 nt.assert_not_in('abc\']', matches)
797 nt.assert_not_in('abc\']', matches)
798 nt.assert_not_in('\'abc\'', matches)
798 nt.assert_not_in('\'abc\'', matches)
799 nt.assert_not_in('\'abc\']', matches)
799 nt.assert_not_in('\'abc\']', matches)
800
800
801 def assert_completion(**kwargs):
801 def assert_completion(**kwargs):
802 _, matches = complete(**kwargs)
802 _, matches = complete(**kwargs)
803 nt.assert_in("'abc'", matches)
803 nt.assert_in("'abc'", matches)
804 nt.assert_not_in("'abc']", matches)
804 nt.assert_not_in("'abc']", matches)
805
805
806 # no completion after string closed, even if reopened
806 # no completion after string closed, even if reopened
807 assert_no_completion(line_buffer="d['a'")
807 assert_no_completion(line_buffer="d['a'")
808 assert_no_completion(line_buffer="d[\"a\"")
808 assert_no_completion(line_buffer="d[\"a\"")
809 assert_no_completion(line_buffer="d['a' + ")
809 assert_no_completion(line_buffer="d['a' + ")
810 assert_no_completion(line_buffer="d['a' + '")
810 assert_no_completion(line_buffer="d['a' + '")
811
811
812 # completion in non-trivial expressions
812 # completion in non-trivial expressions
813 assert_completion(line_buffer="+ d[")
813 assert_completion(line_buffer="+ d[")
814 assert_completion(line_buffer="(d[")
814 assert_completion(line_buffer="(d[")
815 assert_completion(line_buffer="C.data[")
815 assert_completion(line_buffer="C.data[")
816
816
817 # greedy flag
817 # greedy flag
818 def assert_completion(**kwargs):
818 def assert_completion(**kwargs):
819 _, matches = complete(**kwargs)
819 _, matches = complete(**kwargs)
820 nt.assert_in("get()['abc']", matches)
820 nt.assert_in("get()['abc']", matches)
821
821
822 assert_no_completion(line_buffer="get()[")
822 assert_no_completion(line_buffer="get()[")
823 with greedy_completion():
823 with greedy_completion():
824 assert_completion(line_buffer="get()[")
824 assert_completion(line_buffer="get()[")
825 assert_completion(line_buffer="get()['")
825 assert_completion(line_buffer="get()['")
826 assert_completion(line_buffer="get()['a")
826 assert_completion(line_buffer="get()['a")
827 assert_completion(line_buffer="get()['ab")
827 assert_completion(line_buffer="get()['ab")
828 assert_completion(line_buffer="get()['abc")
828 assert_completion(line_buffer="get()['abc")
829
829
830
830
831
831
832 def test_dict_key_completion_bytes():
832 def test_dict_key_completion_bytes():
833 """Test handling of bytes in dict key completion"""
833 """Test handling of bytes in dict key completion"""
834 ip = get_ipython()
834 ip = get_ipython()
835 complete = ip.Completer.complete
835 complete = ip.Completer.complete
836
836
837 ip.user_ns['d'] = {'abc': None, b'abd': None}
837 ip.user_ns['d'] = {'abc': None, b'abd': None}
838
838
839 _, matches = complete(line_buffer="d[")
839 _, matches = complete(line_buffer="d[")
840 nt.assert_in("'abc'", matches)
840 nt.assert_in("'abc'", matches)
841 nt.assert_in("b'abd'", matches)
841 nt.assert_in("b'abd'", matches)
842
842
843 if False: # not currently implemented
843 if False: # not currently implemented
844 _, matches = complete(line_buffer="d[b")
844 _, matches = complete(line_buffer="d[b")
845 nt.assert_in("b'abd'", matches)
845 nt.assert_in("b'abd'", matches)
846 nt.assert_not_in("b'abc'", matches)
846 nt.assert_not_in("b'abc'", matches)
847
847
848 _, matches = complete(line_buffer="d[b'")
848 _, matches = complete(line_buffer="d[b'")
849 nt.assert_in("abd", matches)
849 nt.assert_in("abd", matches)
850 nt.assert_not_in("abc", matches)
850 nt.assert_not_in("abc", matches)
851
851
852 _, matches = complete(line_buffer="d[B'")
852 _, matches = complete(line_buffer="d[B'")
853 nt.assert_in("abd", matches)
853 nt.assert_in("abd", matches)
854 nt.assert_not_in("abc", matches)
854 nt.assert_not_in("abc", matches)
855
855
856 _, matches = complete(line_buffer="d['")
856 _, matches = complete(line_buffer="d['")
857 nt.assert_in("abc", matches)
857 nt.assert_in("abc", matches)
858 nt.assert_not_in("abd", matches)
858 nt.assert_not_in("abd", matches)
859
859
860
860
861 def test_dict_key_completion_unicode_py3():
861 def test_dict_key_completion_unicode_py3():
862 """Test handling of unicode in dict key completion"""
862 """Test handling of unicode in dict key completion"""
863 ip = get_ipython()
863 ip = get_ipython()
864 complete = ip.Completer.complete
864 complete = ip.Completer.complete
865
865
866 ip.user_ns['d'] = {u'a\u05d0': None}
866 ip.user_ns['d'] = {u'a\u05d0': None}
867
867
868 # query using escape
868 # query using escape
869 if sys.platform != 'win32':
869 if sys.platform != 'win32':
870 # Known failure on Windows
870 # Known failure on Windows
871 _, matches = complete(line_buffer="d['a\\u05d0")
871 _, matches = complete(line_buffer="d['a\\u05d0")
872 nt.assert_in("u05d0", matches) # tokenized after \\
872 nt.assert_in("u05d0", matches) # tokenized after \\
873
873
874 # query using character
874 # query using character
875 _, matches = complete(line_buffer="d['a\u05d0")
875 _, matches = complete(line_buffer="d['a\u05d0")
876 nt.assert_in(u"a\u05d0", matches)
876 nt.assert_in(u"a\u05d0", matches)
877
877
878 with greedy_completion():
878 with greedy_completion():
879 # query using escape
879 # query using escape
880 _, matches = complete(line_buffer="d['a\\u05d0")
880 _, matches = complete(line_buffer="d['a\\u05d0")
881 nt.assert_in("d['a\\u05d0']", matches) # tokenized after \\
881 nt.assert_in("d['a\\u05d0']", matches) # tokenized after \\
882
882
883 # query using character
883 # query using character
884 _, matches = complete(line_buffer="d['a\u05d0")
884 _, matches = complete(line_buffer="d['a\u05d0")
885 nt.assert_in(u"d['a\u05d0']", matches)
885 nt.assert_in(u"d['a\u05d0']", matches)
886
886
887
887
888
888
889 @dec.skip_without('numpy')
889 @dec.skip_without('numpy')
890 def test_struct_array_key_completion():
890 def test_struct_array_key_completion():
891 """Test dict key completion applies to numpy struct arrays"""
891 """Test dict key completion applies to numpy struct arrays"""
892 import numpy
892 import numpy
893 ip = get_ipython()
893 ip = get_ipython()
894 complete = ip.Completer.complete
894 complete = ip.Completer.complete
895 ip.user_ns['d'] = numpy.array([], dtype=[('hello', 'f'), ('world', 'f')])
895 ip.user_ns['d'] = numpy.array([], dtype=[('hello', 'f'), ('world', 'f')])
896 _, matches = complete(line_buffer="d['")
896 _, matches = complete(line_buffer="d['")
897 nt.assert_in("hello", matches)
897 nt.assert_in("hello", matches)
898 nt.assert_in("world", matches)
898 nt.assert_in("world", matches)
899 # complete on the numpy struct itself
899 # complete on the numpy struct itself
900 dt = numpy.dtype([('my_head', [('my_dt', '>u4'), ('my_df', '>u4')]),
900 dt = numpy.dtype([('my_head', [('my_dt', '>u4'), ('my_df', '>u4')]),
901 ('my_data', '>f4', 5)])
901 ('my_data', '>f4', 5)])
902 x = numpy.zeros(2, dtype=dt)
902 x = numpy.zeros(2, dtype=dt)
903 ip.user_ns['d'] = x[1]
903 ip.user_ns['d'] = x[1]
904 _, matches = complete(line_buffer="d['")
904 _, matches = complete(line_buffer="d['")
905 nt.assert_in("my_head", matches)
905 nt.assert_in("my_head", matches)
906 nt.assert_in("my_data", matches)
906 nt.assert_in("my_data", matches)
907 # complete on a nested level
907 # complete on a nested level
908 with greedy_completion():
908 with greedy_completion():
909 ip.user_ns['d'] = numpy.zeros(2, dtype=dt)
909 ip.user_ns['d'] = numpy.zeros(2, dtype=dt)
910 _, matches = complete(line_buffer="d[1]['my_head']['")
910 _, matches = complete(line_buffer="d[1]['my_head']['")
911 nt.assert_true(any(["my_dt" in m for m in matches]))
911 nt.assert_true(any(["my_dt" in m for m in matches]))
912 nt.assert_true(any(["my_df" in m for m in matches]))
912 nt.assert_true(any(["my_df" in m for m in matches]))
913
913
914
914
915 @dec.skip_without('pandas')
915 @dec.skip_without('pandas')
916 def test_dataframe_key_completion():
916 def test_dataframe_key_completion():
917 """Test dict key completion applies to pandas DataFrames"""
917 """Test dict key completion applies to pandas DataFrames"""
918 import pandas
918 import pandas
919 ip = get_ipython()
919 ip = get_ipython()
920 complete = ip.Completer.complete
920 complete = ip.Completer.complete
921 ip.user_ns['d'] = pandas.DataFrame({'hello': [1], 'world': [2]})
921 ip.user_ns['d'] = pandas.DataFrame({'hello': [1], 'world': [2]})
922 _, matches = complete(line_buffer="d['")
922 _, matches = complete(line_buffer="d['")
923 nt.assert_in("hello", matches)
923 nt.assert_in("hello", matches)
924 nt.assert_in("world", matches)
924 nt.assert_in("world", matches)
925
925
926
926
927 def test_dict_key_completion_invalids():
927 def test_dict_key_completion_invalids():
928 """Smoke test cases dict key completion can't handle"""
928 """Smoke test cases dict key completion can't handle"""
929 ip = get_ipython()
929 ip = get_ipython()
930 complete = ip.Completer.complete
930 complete = ip.Completer.complete
931
931
932 ip.user_ns['no_getitem'] = None
932 ip.user_ns['no_getitem'] = None
933 ip.user_ns['no_keys'] = []
933 ip.user_ns['no_keys'] = []
934 ip.user_ns['cant_call_keys'] = dict
934 ip.user_ns['cant_call_keys'] = dict
935 ip.user_ns['empty'] = {}
935 ip.user_ns['empty'] = {}
936 ip.user_ns['d'] = {'abc': 5}
936 ip.user_ns['d'] = {'abc': 5}
937
937
938 _, matches = complete(line_buffer="no_getitem['")
938 _, matches = complete(line_buffer="no_getitem['")
939 _, matches = complete(line_buffer="no_keys['")
939 _, matches = complete(line_buffer="no_keys['")
940 _, matches = complete(line_buffer="cant_call_keys['")
940 _, matches = complete(line_buffer="cant_call_keys['")
941 _, matches = complete(line_buffer="empty['")
941 _, matches = complete(line_buffer="empty['")
942 _, matches = complete(line_buffer="name_error['")
942 _, matches = complete(line_buffer="name_error['")
943 _, matches = complete(line_buffer="d['\\") # incomplete escape
943 _, matches = complete(line_buffer="d['\\") # incomplete escape
944
944
945 class KeyCompletable(object):
945 class KeyCompletable(object):
946 def __init__(self, things=()):
946 def __init__(self, things=()):
947 self.things = things
947 self.things = things
948
948
949 def _ipython_key_completions_(self):
949 def _ipython_key_completions_(self):
950 return list(self.things)
950 return list(self.things)
951
951
952 def test_object_key_completion():
952 def test_object_key_completion():
953 ip = get_ipython()
953 ip = get_ipython()
954 ip.user_ns['key_completable'] = KeyCompletable(['qwerty', 'qwick'])
954 ip.user_ns['key_completable'] = KeyCompletable(['qwerty', 'qwick'])
955
955
956 _, matches = ip.Completer.complete(line_buffer="key_completable['qw")
956 _, matches = ip.Completer.complete(line_buffer="key_completable['qw")
957 nt.assert_in('qwerty', matches)
957 nt.assert_in('qwerty', matches)
958 nt.assert_in('qwick', matches)
958 nt.assert_in('qwick', matches)
959
959
960
960
961 class NamedInstanceMetaclass(type):
961 class NamedInstanceMetaclass(type):
962 def __getitem__(cls, item):
962 def __getitem__(cls, item):
963 return cls.get_instance(item)
963 return cls.get_instance(item)
964
964
965 class NamedInstanceClass(object, metaclass=NamedInstanceMetaclass):
965 class NamedInstanceClass(object, metaclass=NamedInstanceMetaclass):
966 def __init__(self, name):
966 def __init__(self, name):
967 if not hasattr(self.__class__, 'instances'):
967 if not hasattr(self.__class__, 'instances'):
968 self.__class__.instances = {}
968 self.__class__.instances = {}
969 self.__class__.instances[name] = self
969 self.__class__.instances[name] = self
970
970
971 @classmethod
971 @classmethod
972 def _ipython_key_completions_(cls):
972 def _ipython_key_completions_(cls):
973 return cls.instances.keys()
973 return cls.instances.keys()
974
974
975 @classmethod
975 @classmethod
976 def get_instance(cls, name):
976 def get_instance(cls, name):
977 return cls.instances[name]
977 return cls.instances[name]
978
978
979 def test_class_key_completion():
979 def test_class_key_completion():
980 ip = get_ipython()
980 ip = get_ipython()
981 NamedInstanceClass('qwerty')
981 NamedInstanceClass('qwerty')
982 NamedInstanceClass('qwick')
982 NamedInstanceClass('qwick')
983 ip.user_ns['named_instance_class'] = NamedInstanceClass
983 ip.user_ns['named_instance_class'] = NamedInstanceClass
984
984
985 _, matches = ip.Completer.complete(line_buffer="named_instance_class['qw")
985 _, matches = ip.Completer.complete(line_buffer="named_instance_class['qw")
986 nt.assert_in('qwerty', matches)
986 nt.assert_in('qwerty', matches)
987 nt.assert_in('qwick', matches)
987 nt.assert_in('qwick', matches)
988
988
989 def test_tryimport():
989 def test_tryimport():
990 """
990 """
991 Test that try-import don't crash on trailing dot, and import modules before
991 Test that try-import don't crash on trailing dot, and import modules before
992 """
992 """
993 from IPython.core.completerlib import try_import
993 from IPython.core.completerlib import try_import
994 assert(try_import("IPython."))
994 assert(try_import("IPython."))
995
995
996
996
997 def test_aimport_module_completer():
997 def test_aimport_module_completer():
998 ip = get_ipython()
998 ip = get_ipython()
999 _, matches = ip.complete('i', '%aimport i')
999 _, matches = ip.complete('i', '%aimport i')
1000 nt.assert_in('io', matches)
1000 nt.assert_in('io', matches)
1001 nt.assert_not_in('int', matches)
1001 nt.assert_not_in('int', matches)
1002
1002
1003 def test_nested_import_module_completer():
1003 def test_nested_import_module_completer():
1004 ip = get_ipython()
1004 ip = get_ipython()
1005 _, matches = ip.complete(None, 'import IPython.co', 17)
1005 _, matches = ip.complete(None, 'import IPython.co', 17)
1006 nt.assert_in('IPython.core', matches)
1006 nt.assert_in('IPython.core', matches)
1007 nt.assert_not_in('import IPython.core', matches)
1007 nt.assert_not_in('import IPython.core', matches)
1008 nt.assert_not_in('IPython.display', matches)
1008 nt.assert_not_in('IPython.display', matches)
1009
1009
1010 def test_import_module_completer():
1010 def test_import_module_completer():
1011 ip = get_ipython()
1011 ip = get_ipython()
1012 _, matches = ip.complete('i', 'import i')
1012 _, matches = ip.complete('i', 'import i')
1013 nt.assert_in('io', matches)
1013 nt.assert_in('io', matches)
1014 nt.assert_not_in('int', matches)
1014 nt.assert_not_in('int', matches)
1015
1015
1016 def test_from_module_completer():
1016 def test_from_module_completer():
1017 ip = get_ipython()
1017 ip = get_ipython()
1018 _, matches = ip.complete('B', 'from io import B', 16)
1018 _, matches = ip.complete('B', 'from io import B', 16)
1019 nt.assert_in('BytesIO', matches)
1019 nt.assert_in('BytesIO', matches)
1020 nt.assert_not_in('BaseException', matches)
1020 nt.assert_not_in('BaseException', matches)
1021
1021
1022 def test_snake_case_completion():
1022 def test_snake_case_completion():
1023 ip = get_ipython()
1023 ip = get_ipython()
1024 ip.Completer.use_jedi = False
1024 ip.Completer.use_jedi = False
1025 ip.user_ns['some_three'] = 3
1025 ip.user_ns['some_three'] = 3
1026 ip.user_ns['some_four'] = 4
1026 ip.user_ns['some_four'] = 4
1027 _, matches = ip.complete("s_", "print(s_f")
1027 _, matches = ip.complete("s_", "print(s_f")
1028 nt.assert_in('some_three', matches)
1028 nt.assert_in('some_three', matches)
1029 nt.assert_in('some_four', matches)
1029 nt.assert_in('some_four', matches)
@@ -1,5 +1,5 b''
1 """
1 """
2 This package contains all third-party modules bundled with IPython.
2 This package contains all third-party modules bundled with IPython.
3 """
3 """
4
4
5 __all__ = ["simplegeneric"]
5 __all__ = []
@@ -1,34 +1,30 b''
1 # encoding: utf-8
1 # encoding: utf-8
2 """Generic functions for extending IPython.
2 """Generic functions for extending IPython.
3
4 See http://pypi.python.org/pypi/simplegeneric.
5 """
3 """
6
4
7 from IPython.core.error import TryNext
5 from IPython.core.error import TryNext
8 from simplegeneric import generic
6 from functools import singledispatch
9
7
10
8
11 @generic
9 @singledispatch
12 def inspect_object(obj):
10 def inspect_object(obj):
13 """Called when you do obj?"""
11 """Called when you do obj?"""
14 raise TryNext
12 raise TryNext
15
13
16
14
17 @generic
15 @singledispatch
18 def complete_object(obj, prev_completions):
16 def complete_object(obj, prev_completions):
19 """Custom completer dispatching for python objects.
17 """Custom completer dispatching for python objects.
20
18
21 Parameters
19 Parameters
22 ----------
20 ----------
23 obj : object
21 obj : object
24 The object to complete.
22 The object to complete.
25 prev_completions : list
23 prev_completions : list
26 List of attributes discovered so far.
24 List of attributes discovered so far.
27
25
28 This should return the list of attributes in obj. If you only wish to
26 This should return the list of attributes in obj. If you only wish to
29 add to the attributes already discovered normally, return
27 add to the attributes already discovered normally, return
30 own_attrs + prev_completions.
28 own_attrs + prev_completions.
31 """
29 """
32 raise TryNext
30 raise TryNext
33
34
@@ -1,772 +1,772 b''
1 # encoding: utf-8
1 # encoding: utf-8
2 """
2 """
3 Utilities for working with strings and text.
3 Utilities for working with strings and text.
4
4
5 Inheritance diagram:
5 Inheritance diagram:
6
6
7 .. inheritance-diagram:: IPython.utils.text
7 .. inheritance-diagram:: IPython.utils.text
8 :parts: 3
8 :parts: 3
9 """
9 """
10
10
11 import os
11 import os
12 import re
12 import re
13 import sys
13 import sys
14 import textwrap
14 import textwrap
15 from string import Formatter
15 from string import Formatter
16 from pathlib import Path
16 from pathlib import Path
17
17
18 from IPython.utils import py3compat
18 from IPython.utils import py3compat
19
19
20 # datetime.strftime date format for ipython
20 # datetime.strftime date format for ipython
21 if sys.platform == 'win32':
21 if sys.platform == 'win32':
22 date_format = "%B %d, %Y"
22 date_format = "%B %d, %Y"
23 else:
23 else:
24 date_format = "%B %-d, %Y"
24 date_format = "%B %-d, %Y"
25
25
26 class LSString(str):
26 class LSString(str):
27 """String derivative with a special access attributes.
27 """String derivative with a special access attributes.
28
28
29 These are normal strings, but with the special attributes:
29 These are normal strings, but with the special attributes:
30
30
31 .l (or .list) : value as list (split on newlines).
31 .l (or .list) : value as list (split on newlines).
32 .n (or .nlstr): original value (the string itself).
32 .n (or .nlstr): original value (the string itself).
33 .s (or .spstr): value as whitespace-separated string.
33 .s (or .spstr): value as whitespace-separated string.
34 .p (or .paths): list of path objects (requires path.py package)
34 .p (or .paths): list of path objects (requires path.py package)
35
35
36 Any values which require transformations are computed only once and
36 Any values which require transformations are computed only once and
37 cached.
37 cached.
38
38
39 Such strings are very useful to efficiently interact with the shell, which
39 Such strings are very useful to efficiently interact with the shell, which
40 typically only understands whitespace-separated options for commands."""
40 typically only understands whitespace-separated options for commands."""
41
41
42 def get_list(self):
42 def get_list(self):
43 try:
43 try:
44 return self.__list
44 return self.__list
45 except AttributeError:
45 except AttributeError:
46 self.__list = self.split('\n')
46 self.__list = self.split('\n')
47 return self.__list
47 return self.__list
48
48
49 l = list = property(get_list)
49 l = list = property(get_list)
50
50
51 def get_spstr(self):
51 def get_spstr(self):
52 try:
52 try:
53 return self.__spstr
53 return self.__spstr
54 except AttributeError:
54 except AttributeError:
55 self.__spstr = self.replace('\n',' ')
55 self.__spstr = self.replace('\n',' ')
56 return self.__spstr
56 return self.__spstr
57
57
58 s = spstr = property(get_spstr)
58 s = spstr = property(get_spstr)
59
59
60 def get_nlstr(self):
60 def get_nlstr(self):
61 return self
61 return self
62
62
63 n = nlstr = property(get_nlstr)
63 n = nlstr = property(get_nlstr)
64
64
65 def get_paths(self):
65 def get_paths(self):
66 try:
66 try:
67 return self.__paths
67 return self.__paths
68 except AttributeError:
68 except AttributeError:
69 self.__paths = [Path(p) for p in self.split('\n') if os.path.exists(p)]
69 self.__paths = [Path(p) for p in self.split('\n') if os.path.exists(p)]
70 return self.__paths
70 return self.__paths
71
71
72 p = paths = property(get_paths)
72 p = paths = property(get_paths)
73
73
74 # FIXME: We need to reimplement type specific displayhook and then add this
74 # FIXME: We need to reimplement type specific displayhook and then add this
75 # back as a custom printer. This should also be moved outside utils into the
75 # back as a custom printer. This should also be moved outside utils into the
76 # core.
76 # core.
77
77
78 # def print_lsstring(arg):
78 # def print_lsstring(arg):
79 # """ Prettier (non-repr-like) and more informative printer for LSString """
79 # """ Prettier (non-repr-like) and more informative printer for LSString """
80 # print "LSString (.p, .n, .l, .s available). Value:"
80 # print "LSString (.p, .n, .l, .s available). Value:"
81 # print arg
81 # print arg
82 #
82 #
83 #
83 #
84 # print_lsstring = result_display.when_type(LSString)(print_lsstring)
84 # print_lsstring = result_display.register(LSString)(print_lsstring)
85
85
86
86
87 class SList(list):
87 class SList(list):
88 """List derivative with a special access attributes.
88 """List derivative with a special access attributes.
89
89
90 These are normal lists, but with the special attributes:
90 These are normal lists, but with the special attributes:
91
91
92 * .l (or .list) : value as list (the list itself).
92 * .l (or .list) : value as list (the list itself).
93 * .n (or .nlstr): value as a string, joined on newlines.
93 * .n (or .nlstr): value as a string, joined on newlines.
94 * .s (or .spstr): value as a string, joined on spaces.
94 * .s (or .spstr): value as a string, joined on spaces.
95 * .p (or .paths): list of path objects (requires path.py package)
95 * .p (or .paths): list of path objects (requires path.py package)
96
96
97 Any values which require transformations are computed only once and
97 Any values which require transformations are computed only once and
98 cached."""
98 cached."""
99
99
100 def get_list(self):
100 def get_list(self):
101 return self
101 return self
102
102
103 l = list = property(get_list)
103 l = list = property(get_list)
104
104
105 def get_spstr(self):
105 def get_spstr(self):
106 try:
106 try:
107 return self.__spstr
107 return self.__spstr
108 except AttributeError:
108 except AttributeError:
109 self.__spstr = ' '.join(self)
109 self.__spstr = ' '.join(self)
110 return self.__spstr
110 return self.__spstr
111
111
112 s = spstr = property(get_spstr)
112 s = spstr = property(get_spstr)
113
113
114 def get_nlstr(self):
114 def get_nlstr(self):
115 try:
115 try:
116 return self.__nlstr
116 return self.__nlstr
117 except AttributeError:
117 except AttributeError:
118 self.__nlstr = '\n'.join(self)
118 self.__nlstr = '\n'.join(self)
119 return self.__nlstr
119 return self.__nlstr
120
120
121 n = nlstr = property(get_nlstr)
121 n = nlstr = property(get_nlstr)
122
122
123 def get_paths(self):
123 def get_paths(self):
124 try:
124 try:
125 return self.__paths
125 return self.__paths
126 except AttributeError:
126 except AttributeError:
127 self.__paths = [Path(p) for p in self if os.path.exists(p)]
127 self.__paths = [Path(p) for p in self if os.path.exists(p)]
128 return self.__paths
128 return self.__paths
129
129
130 p = paths = property(get_paths)
130 p = paths = property(get_paths)
131
131
132 def grep(self, pattern, prune = False, field = None):
132 def grep(self, pattern, prune = False, field = None):
133 """ Return all strings matching 'pattern' (a regex or callable)
133 """ Return all strings matching 'pattern' (a regex or callable)
134
134
135 This is case-insensitive. If prune is true, return all items
135 This is case-insensitive. If prune is true, return all items
136 NOT matching the pattern.
136 NOT matching the pattern.
137
137
138 If field is specified, the match must occur in the specified
138 If field is specified, the match must occur in the specified
139 whitespace-separated field.
139 whitespace-separated field.
140
140
141 Examples::
141 Examples::
142
142
143 a.grep( lambda x: x.startswith('C') )
143 a.grep( lambda x: x.startswith('C') )
144 a.grep('Cha.*log', prune=1)
144 a.grep('Cha.*log', prune=1)
145 a.grep('chm', field=-1)
145 a.grep('chm', field=-1)
146 """
146 """
147
147
148 def match_target(s):
148 def match_target(s):
149 if field is None:
149 if field is None:
150 return s
150 return s
151 parts = s.split()
151 parts = s.split()
152 try:
152 try:
153 tgt = parts[field]
153 tgt = parts[field]
154 return tgt
154 return tgt
155 except IndexError:
155 except IndexError:
156 return ""
156 return ""
157
157
158 if isinstance(pattern, str):
158 if isinstance(pattern, str):
159 pred = lambda x : re.search(pattern, x, re.IGNORECASE)
159 pred = lambda x : re.search(pattern, x, re.IGNORECASE)
160 else:
160 else:
161 pred = pattern
161 pred = pattern
162 if not prune:
162 if not prune:
163 return SList([el for el in self if pred(match_target(el))])
163 return SList([el for el in self if pred(match_target(el))])
164 else:
164 else:
165 return SList([el for el in self if not pred(match_target(el))])
165 return SList([el for el in self if not pred(match_target(el))])
166
166
167 def fields(self, *fields):
167 def fields(self, *fields):
168 """ Collect whitespace-separated fields from string list
168 """ Collect whitespace-separated fields from string list
169
169
170 Allows quick awk-like usage of string lists.
170 Allows quick awk-like usage of string lists.
171
171
172 Example data (in var a, created by 'a = !ls -l')::
172 Example data (in var a, created by 'a = !ls -l')::
173
173
174 -rwxrwxrwx 1 ville None 18 Dec 14 2006 ChangeLog
174 -rwxrwxrwx 1 ville None 18 Dec 14 2006 ChangeLog
175 drwxrwxrwx+ 6 ville None 0 Oct 24 18:05 IPython
175 drwxrwxrwx+ 6 ville None 0 Oct 24 18:05 IPython
176
176
177 * ``a.fields(0)`` is ``['-rwxrwxrwx', 'drwxrwxrwx+']``
177 * ``a.fields(0)`` is ``['-rwxrwxrwx', 'drwxrwxrwx+']``
178 * ``a.fields(1,0)`` is ``['1 -rwxrwxrwx', '6 drwxrwxrwx+']``
178 * ``a.fields(1,0)`` is ``['1 -rwxrwxrwx', '6 drwxrwxrwx+']``
179 (note the joining by space).
179 (note the joining by space).
180 * ``a.fields(-1)`` is ``['ChangeLog', 'IPython']``
180 * ``a.fields(-1)`` is ``['ChangeLog', 'IPython']``
181
181
182 IndexErrors are ignored.
182 IndexErrors are ignored.
183
183
184 Without args, fields() just split()'s the strings.
184 Without args, fields() just split()'s the strings.
185 """
185 """
186 if len(fields) == 0:
186 if len(fields) == 0:
187 return [el.split() for el in self]
187 return [el.split() for el in self]
188
188
189 res = SList()
189 res = SList()
190 for el in [f.split() for f in self]:
190 for el in [f.split() for f in self]:
191 lineparts = []
191 lineparts = []
192
192
193 for fd in fields:
193 for fd in fields:
194 try:
194 try:
195 lineparts.append(el[fd])
195 lineparts.append(el[fd])
196 except IndexError:
196 except IndexError:
197 pass
197 pass
198 if lineparts:
198 if lineparts:
199 res.append(" ".join(lineparts))
199 res.append(" ".join(lineparts))
200
200
201 return res
201 return res
202
202
203 def sort(self,field= None, nums = False):
203 def sort(self,field= None, nums = False):
204 """ sort by specified fields (see fields())
204 """ sort by specified fields (see fields())
205
205
206 Example::
206 Example::
207
207
208 a.sort(1, nums = True)
208 a.sort(1, nums = True)
209
209
210 Sorts a by second field, in numerical order (so that 21 > 3)
210 Sorts a by second field, in numerical order (so that 21 > 3)
211
211
212 """
212 """
213
213
214 #decorate, sort, undecorate
214 #decorate, sort, undecorate
215 if field is not None:
215 if field is not None:
216 dsu = [[SList([line]).fields(field), line] for line in self]
216 dsu = [[SList([line]).fields(field), line] for line in self]
217 else:
217 else:
218 dsu = [[line, line] for line in self]
218 dsu = [[line, line] for line in self]
219 if nums:
219 if nums:
220 for i in range(len(dsu)):
220 for i in range(len(dsu)):
221 numstr = "".join([ch for ch in dsu[i][0] if ch.isdigit()])
221 numstr = "".join([ch for ch in dsu[i][0] if ch.isdigit()])
222 try:
222 try:
223 n = int(numstr)
223 n = int(numstr)
224 except ValueError:
224 except ValueError:
225 n = 0
225 n = 0
226 dsu[i][0] = n
226 dsu[i][0] = n
227
227
228
228
229 dsu.sort()
229 dsu.sort()
230 return SList([t[1] for t in dsu])
230 return SList([t[1] for t in dsu])
231
231
232
232
233 # FIXME: We need to reimplement type specific displayhook and then add this
233 # FIXME: We need to reimplement type specific displayhook and then add this
234 # back as a custom printer. This should also be moved outside utils into the
234 # back as a custom printer. This should also be moved outside utils into the
235 # core.
235 # core.
236
236
237 # def print_slist(arg):
237 # def print_slist(arg):
238 # """ Prettier (non-repr-like) and more informative printer for SList """
238 # """ Prettier (non-repr-like) and more informative printer for SList """
239 # print "SList (.p, .n, .l, .s, .grep(), .fields(), sort() available):"
239 # print "SList (.p, .n, .l, .s, .grep(), .fields(), sort() available):"
240 # if hasattr(arg, 'hideonce') and arg.hideonce:
240 # if hasattr(arg, 'hideonce') and arg.hideonce:
241 # arg.hideonce = False
241 # arg.hideonce = False
242 # return
242 # return
243 #
243 #
244 # nlprint(arg) # This was a nested list printer, now removed.
244 # nlprint(arg) # This was a nested list printer, now removed.
245 #
245 #
246 # print_slist = result_display.when_type(SList)(print_slist)
246 # print_slist = result_display.register(SList)(print_slist)
247
247
248
248
249 def indent(instr,nspaces=4, ntabs=0, flatten=False):
249 def indent(instr,nspaces=4, ntabs=0, flatten=False):
250 """Indent a string a given number of spaces or tabstops.
250 """Indent a string a given number of spaces or tabstops.
251
251
252 indent(str,nspaces=4,ntabs=0) -> indent str by ntabs+nspaces.
252 indent(str,nspaces=4,ntabs=0) -> indent str by ntabs+nspaces.
253
253
254 Parameters
254 Parameters
255 ----------
255 ----------
256
256
257 instr : basestring
257 instr : basestring
258 The string to be indented.
258 The string to be indented.
259 nspaces : int (default: 4)
259 nspaces : int (default: 4)
260 The number of spaces to be indented.
260 The number of spaces to be indented.
261 ntabs : int (default: 0)
261 ntabs : int (default: 0)
262 The number of tabs to be indented.
262 The number of tabs to be indented.
263 flatten : bool (default: False)
263 flatten : bool (default: False)
264 Whether to scrub existing indentation. If True, all lines will be
264 Whether to scrub existing indentation. If True, all lines will be
265 aligned to the same indentation. If False, existing indentation will
265 aligned to the same indentation. If False, existing indentation will
266 be strictly increased.
266 be strictly increased.
267
267
268 Returns
268 Returns
269 -------
269 -------
270
270
271 str|unicode : string indented by ntabs and nspaces.
271 str|unicode : string indented by ntabs and nspaces.
272
272
273 """
273 """
274 if instr is None:
274 if instr is None:
275 return
275 return
276 ind = '\t'*ntabs+' '*nspaces
276 ind = '\t'*ntabs+' '*nspaces
277 if flatten:
277 if flatten:
278 pat = re.compile(r'^\s*', re.MULTILINE)
278 pat = re.compile(r'^\s*', re.MULTILINE)
279 else:
279 else:
280 pat = re.compile(r'^', re.MULTILINE)
280 pat = re.compile(r'^', re.MULTILINE)
281 outstr = re.sub(pat, ind, instr)
281 outstr = re.sub(pat, ind, instr)
282 if outstr.endswith(os.linesep+ind):
282 if outstr.endswith(os.linesep+ind):
283 return outstr[:-len(ind)]
283 return outstr[:-len(ind)]
284 else:
284 else:
285 return outstr
285 return outstr
286
286
287
287
288 def list_strings(arg):
288 def list_strings(arg):
289 """Always return a list of strings, given a string or list of strings
289 """Always return a list of strings, given a string or list of strings
290 as input.
290 as input.
291
291
292 Examples
292 Examples
293 --------
293 --------
294 ::
294 ::
295
295
296 In [7]: list_strings('A single string')
296 In [7]: list_strings('A single string')
297 Out[7]: ['A single string']
297 Out[7]: ['A single string']
298
298
299 In [8]: list_strings(['A single string in a list'])
299 In [8]: list_strings(['A single string in a list'])
300 Out[8]: ['A single string in a list']
300 Out[8]: ['A single string in a list']
301
301
302 In [9]: list_strings(['A','list','of','strings'])
302 In [9]: list_strings(['A','list','of','strings'])
303 Out[9]: ['A', 'list', 'of', 'strings']
303 Out[9]: ['A', 'list', 'of', 'strings']
304 """
304 """
305
305
306 if isinstance(arg, str):
306 if isinstance(arg, str):
307 return [arg]
307 return [arg]
308 else:
308 else:
309 return arg
309 return arg
310
310
311
311
312 def marquee(txt='',width=78,mark='*'):
312 def marquee(txt='',width=78,mark='*'):
313 """Return the input string centered in a 'marquee'.
313 """Return the input string centered in a 'marquee'.
314
314
315 Examples
315 Examples
316 --------
316 --------
317 ::
317 ::
318
318
319 In [16]: marquee('A test',40)
319 In [16]: marquee('A test',40)
320 Out[16]: '**************** A test ****************'
320 Out[16]: '**************** A test ****************'
321
321
322 In [17]: marquee('A test',40,'-')
322 In [17]: marquee('A test',40,'-')
323 Out[17]: '---------------- A test ----------------'
323 Out[17]: '---------------- A test ----------------'
324
324
325 In [18]: marquee('A test',40,' ')
325 In [18]: marquee('A test',40,' ')
326 Out[18]: ' A test '
326 Out[18]: ' A test '
327
327
328 """
328 """
329 if not txt:
329 if not txt:
330 return (mark*width)[:width]
330 return (mark*width)[:width]
331 nmark = (width-len(txt)-2)//len(mark)//2
331 nmark = (width-len(txt)-2)//len(mark)//2
332 if nmark < 0: nmark =0
332 if nmark < 0: nmark =0
333 marks = mark*nmark
333 marks = mark*nmark
334 return '%s %s %s' % (marks,txt,marks)
334 return '%s %s %s' % (marks,txt,marks)
335
335
336
336
337 ini_spaces_re = re.compile(r'^(\s+)')
337 ini_spaces_re = re.compile(r'^(\s+)')
338
338
339 def num_ini_spaces(strng):
339 def num_ini_spaces(strng):
340 """Return the number of initial spaces in a string"""
340 """Return the number of initial spaces in a string"""
341
341
342 ini_spaces = ini_spaces_re.match(strng)
342 ini_spaces = ini_spaces_re.match(strng)
343 if ini_spaces:
343 if ini_spaces:
344 return ini_spaces.end()
344 return ini_spaces.end()
345 else:
345 else:
346 return 0
346 return 0
347
347
348
348
349 def format_screen(strng):
349 def format_screen(strng):
350 """Format a string for screen printing.
350 """Format a string for screen printing.
351
351
352 This removes some latex-type format codes."""
352 This removes some latex-type format codes."""
353 # Paragraph continue
353 # Paragraph continue
354 par_re = re.compile(r'\\$',re.MULTILINE)
354 par_re = re.compile(r'\\$',re.MULTILINE)
355 strng = par_re.sub('',strng)
355 strng = par_re.sub('',strng)
356 return strng
356 return strng
357
357
358
358
359 def dedent(text):
359 def dedent(text):
360 """Equivalent of textwrap.dedent that ignores unindented first line.
360 """Equivalent of textwrap.dedent that ignores unindented first line.
361
361
362 This means it will still dedent strings like:
362 This means it will still dedent strings like:
363 '''foo
363 '''foo
364 is a bar
364 is a bar
365 '''
365 '''
366
366
367 For use in wrap_paragraphs.
367 For use in wrap_paragraphs.
368 """
368 """
369
369
370 if text.startswith('\n'):
370 if text.startswith('\n'):
371 # text starts with blank line, don't ignore the first line
371 # text starts with blank line, don't ignore the first line
372 return textwrap.dedent(text)
372 return textwrap.dedent(text)
373
373
374 # split first line
374 # split first line
375 splits = text.split('\n',1)
375 splits = text.split('\n',1)
376 if len(splits) == 1:
376 if len(splits) == 1:
377 # only one line
377 # only one line
378 return textwrap.dedent(text)
378 return textwrap.dedent(text)
379
379
380 first, rest = splits
380 first, rest = splits
381 # dedent everything but the first line
381 # dedent everything but the first line
382 rest = textwrap.dedent(rest)
382 rest = textwrap.dedent(rest)
383 return '\n'.join([first, rest])
383 return '\n'.join([first, rest])
384
384
385
385
386 def wrap_paragraphs(text, ncols=80):
386 def wrap_paragraphs(text, ncols=80):
387 """Wrap multiple paragraphs to fit a specified width.
387 """Wrap multiple paragraphs to fit a specified width.
388
388
389 This is equivalent to textwrap.wrap, but with support for multiple
389 This is equivalent to textwrap.wrap, but with support for multiple
390 paragraphs, as separated by empty lines.
390 paragraphs, as separated by empty lines.
391
391
392 Returns
392 Returns
393 -------
393 -------
394
394
395 list of complete paragraphs, wrapped to fill `ncols` columns.
395 list of complete paragraphs, wrapped to fill `ncols` columns.
396 """
396 """
397 paragraph_re = re.compile(r'\n(\s*\n)+', re.MULTILINE)
397 paragraph_re = re.compile(r'\n(\s*\n)+', re.MULTILINE)
398 text = dedent(text).strip()
398 text = dedent(text).strip()
399 paragraphs = paragraph_re.split(text)[::2] # every other entry is space
399 paragraphs = paragraph_re.split(text)[::2] # every other entry is space
400 out_ps = []
400 out_ps = []
401 indent_re = re.compile(r'\n\s+', re.MULTILINE)
401 indent_re = re.compile(r'\n\s+', re.MULTILINE)
402 for p in paragraphs:
402 for p in paragraphs:
403 # presume indentation that survives dedent is meaningful formatting,
403 # presume indentation that survives dedent is meaningful formatting,
404 # so don't fill unless text is flush.
404 # so don't fill unless text is flush.
405 if indent_re.search(p) is None:
405 if indent_re.search(p) is None:
406 # wrap paragraph
406 # wrap paragraph
407 p = textwrap.fill(p, ncols)
407 p = textwrap.fill(p, ncols)
408 out_ps.append(p)
408 out_ps.append(p)
409 return out_ps
409 return out_ps
410
410
411
411
412 def long_substr(data):
412 def long_substr(data):
413 """Return the longest common substring in a list of strings.
413 """Return the longest common substring in a list of strings.
414
414
415 Credit: http://stackoverflow.com/questions/2892931/longest-common-substring-from-more-than-two-strings-python
415 Credit: http://stackoverflow.com/questions/2892931/longest-common-substring-from-more-than-two-strings-python
416 """
416 """
417 substr = ''
417 substr = ''
418 if len(data) > 1 and len(data[0]) > 0:
418 if len(data) > 1 and len(data[0]) > 0:
419 for i in range(len(data[0])):
419 for i in range(len(data[0])):
420 for j in range(len(data[0])-i+1):
420 for j in range(len(data[0])-i+1):
421 if j > len(substr) and all(data[0][i:i+j] in x for x in data):
421 if j > len(substr) and all(data[0][i:i+j] in x for x in data):
422 substr = data[0][i:i+j]
422 substr = data[0][i:i+j]
423 elif len(data) == 1:
423 elif len(data) == 1:
424 substr = data[0]
424 substr = data[0]
425 return substr
425 return substr
426
426
427
427
428 def strip_email_quotes(text):
428 def strip_email_quotes(text):
429 """Strip leading email quotation characters ('>').
429 """Strip leading email quotation characters ('>').
430
430
431 Removes any combination of leading '>' interspersed with whitespace that
431 Removes any combination of leading '>' interspersed with whitespace that
432 appears *identically* in all lines of the input text.
432 appears *identically* in all lines of the input text.
433
433
434 Parameters
434 Parameters
435 ----------
435 ----------
436 text : str
436 text : str
437
437
438 Examples
438 Examples
439 --------
439 --------
440
440
441 Simple uses::
441 Simple uses::
442
442
443 In [2]: strip_email_quotes('> > text')
443 In [2]: strip_email_quotes('> > text')
444 Out[2]: 'text'
444 Out[2]: 'text'
445
445
446 In [3]: strip_email_quotes('> > text\\n> > more')
446 In [3]: strip_email_quotes('> > text\\n> > more')
447 Out[3]: 'text\\nmore'
447 Out[3]: 'text\\nmore'
448
448
449 Note how only the common prefix that appears in all lines is stripped::
449 Note how only the common prefix that appears in all lines is stripped::
450
450
451 In [4]: strip_email_quotes('> > text\\n> > more\\n> more...')
451 In [4]: strip_email_quotes('> > text\\n> > more\\n> more...')
452 Out[4]: '> text\\n> more\\nmore...'
452 Out[4]: '> text\\n> more\\nmore...'
453
453
454 So if any line has no quote marks ('>') , then none are stripped from any
454 So if any line has no quote marks ('>') , then none are stripped from any
455 of them ::
455 of them ::
456
456
457 In [5]: strip_email_quotes('> > text\\n> > more\\nlast different')
457 In [5]: strip_email_quotes('> > text\\n> > more\\nlast different')
458 Out[5]: '> > text\\n> > more\\nlast different'
458 Out[5]: '> > text\\n> > more\\nlast different'
459 """
459 """
460 lines = text.splitlines()
460 lines = text.splitlines()
461 matches = set()
461 matches = set()
462 for line in lines:
462 for line in lines:
463 prefix = re.match(r'^(\s*>[ >]*)', line)
463 prefix = re.match(r'^(\s*>[ >]*)', line)
464 if prefix:
464 if prefix:
465 matches.add(prefix.group(1))
465 matches.add(prefix.group(1))
466 else:
466 else:
467 break
467 break
468 else:
468 else:
469 prefix = long_substr(list(matches))
469 prefix = long_substr(list(matches))
470 if prefix:
470 if prefix:
471 strip = len(prefix)
471 strip = len(prefix)
472 text = '\n'.join([ ln[strip:] for ln in lines])
472 text = '\n'.join([ ln[strip:] for ln in lines])
473 return text
473 return text
474
474
475 def strip_ansi(source):
475 def strip_ansi(source):
476 """
476 """
477 Remove ansi escape codes from text.
477 Remove ansi escape codes from text.
478
478
479 Parameters
479 Parameters
480 ----------
480 ----------
481 source : str
481 source : str
482 Source to remove the ansi from
482 Source to remove the ansi from
483 """
483 """
484 return re.sub(r'\033\[(\d|;)+?m', '', source)
484 return re.sub(r'\033\[(\d|;)+?m', '', source)
485
485
486
486
487 class EvalFormatter(Formatter):
487 class EvalFormatter(Formatter):
488 """A String Formatter that allows evaluation of simple expressions.
488 """A String Formatter that allows evaluation of simple expressions.
489
489
490 Note that this version interprets a : as specifying a format string (as per
490 Note that this version interprets a : as specifying a format string (as per
491 standard string formatting), so if slicing is required, you must explicitly
491 standard string formatting), so if slicing is required, you must explicitly
492 create a slice.
492 create a slice.
493
493
494 This is to be used in templating cases, such as the parallel batch
494 This is to be used in templating cases, such as the parallel batch
495 script templates, where simple arithmetic on arguments is useful.
495 script templates, where simple arithmetic on arguments is useful.
496
496
497 Examples
497 Examples
498 --------
498 --------
499 ::
499 ::
500
500
501 In [1]: f = EvalFormatter()
501 In [1]: f = EvalFormatter()
502 In [2]: f.format('{n//4}', n=8)
502 In [2]: f.format('{n//4}', n=8)
503 Out[2]: '2'
503 Out[2]: '2'
504
504
505 In [3]: f.format("{greeting[slice(2,4)]}", greeting="Hello")
505 In [3]: f.format("{greeting[slice(2,4)]}", greeting="Hello")
506 Out[3]: 'll'
506 Out[3]: 'll'
507 """
507 """
508 def get_field(self, name, args, kwargs):
508 def get_field(self, name, args, kwargs):
509 v = eval(name, kwargs)
509 v = eval(name, kwargs)
510 return v, name
510 return v, name
511
511
512 #XXX: As of Python 3.4, the format string parsing no longer splits on a colon
512 #XXX: As of Python 3.4, the format string parsing no longer splits on a colon
513 # inside [], so EvalFormatter can handle slicing. Once we only support 3.4 and
513 # inside [], so EvalFormatter can handle slicing. Once we only support 3.4 and
514 # above, it should be possible to remove FullEvalFormatter.
514 # above, it should be possible to remove FullEvalFormatter.
515
515
516 class FullEvalFormatter(Formatter):
516 class FullEvalFormatter(Formatter):
517 """A String Formatter that allows evaluation of simple expressions.
517 """A String Formatter that allows evaluation of simple expressions.
518
518
519 Any time a format key is not found in the kwargs,
519 Any time a format key is not found in the kwargs,
520 it will be tried as an expression in the kwargs namespace.
520 it will be tried as an expression in the kwargs namespace.
521
521
522 Note that this version allows slicing using [1:2], so you cannot specify
522 Note that this version allows slicing using [1:2], so you cannot specify
523 a format string. Use :class:`EvalFormatter` to permit format strings.
523 a format string. Use :class:`EvalFormatter` to permit format strings.
524
524
525 Examples
525 Examples
526 --------
526 --------
527 ::
527 ::
528
528
529 In [1]: f = FullEvalFormatter()
529 In [1]: f = FullEvalFormatter()
530 In [2]: f.format('{n//4}', n=8)
530 In [2]: f.format('{n//4}', n=8)
531 Out[2]: '2'
531 Out[2]: '2'
532
532
533 In [3]: f.format('{list(range(5))[2:4]}')
533 In [3]: f.format('{list(range(5))[2:4]}')
534 Out[3]: '[2, 3]'
534 Out[3]: '[2, 3]'
535
535
536 In [4]: f.format('{3*2}')
536 In [4]: f.format('{3*2}')
537 Out[4]: '6'
537 Out[4]: '6'
538 """
538 """
539 # copied from Formatter._vformat with minor changes to allow eval
539 # copied from Formatter._vformat with minor changes to allow eval
540 # and replace the format_spec code with slicing
540 # and replace the format_spec code with slicing
541 def vformat(self, format_string, args, kwargs):
541 def vformat(self, format_string, args, kwargs):
542 result = []
542 result = []
543 for literal_text, field_name, format_spec, conversion in \
543 for literal_text, field_name, format_spec, conversion in \
544 self.parse(format_string):
544 self.parse(format_string):
545
545
546 # output the literal text
546 # output the literal text
547 if literal_text:
547 if literal_text:
548 result.append(literal_text)
548 result.append(literal_text)
549
549
550 # if there's a field, output it
550 # if there's a field, output it
551 if field_name is not None:
551 if field_name is not None:
552 # this is some markup, find the object and do
552 # this is some markup, find the object and do
553 # the formatting
553 # the formatting
554
554
555 if format_spec:
555 if format_spec:
556 # override format spec, to allow slicing:
556 # override format spec, to allow slicing:
557 field_name = ':'.join([field_name, format_spec])
557 field_name = ':'.join([field_name, format_spec])
558
558
559 # eval the contents of the field for the object
559 # eval the contents of the field for the object
560 # to be formatted
560 # to be formatted
561 obj = eval(field_name, kwargs)
561 obj = eval(field_name, kwargs)
562
562
563 # do any conversion on the resulting object
563 # do any conversion on the resulting object
564 obj = self.convert_field(obj, conversion)
564 obj = self.convert_field(obj, conversion)
565
565
566 # format the object and append to the result
566 # format the object and append to the result
567 result.append(self.format_field(obj, ''))
567 result.append(self.format_field(obj, ''))
568
568
569 return ''.join(py3compat.cast_unicode(s) for s in result)
569 return ''.join(py3compat.cast_unicode(s) for s in result)
570
570
571
571
572 class DollarFormatter(FullEvalFormatter):
572 class DollarFormatter(FullEvalFormatter):
573 """Formatter allowing Itpl style $foo replacement, for names and attribute
573 """Formatter allowing Itpl style $foo replacement, for names and attribute
574 access only. Standard {foo} replacement also works, and allows full
574 access only. Standard {foo} replacement also works, and allows full
575 evaluation of its arguments.
575 evaluation of its arguments.
576
576
577 Examples
577 Examples
578 --------
578 --------
579 ::
579 ::
580
580
581 In [1]: f = DollarFormatter()
581 In [1]: f = DollarFormatter()
582 In [2]: f.format('{n//4}', n=8)
582 In [2]: f.format('{n//4}', n=8)
583 Out[2]: '2'
583 Out[2]: '2'
584
584
585 In [3]: f.format('23 * 76 is $result', result=23*76)
585 In [3]: f.format('23 * 76 is $result', result=23*76)
586 Out[3]: '23 * 76 is 1748'
586 Out[3]: '23 * 76 is 1748'
587
587
588 In [4]: f.format('$a or {b}', a=1, b=2)
588 In [4]: f.format('$a or {b}', a=1, b=2)
589 Out[4]: '1 or 2'
589 Out[4]: '1 or 2'
590 """
590 """
591 _dollar_pattern_ignore_single_quote = re.compile(r"(.*?)\$(\$?[\w\.]+)(?=([^']*'[^']*')*[^']*$)")
591 _dollar_pattern_ignore_single_quote = re.compile(r"(.*?)\$(\$?[\w\.]+)(?=([^']*'[^']*')*[^']*$)")
592 def parse(self, fmt_string):
592 def parse(self, fmt_string):
593 for literal_txt, field_name, format_spec, conversion \
593 for literal_txt, field_name, format_spec, conversion \
594 in Formatter.parse(self, fmt_string):
594 in Formatter.parse(self, fmt_string):
595
595
596 # Find $foo patterns in the literal text.
596 # Find $foo patterns in the literal text.
597 continue_from = 0
597 continue_from = 0
598 txt = ""
598 txt = ""
599 for m in self._dollar_pattern_ignore_single_quote.finditer(literal_txt):
599 for m in self._dollar_pattern_ignore_single_quote.finditer(literal_txt):
600 new_txt, new_field = m.group(1,2)
600 new_txt, new_field = m.group(1,2)
601 # $$foo --> $foo
601 # $$foo --> $foo
602 if new_field.startswith("$"):
602 if new_field.startswith("$"):
603 txt += new_txt + new_field
603 txt += new_txt + new_field
604 else:
604 else:
605 yield (txt + new_txt, new_field, "", None)
605 yield (txt + new_txt, new_field, "", None)
606 txt = ""
606 txt = ""
607 continue_from = m.end()
607 continue_from = m.end()
608
608
609 # Re-yield the {foo} style pattern
609 # Re-yield the {foo} style pattern
610 yield (txt + literal_txt[continue_from:], field_name, format_spec, conversion)
610 yield (txt + literal_txt[continue_from:], field_name, format_spec, conversion)
611
611
612 #-----------------------------------------------------------------------------
612 #-----------------------------------------------------------------------------
613 # Utils to columnize a list of string
613 # Utils to columnize a list of string
614 #-----------------------------------------------------------------------------
614 #-----------------------------------------------------------------------------
615
615
616 def _col_chunks(l, max_rows, row_first=False):
616 def _col_chunks(l, max_rows, row_first=False):
617 """Yield successive max_rows-sized column chunks from l."""
617 """Yield successive max_rows-sized column chunks from l."""
618 if row_first:
618 if row_first:
619 ncols = (len(l) // max_rows) + (len(l) % max_rows > 0)
619 ncols = (len(l) // max_rows) + (len(l) % max_rows > 0)
620 for i in range(ncols):
620 for i in range(ncols):
621 yield [l[j] for j in range(i, len(l), ncols)]
621 yield [l[j] for j in range(i, len(l), ncols)]
622 else:
622 else:
623 for i in range(0, len(l), max_rows):
623 for i in range(0, len(l), max_rows):
624 yield l[i:(i + max_rows)]
624 yield l[i:(i + max_rows)]
625
625
626
626
627 def _find_optimal(rlist, row_first=False, separator_size=2, displaywidth=80):
627 def _find_optimal(rlist, row_first=False, separator_size=2, displaywidth=80):
628 """Calculate optimal info to columnize a list of string"""
628 """Calculate optimal info to columnize a list of string"""
629 for max_rows in range(1, len(rlist) + 1):
629 for max_rows in range(1, len(rlist) + 1):
630 col_widths = list(map(max, _col_chunks(rlist, max_rows, row_first)))
630 col_widths = list(map(max, _col_chunks(rlist, max_rows, row_first)))
631 sumlength = sum(col_widths)
631 sumlength = sum(col_widths)
632 ncols = len(col_widths)
632 ncols = len(col_widths)
633 if sumlength + separator_size * (ncols - 1) <= displaywidth:
633 if sumlength + separator_size * (ncols - 1) <= displaywidth:
634 break
634 break
635 return {'num_columns': ncols,
635 return {'num_columns': ncols,
636 'optimal_separator_width': (displaywidth - sumlength) // (ncols - 1) if (ncols - 1) else 0,
636 'optimal_separator_width': (displaywidth - sumlength) // (ncols - 1) if (ncols - 1) else 0,
637 'max_rows': max_rows,
637 'max_rows': max_rows,
638 'column_widths': col_widths
638 'column_widths': col_widths
639 }
639 }
640
640
641
641
642 def _get_or_default(mylist, i, default=None):
642 def _get_or_default(mylist, i, default=None):
643 """return list item number, or default if don't exist"""
643 """return list item number, or default if don't exist"""
644 if i >= len(mylist):
644 if i >= len(mylist):
645 return default
645 return default
646 else :
646 else :
647 return mylist[i]
647 return mylist[i]
648
648
649
649
650 def compute_item_matrix(items, row_first=False, empty=None, *args, **kwargs) :
650 def compute_item_matrix(items, row_first=False, empty=None, *args, **kwargs) :
651 """Returns a nested list, and info to columnize items
651 """Returns a nested list, and info to columnize items
652
652
653 Parameters
653 Parameters
654 ----------
654 ----------
655
655
656 items
656 items
657 list of strings to columize
657 list of strings to columize
658 row_first : (default False)
658 row_first : (default False)
659 Whether to compute columns for a row-first matrix instead of
659 Whether to compute columns for a row-first matrix instead of
660 column-first (default).
660 column-first (default).
661 empty : (default None)
661 empty : (default None)
662 default value to fill list if needed
662 default value to fill list if needed
663 separator_size : int (default=2)
663 separator_size : int (default=2)
664 How much characters will be used as a separation between each columns.
664 How much characters will be used as a separation between each columns.
665 displaywidth : int (default=80)
665 displaywidth : int (default=80)
666 The width of the area onto which the columns should enter
666 The width of the area onto which the columns should enter
667
667
668 Returns
668 Returns
669 -------
669 -------
670
670
671 strings_matrix
671 strings_matrix
672
672
673 nested list of string, the outer most list contains as many list as
673 nested list of string, the outer most list contains as many list as
674 rows, the innermost lists have each as many element as columns. If the
674 rows, the innermost lists have each as many element as columns. If the
675 total number of elements in `items` does not equal the product of
675 total number of elements in `items` does not equal the product of
676 rows*columns, the last element of some lists are filled with `None`.
676 rows*columns, the last element of some lists are filled with `None`.
677
677
678 dict_info
678 dict_info
679 some info to make columnize easier:
679 some info to make columnize easier:
680
680
681 num_columns
681 num_columns
682 number of columns
682 number of columns
683 max_rows
683 max_rows
684 maximum number of rows (final number may be less)
684 maximum number of rows (final number may be less)
685 column_widths
685 column_widths
686 list of with of each columns
686 list of with of each columns
687 optimal_separator_width
687 optimal_separator_width
688 best separator width between columns
688 best separator width between columns
689
689
690 Examples
690 Examples
691 --------
691 --------
692 ::
692 ::
693
693
694 In [1]: l = ['aaa','b','cc','d','eeeee','f','g','h','i','j','k','l']
694 In [1]: l = ['aaa','b','cc','d','eeeee','f','g','h','i','j','k','l']
695 In [2]: list, info = compute_item_matrix(l, displaywidth=12)
695 In [2]: list, info = compute_item_matrix(l, displaywidth=12)
696 In [3]: list
696 In [3]: list
697 Out[3]: [['aaa', 'f', 'k'], ['b', 'g', 'l'], ['cc', 'h', None], ['d', 'i', None], ['eeeee', 'j', None]]
697 Out[3]: [['aaa', 'f', 'k'], ['b', 'g', 'l'], ['cc', 'h', None], ['d', 'i', None], ['eeeee', 'j', None]]
698 In [4]: ideal = {'num_columns': 3, 'column_widths': [5, 1, 1], 'optimal_separator_width': 2, 'max_rows': 5}
698 In [4]: ideal = {'num_columns': 3, 'column_widths': [5, 1, 1], 'optimal_separator_width': 2, 'max_rows': 5}
699 In [5]: all((info[k] == ideal[k] for k in ideal.keys()))
699 In [5]: all((info[k] == ideal[k] for k in ideal.keys()))
700 Out[5]: True
700 Out[5]: True
701 """
701 """
702 info = _find_optimal(list(map(len, items)), row_first, *args, **kwargs)
702 info = _find_optimal(list(map(len, items)), row_first, *args, **kwargs)
703 nrow, ncol = info['max_rows'], info['num_columns']
703 nrow, ncol = info['max_rows'], info['num_columns']
704 if row_first:
704 if row_first:
705 return ([[_get_or_default(items, r * ncol + c, default=empty) for c in range(ncol)] for r in range(nrow)], info)
705 return ([[_get_or_default(items, r * ncol + c, default=empty) for c in range(ncol)] for r in range(nrow)], info)
706 else:
706 else:
707 return ([[_get_or_default(items, c * nrow + r, default=empty) for c in range(ncol)] for r in range(nrow)], info)
707 return ([[_get_or_default(items, c * nrow + r, default=empty) for c in range(ncol)] for r in range(nrow)], info)
708
708
709
709
710 def columnize(items, row_first=False, separator=' ', displaywidth=80, spread=False):
710 def columnize(items, row_first=False, separator=' ', displaywidth=80, spread=False):
711 """ Transform a list of strings into a single string with columns.
711 """ Transform a list of strings into a single string with columns.
712
712
713 Parameters
713 Parameters
714 ----------
714 ----------
715 items : sequence of strings
715 items : sequence of strings
716 The strings to process.
716 The strings to process.
717
717
718 row_first : (default False)
718 row_first : (default False)
719 Whether to compute columns for a row-first matrix instead of
719 Whether to compute columns for a row-first matrix instead of
720 column-first (default).
720 column-first (default).
721
721
722 separator : str, optional [default is two spaces]
722 separator : str, optional [default is two spaces]
723 The string that separates columns.
723 The string that separates columns.
724
724
725 displaywidth : int, optional [default is 80]
725 displaywidth : int, optional [default is 80]
726 Width of the display in number of characters.
726 Width of the display in number of characters.
727
727
728 Returns
728 Returns
729 -------
729 -------
730 The formatted string.
730 The formatted string.
731 """
731 """
732 if not items:
732 if not items:
733 return '\n'
733 return '\n'
734 matrix, info = compute_item_matrix(items, row_first=row_first, separator_size=len(separator), displaywidth=displaywidth)
734 matrix, info = compute_item_matrix(items, row_first=row_first, separator_size=len(separator), displaywidth=displaywidth)
735 if spread:
735 if spread:
736 separator = separator.ljust(int(info['optimal_separator_width']))
736 separator = separator.ljust(int(info['optimal_separator_width']))
737 fmatrix = [filter(None, x) for x in matrix]
737 fmatrix = [filter(None, x) for x in matrix]
738 sjoin = lambda x : separator.join([ y.ljust(w, ' ') for y, w in zip(x, info['column_widths'])])
738 sjoin = lambda x : separator.join([ y.ljust(w, ' ') for y, w in zip(x, info['column_widths'])])
739 return '\n'.join(map(sjoin, fmatrix))+'\n'
739 return '\n'.join(map(sjoin, fmatrix))+'\n'
740
740
741
741
742 def get_text_list(list_, last_sep=' and ', sep=", ", wrap_item_with=""):
742 def get_text_list(list_, last_sep=' and ', sep=", ", wrap_item_with=""):
743 """
743 """
744 Return a string with a natural enumeration of items
744 Return a string with a natural enumeration of items
745
745
746 >>> get_text_list(['a', 'b', 'c', 'd'])
746 >>> get_text_list(['a', 'b', 'c', 'd'])
747 'a, b, c and d'
747 'a, b, c and d'
748 >>> get_text_list(['a', 'b', 'c'], ' or ')
748 >>> get_text_list(['a', 'b', 'c'], ' or ')
749 'a, b or c'
749 'a, b or c'
750 >>> get_text_list(['a', 'b', 'c'], ', ')
750 >>> get_text_list(['a', 'b', 'c'], ', ')
751 'a, b, c'
751 'a, b, c'
752 >>> get_text_list(['a', 'b'], ' or ')
752 >>> get_text_list(['a', 'b'], ' or ')
753 'a or b'
753 'a or b'
754 >>> get_text_list(['a'])
754 >>> get_text_list(['a'])
755 'a'
755 'a'
756 >>> get_text_list([])
756 >>> get_text_list([])
757 ''
757 ''
758 >>> get_text_list(['a', 'b'], wrap_item_with="`")
758 >>> get_text_list(['a', 'b'], wrap_item_with="`")
759 '`a` and `b`'
759 '`a` and `b`'
760 >>> get_text_list(['a', 'b', 'c', 'd'], " = ", sep=" + ")
760 >>> get_text_list(['a', 'b', 'c', 'd'], " = ", sep=" + ")
761 'a + b + c = d'
761 'a + b + c = d'
762 """
762 """
763 if len(list_) == 0:
763 if len(list_) == 0:
764 return ''
764 return ''
765 if wrap_item_with:
765 if wrap_item_with:
766 list_ = ['%s%s%s' % (wrap_item_with, item, wrap_item_with) for
766 list_ = ['%s%s%s' % (wrap_item_with, item, wrap_item_with) for
767 item in list_]
767 item in list_]
768 if len(list_) == 1:
768 if len(list_) == 1:
769 return list_[0]
769 return list_[0]
770 return '%s%s%s' % (
770 return '%s%s%s' % (
771 sep.join(i for i in list_[:-1]),
771 sep.join(i for i in list_[:-1]),
772 last_sep, list_[-1])
772 last_sep, list_[-1])
@@ -1,264 +1,263 b''
1 #!/usr/bin/env python3
1 #!/usr/bin/env python3
2 # -*- coding: utf-8 -*-
2 # -*- coding: utf-8 -*-
3 """Setup script for IPython.
3 """Setup script for IPython.
4
4
5 Under Posix environments it works like a typical setup.py script.
5 Under Posix environments it works like a typical setup.py script.
6 Under Windows, the command sdist is not supported, since IPython
6 Under Windows, the command sdist is not supported, since IPython
7 requires utilities which are not available under Windows."""
7 requires utilities which are not available under Windows."""
8
8
9 #-----------------------------------------------------------------------------
9 #-----------------------------------------------------------------------------
10 # Copyright (c) 2008-2011, IPython Development Team.
10 # Copyright (c) 2008-2011, IPython Development Team.
11 # Copyright (c) 2001-2007, Fernando Perez <fernando.perez@colorado.edu>
11 # Copyright (c) 2001-2007, Fernando Perez <fernando.perez@colorado.edu>
12 # Copyright (c) 2001, Janko Hauser <jhauser@zscout.de>
12 # Copyright (c) 2001, Janko Hauser <jhauser@zscout.de>
13 # Copyright (c) 2001, Nathaniel Gray <n8gray@caltech.edu>
13 # Copyright (c) 2001, Nathaniel Gray <n8gray@caltech.edu>
14 #
14 #
15 # Distributed under the terms of the Modified BSD License.
15 # Distributed under the terms of the Modified BSD License.
16 #
16 #
17 # The full license is in the file COPYING.rst, distributed with this software.
17 # The full license is in the file COPYING.rst, distributed with this software.
18 #-----------------------------------------------------------------------------
18 #-----------------------------------------------------------------------------
19
19
20 from __future__ import print_function
20 from __future__ import print_function
21
21
22 import os
22 import os
23 import sys
23 import sys
24
24
25 # **Python version check**
25 # **Python version check**
26 #
26 #
27 # This check is also made in IPython/__init__, don't forget to update both when
27 # This check is also made in IPython/__init__, don't forget to update both when
28 # changing Python version requirements.
28 # changing Python version requirements.
29 if sys.version_info < (3, 5):
29 if sys.version_info < (3, 5):
30 pip_message = 'This may be due to an out of date pip. Make sure you have pip >= 9.0.1.'
30 pip_message = 'This may be due to an out of date pip. Make sure you have pip >= 9.0.1.'
31 try:
31 try:
32 import pip
32 import pip
33 pip_version = tuple([int(x) for x in pip.__version__.split('.')[:3]])
33 pip_version = tuple([int(x) for x in pip.__version__.split('.')[:3]])
34 if pip_version < (9, 0, 1) :
34 if pip_version < (9, 0, 1) :
35 pip_message = 'Your pip version is out of date, please install pip >= 9.0.1. '\
35 pip_message = 'Your pip version is out of date, please install pip >= 9.0.1. '\
36 'pip {} detected.'.format(pip.__version__)
36 'pip {} detected.'.format(pip.__version__)
37 else:
37 else:
38 # pip is new enough - it must be something else
38 # pip is new enough - it must be something else
39 pip_message = ''
39 pip_message = ''
40 except Exception:
40 except Exception:
41 pass
41 pass
42
42
43
43
44 error = """
44 error = """
45 IPython 7.0+ supports Python 3.5 and above.
45 IPython 7.0+ supports Python 3.5 and above.
46 When using Python 2.7, please install IPython 5.x LTS Long Term Support version.
46 When using Python 2.7, please install IPython 5.x LTS Long Term Support version.
47 Python 3.3 and 3.4 were supported up to IPython 6.x.
47 Python 3.3 and 3.4 were supported up to IPython 6.x.
48
48
49 See IPython `README.rst` file for more information:
49 See IPython `README.rst` file for more information:
50
50
51 https://github.com/ipython/ipython/blob/master/README.rst
51 https://github.com/ipython/ipython/blob/master/README.rst
52
52
53 Python {py} detected.
53 Python {py} detected.
54 {pip}
54 {pip}
55 """.format(py=sys.version_info, pip=pip_message )
55 """.format(py=sys.version_info, pip=pip_message )
56
56
57 print(error, file=sys.stderr)
57 print(error, file=sys.stderr)
58 sys.exit(1)
58 sys.exit(1)
59
59
60 # At least we're on the python version we need, move on.
60 # At least we're on the python version we need, move on.
61
61
62 # BEFORE importing distutils, remove MANIFEST. distutils doesn't properly
62 # BEFORE importing distutils, remove MANIFEST. distutils doesn't properly
63 # update it when the contents of directories change.
63 # update it when the contents of directories change.
64 if os.path.exists('MANIFEST'): os.remove('MANIFEST')
64 if os.path.exists('MANIFEST'): os.remove('MANIFEST')
65
65
66 from distutils.core import setup
66 from distutils.core import setup
67
67
68 # Our own imports
68 # Our own imports
69 from setupbase import target_update
69 from setupbase import target_update
70
70
71 from setupbase import (
71 from setupbase import (
72 setup_args,
72 setup_args,
73 find_packages,
73 find_packages,
74 find_package_data,
74 find_package_data,
75 check_package_data_first,
75 check_package_data_first,
76 find_entry_points,
76 find_entry_points,
77 build_scripts_entrypt,
77 build_scripts_entrypt,
78 find_data_files,
78 find_data_files,
79 git_prebuild,
79 git_prebuild,
80 install_symlinked,
80 install_symlinked,
81 install_lib_symlink,
81 install_lib_symlink,
82 install_scripts_for_symlink,
82 install_scripts_for_symlink,
83 unsymlink,
83 unsymlink,
84 )
84 )
85
85
86 isfile = os.path.isfile
86 isfile = os.path.isfile
87 pjoin = os.path.join
87 pjoin = os.path.join
88
88
89 #-------------------------------------------------------------------------------
89 #-------------------------------------------------------------------------------
90 # Handle OS specific things
90 # Handle OS specific things
91 #-------------------------------------------------------------------------------
91 #-------------------------------------------------------------------------------
92
92
93 if os.name in ('nt','dos'):
93 if os.name in ('nt','dos'):
94 os_name = 'windows'
94 os_name = 'windows'
95 else:
95 else:
96 os_name = os.name
96 os_name = os.name
97
97
98 # Under Windows, 'sdist' has not been supported. Now that the docs build with
98 # Under Windows, 'sdist' has not been supported. Now that the docs build with
99 # Sphinx it might work, but let's not turn it on until someone confirms that it
99 # Sphinx it might work, but let's not turn it on until someone confirms that it
100 # actually works.
100 # actually works.
101 if os_name == 'windows' and 'sdist' in sys.argv:
101 if os_name == 'windows' and 'sdist' in sys.argv:
102 print('The sdist command is not available under Windows. Exiting.')
102 print('The sdist command is not available under Windows. Exiting.')
103 sys.exit(1)
103 sys.exit(1)
104
104
105
105
106 #-------------------------------------------------------------------------------
106 #-------------------------------------------------------------------------------
107 # Things related to the IPython documentation
107 # Things related to the IPython documentation
108 #-------------------------------------------------------------------------------
108 #-------------------------------------------------------------------------------
109
109
110 # update the manuals when building a source dist
110 # update the manuals when building a source dist
111 if len(sys.argv) >= 2 and sys.argv[1] in ('sdist','bdist_rpm'):
111 if len(sys.argv) >= 2 and sys.argv[1] in ('sdist','bdist_rpm'):
112
112
113 # List of things to be updated. Each entry is a triplet of args for
113 # List of things to be updated. Each entry is a triplet of args for
114 # target_update()
114 # target_update()
115 to_update = [
115 to_update = [
116 ('docs/man/ipython.1.gz',
116 ('docs/man/ipython.1.gz',
117 ['docs/man/ipython.1'],
117 ['docs/man/ipython.1'],
118 'cd docs/man && gzip -9c ipython.1 > ipython.1.gz'),
118 'cd docs/man && gzip -9c ipython.1 > ipython.1.gz'),
119 ]
119 ]
120
120
121
121
122 [ target_update(*t) for t in to_update ]
122 [ target_update(*t) for t in to_update ]
123
123
124 #---------------------------------------------------------------------------
124 #---------------------------------------------------------------------------
125 # Find all the packages, package data, and data_files
125 # Find all the packages, package data, and data_files
126 #---------------------------------------------------------------------------
126 #---------------------------------------------------------------------------
127
127
128 packages = find_packages()
128 packages = find_packages()
129 package_data = find_package_data()
129 package_data = find_package_data()
130
130
131 data_files = find_data_files()
131 data_files = find_data_files()
132
132
133 setup_args['packages'] = packages
133 setup_args['packages'] = packages
134 setup_args['package_data'] = package_data
134 setup_args['package_data'] = package_data
135 setup_args['data_files'] = data_files
135 setup_args['data_files'] = data_files
136
136
137 #---------------------------------------------------------------------------
137 #---------------------------------------------------------------------------
138 # custom distutils commands
138 # custom distutils commands
139 #---------------------------------------------------------------------------
139 #---------------------------------------------------------------------------
140 # imports here, so they are after setuptools import if there was one
140 # imports here, so they are after setuptools import if there was one
141 from distutils.command.sdist import sdist
141 from distutils.command.sdist import sdist
142
142
143 setup_args['cmdclass'] = {
143 setup_args['cmdclass'] = {
144 'build_py': \
144 'build_py': \
145 check_package_data_first(git_prebuild('IPython')),
145 check_package_data_first(git_prebuild('IPython')),
146 'sdist' : git_prebuild('IPython', sdist),
146 'sdist' : git_prebuild('IPython', sdist),
147 'symlink': install_symlinked,
147 'symlink': install_symlinked,
148 'install_lib_symlink': install_lib_symlink,
148 'install_lib_symlink': install_lib_symlink,
149 'install_scripts_sym': install_scripts_for_symlink,
149 'install_scripts_sym': install_scripts_for_symlink,
150 'unsymlink': unsymlink,
150 'unsymlink': unsymlink,
151 }
151 }
152
152
153
153
154 #---------------------------------------------------------------------------
154 #---------------------------------------------------------------------------
155 # Handle scripts, dependencies, and setuptools specific things
155 # Handle scripts, dependencies, and setuptools specific things
156 #---------------------------------------------------------------------------
156 #---------------------------------------------------------------------------
157
157
158 # For some commands, use setuptools. Note that we do NOT list install here!
158 # For some commands, use setuptools. Note that we do NOT list install here!
159 # If you want a setuptools-enhanced install, just run 'setupegg.py install'
159 # If you want a setuptools-enhanced install, just run 'setupegg.py install'
160 needs_setuptools = {'develop', 'release', 'bdist_egg', 'bdist_rpm',
160 needs_setuptools = {'develop', 'release', 'bdist_egg', 'bdist_rpm',
161 'bdist', 'bdist_dumb', 'bdist_wininst', 'bdist_wheel',
161 'bdist', 'bdist_dumb', 'bdist_wininst', 'bdist_wheel',
162 'egg_info', 'easy_install', 'upload', 'install_egg_info',
162 'egg_info', 'easy_install', 'upload', 'install_egg_info',
163 }
163 }
164
164
165 if len(needs_setuptools.intersection(sys.argv)) > 0:
165 if len(needs_setuptools.intersection(sys.argv)) > 0:
166 import setuptools
166 import setuptools
167
167
168 # This dict is used for passing extra arguments that are setuptools
168 # This dict is used for passing extra arguments that are setuptools
169 # specific to setup
169 # specific to setup
170 setuptools_extra_args = {}
170 setuptools_extra_args = {}
171
171
172 # setuptools requirements
172 # setuptools requirements
173
173
174 extras_require = dict(
174 extras_require = dict(
175 parallel = ['ipyparallel'],
175 parallel = ['ipyparallel'],
176 qtconsole = ['qtconsole'],
176 qtconsole = ['qtconsole'],
177 doc = ['Sphinx>=1.3'],
177 doc = ['Sphinx>=1.3'],
178 test = ['nose>=0.10.1', 'requests', 'testpath', 'pygments', 'nbformat', 'ipykernel', 'numpy'],
178 test = ['nose>=0.10.1', 'requests', 'testpath', 'pygments', 'nbformat', 'ipykernel', 'numpy'],
179 terminal = [],
179 terminal = [],
180 kernel = ['ipykernel'],
180 kernel = ['ipykernel'],
181 nbformat = ['nbformat'],
181 nbformat = ['nbformat'],
182 notebook = ['notebook', 'ipywidgets'],
182 notebook = ['notebook', 'ipywidgets'],
183 nbconvert = ['nbconvert'],
183 nbconvert = ['nbconvert'],
184 )
184 )
185
185
186 install_requires = [
186 install_requires = [
187 'setuptools>=18.5',
187 'setuptools>=18.5',
188 'jedi>=0.10',
188 'jedi>=0.10',
189 'decorator',
189 'decorator',
190 'pickleshare',
190 'pickleshare',
191 'simplegeneric>0.8',
192 'traitlets>=4.2',
191 'traitlets>=4.2',
193 'prompt_toolkit>=2.0.0,<2.1.0',
192 'prompt_toolkit>=2.0.0,<2.1.0',
194 'pygments',
193 'pygments',
195 'backcall',
194 'backcall',
196 ]
195 ]
197
196
198 # Platform-specific dependencies:
197 # Platform-specific dependencies:
199 # This is the correct way to specify these,
198 # This is the correct way to specify these,
200 # but requires pip >= 6. pip < 6 ignores these.
199 # but requires pip >= 6. pip < 6 ignores these.
201
200
202 extras_require.update({
201 extras_require.update({
203 ':python_version == "3.4"': ['typing'],
202 ':python_version == "3.4"': ['typing'],
204 ':sys_platform != "win32"': ['pexpect'],
203 ':sys_platform != "win32"': ['pexpect'],
205 ':sys_platform == "darwin"': ['appnope'],
204 ':sys_platform == "darwin"': ['appnope'],
206 ':sys_platform == "win32"': ['colorama'],
205 ':sys_platform == "win32"': ['colorama'],
207 ':sys_platform == "win32" and python_version < "3.6"': ['win_unicode_console>=0.5'],
206 ':sys_platform == "win32" and python_version < "3.6"': ['win_unicode_console>=0.5'],
208 })
207 })
209 # FIXME: re-specify above platform dependencies for pip < 6
208 # FIXME: re-specify above platform dependencies for pip < 6
210 # These would result in non-portable bdists.
209 # These would result in non-portable bdists.
211 if not any(arg.startswith('bdist') for arg in sys.argv):
210 if not any(arg.startswith('bdist') for arg in sys.argv):
212 if sys.platform == 'darwin':
211 if sys.platform == 'darwin':
213 install_requires.extend(['appnope'])
212 install_requires.extend(['appnope'])
214
213
215 if not sys.platform.startswith('win'):
214 if not sys.platform.startswith('win'):
216 install_requires.append('pexpect')
215 install_requires.append('pexpect')
217
216
218 # workaround pypa/setuptools#147, where setuptools misspells
217 # workaround pypa/setuptools#147, where setuptools misspells
219 # platform_python_implementation as python_implementation
218 # platform_python_implementation as python_implementation
220 if 'setuptools' in sys.modules:
219 if 'setuptools' in sys.modules:
221 for key in list(extras_require):
220 for key in list(extras_require):
222 if 'platform_python_implementation' in key:
221 if 'platform_python_implementation' in key:
223 new_key = key.replace('platform_python_implementation', 'python_implementation')
222 new_key = key.replace('platform_python_implementation', 'python_implementation')
224 extras_require[new_key] = extras_require.pop(key)
223 extras_require[new_key] = extras_require.pop(key)
225
224
226 everything = set()
225 everything = set()
227 for key, deps in extras_require.items():
226 for key, deps in extras_require.items():
228 if ':' not in key:
227 if ':' not in key:
229 everything.update(deps)
228 everything.update(deps)
230 extras_require['all'] = everything
229 extras_require['all'] = everything
231
230
232 if 'setuptools' in sys.modules:
231 if 'setuptools' in sys.modules:
233 setuptools_extra_args['python_requires'] = '>=3.5'
232 setuptools_extra_args['python_requires'] = '>=3.5'
234 setuptools_extra_args['zip_safe'] = False
233 setuptools_extra_args['zip_safe'] = False
235 setuptools_extra_args['entry_points'] = {
234 setuptools_extra_args['entry_points'] = {
236 'console_scripts': find_entry_points(),
235 'console_scripts': find_entry_points(),
237 'pygments.lexers': [
236 'pygments.lexers': [
238 'ipythonconsole = IPython.lib.lexers:IPythonConsoleLexer',
237 'ipythonconsole = IPython.lib.lexers:IPythonConsoleLexer',
239 'ipython = IPython.lib.lexers:IPythonLexer',
238 'ipython = IPython.lib.lexers:IPythonLexer',
240 'ipython3 = IPython.lib.lexers:IPython3Lexer',
239 'ipython3 = IPython.lib.lexers:IPython3Lexer',
241 ],
240 ],
242 }
241 }
243 setup_args['extras_require'] = extras_require
242 setup_args['extras_require'] = extras_require
244 setup_args['install_requires'] = install_requires
243 setup_args['install_requires'] = install_requires
245
244
246 else:
245 else:
247 # scripts has to be a non-empty list, or install_scripts isn't called
246 # scripts has to be a non-empty list, or install_scripts isn't called
248 setup_args['scripts'] = [e.split('=')[0].strip() for e in find_entry_points()]
247 setup_args['scripts'] = [e.split('=')[0].strip() for e in find_entry_points()]
249
248
250 setup_args['cmdclass']['build_scripts'] = build_scripts_entrypt
249 setup_args['cmdclass']['build_scripts'] = build_scripts_entrypt
251
250
252 #---------------------------------------------------------------------------
251 #---------------------------------------------------------------------------
253 # Do the actual setup now
252 # Do the actual setup now
254 #---------------------------------------------------------------------------
253 #---------------------------------------------------------------------------
255
254
256 setup_args.update(setuptools_extra_args)
255 setup_args.update(setuptools_extra_args)
257
256
258
257
259
258
260 def main():
259 def main():
261 setup(**setup_args)
260 setup(**setup_args)
262
261
263 if __name__ == '__main__':
262 if __name__ == '__main__':
264 main()
263 main()
General Comments 0
You need to be logged in to leave comments. Login now