##// END OF EJS Templates
Python 3.11 fixes
Nikita Kniazev -
Show More
@@ -1,55 +1,59 b''
1 name: Run tests
1 name: Run tests
2
2
3 on:
3 on:
4 push:
4 push:
5 pull_request:
5 pull_request:
6 # Run weekly on Monday at 1:23 UTC
6 # Run weekly on Monday at 1:23 UTC
7 schedule:
7 schedule:
8 - cron: '23 1 * * 1'
8 - cron: '23 1 * * 1'
9 workflow_dispatch:
9 workflow_dispatch:
10
10
11
11
12 jobs:
12 jobs:
13 test:
13 test:
14 runs-on: ${{ matrix.os }}
14 runs-on: ${{ matrix.os }}
15 strategy:
15 strategy:
16 matrix:
16 matrix:
17 os: [ubuntu-latest]
17 os: [ubuntu-latest]
18 python-version: ["3.7", "3.8", "3.9", "3.10"]
18 python-version: ["3.7", "3.8", "3.9", "3.10"]
19 deps: [test_extra]
19 deps: [test_extra]
20 # Test all on ubuntu, test ends on macos
20 # Test all on ubuntu, test ends on macos
21 include:
21 include:
22 - os: macos-latest
22 - os: macos-latest
23 python-version: "3.7"
23 python-version: "3.7"
24 deps: test_extra
24 deps: test_extra
25 - os: macos-latest
25 - os: macos-latest
26 python-version: "3.10"
26 python-version: "3.10"
27 deps: test_extra
27 deps: test_extra
28 # Tests minimal dependencies set
28 # Tests minimal dependencies set
29 - os: ubuntu-latest
29 - os: ubuntu-latest
30 python-version: "3.10"
30 python-version: "3.10"
31 deps: test
31 deps: test
32 # Tests latest development Python version
33 - os: ubuntu-latest
34 python-version: "3.11-dev"
35 deps: test
32
36
33 steps:
37 steps:
34 - uses: actions/checkout@v2
38 - uses: actions/checkout@v2
35 - name: Set up Python ${{ matrix.python-version }}
39 - name: Set up Python ${{ matrix.python-version }}
36 uses: actions/setup-python@v2
40 uses: actions/setup-python@v2
37 with:
41 with:
38 python-version: ${{ matrix.python-version }}
42 python-version: ${{ matrix.python-version }}
39 - name: Install latex
43 - name: Install latex
40 if: runner.os == 'Linux' && matrix.deps == 'test_extra'
44 if: runner.os == 'Linux' && matrix.deps == 'test_extra'
41 run: sudo apt-get -yq -o Acquire::Retries=3 --no-install-suggests --no-install-recommends install texlive dvipng
45 run: sudo apt-get -yq -o Acquire::Retries=3 --no-install-suggests --no-install-recommends install texlive dvipng
42 - name: Install and update Python dependencies
46 - name: Install and update Python dependencies
43 run: |
47 run: |
44 python -m pip install --upgrade pip setuptools wheel
48 python -m pip install --upgrade pip setuptools wheel
45 python -m pip install --upgrade -e .[${{ matrix.deps }}]
49 python -m pip install --upgrade -e .[${{ matrix.deps }}]
46 python -m pip install --upgrade check-manifest pytest-cov
50 python -m pip install --upgrade check-manifest pytest-cov
47 - name: Check manifest
51 - name: Check manifest
48 run: check-manifest
52 run: check-manifest
49 - name: pytest
53 - name: pytest
50 env:
54 env:
51 COLUMNS: 120
55 COLUMNS: 120
52 run: |
56 run: |
53 pytest --color=yes -v --cov --cov-report=xml
57 pytest --color=yes -v --cov --cov-report=xml
54 - name: Upload coverage to Codecov
58 - name: Upload coverage to Codecov
55 uses: codecov/codecov-action@v2
59 uses: codecov/codecov-action@v2
@@ -1,1284 +1,1289 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 pytest
8 import pytest
9 import sys
9 import sys
10 import textwrap
10 import textwrap
11 import unittest
11 import unittest
12
12
13 from contextlib import contextmanager
13 from contextlib import contextmanager
14
14
15 from traitlets.config.loader import Config
15 from traitlets.config.loader import Config
16 from IPython import get_ipython
16 from IPython import get_ipython
17 from IPython.core import completer
17 from IPython.core import completer
18 from IPython.utils.tempdir import TemporaryDirectory, TemporaryWorkingDirectory
18 from IPython.utils.tempdir import TemporaryDirectory, TemporaryWorkingDirectory
19 from IPython.utils.generics import complete_object
19 from IPython.utils.generics import complete_object
20 from IPython.testing import decorators as dec
20 from IPython.testing import decorators as dec
21
21
22 from IPython.core.completer import (
22 from IPython.core.completer import (
23 Completion,
23 Completion,
24 provisionalcompleter,
24 provisionalcompleter,
25 match_dict_keys,
25 match_dict_keys,
26 _deduplicate_completions,
26 _deduplicate_completions,
27 )
27 )
28
28
29 if sys.version_info >= (3, 10):
29 if sys.version_info >= (3, 10):
30 import jedi
30 import jedi
31 from pkg_resources import parse_version
31 from pkg_resources import parse_version
32
32
33 # Requires https://github.com/davidhalter/jedi/pull/1795
33 # Requires https://github.com/davidhalter/jedi/pull/1795
34 jedi_issue = parse_version(jedi.__version__) <= parse_version("0.18.0")
34 jedi_issue = parse_version(jedi.__version__) <= parse_version("0.18.0")
35 else:
35 else:
36 jedi_issue = False
36 jedi_issue = False
37
37
38 # -----------------------------------------------------------------------------
38 # -----------------------------------------------------------------------------
39 # Test functions
39 # Test functions
40 # -----------------------------------------------------------------------------
40 # -----------------------------------------------------------------------------
41
41
42 def recompute_unicode_ranges():
42 def recompute_unicode_ranges():
43 """
43 """
44 utility to recompute the largest unicode range without any characters
44 utility to recompute the largest unicode range without any characters
45
45
46 use to recompute the gap in the global _UNICODE_RANGES of completer.py
46 use to recompute the gap in the global _UNICODE_RANGES of completer.py
47 """
47 """
48 import itertools
48 import itertools
49 import unicodedata
49 import unicodedata
50 valid = []
50 valid = []
51 for c in range(0,0x10FFFF + 1):
51 for c in range(0,0x10FFFF + 1):
52 try:
52 try:
53 unicodedata.name(chr(c))
53 unicodedata.name(chr(c))
54 except ValueError:
54 except ValueError:
55 continue
55 continue
56 valid.append(c)
56 valid.append(c)
57
57
58 def ranges(i):
58 def ranges(i):
59 for a, b in itertools.groupby(enumerate(i), lambda pair: pair[1] - pair[0]):
59 for a, b in itertools.groupby(enumerate(i), lambda pair: pair[1] - pair[0]):
60 b = list(b)
60 b = list(b)
61 yield b[0][1], b[-1][1]
61 yield b[0][1], b[-1][1]
62
62
63 rg = list(ranges(valid))
63 rg = list(ranges(valid))
64 lens = []
64 lens = []
65 gap_lens = []
65 gap_lens = []
66 pstart, pstop = 0,0
66 pstart, pstop = 0,0
67 for start, stop in rg:
67 for start, stop in rg:
68 lens.append(stop-start)
68 lens.append(stop-start)
69 gap_lens.append((start - pstop, hex(pstop), hex(start), f'{round((start - pstop)/0xe01f0*100)}%'))
69 gap_lens.append((start - pstop, hex(pstop), hex(start), f'{round((start - pstop)/0xe01f0*100)}%'))
70 pstart, pstop = start, stop
70 pstart, pstop = start, stop
71
71
72 return sorted(gap_lens)[-1]
72 return sorted(gap_lens)[-1]
73
73
74
74
75
75
76 def test_unicode_range():
76 def test_unicode_range():
77 """
77 """
78 Test that the ranges we test for unicode names give the same number of
78 Test that the ranges we test for unicode names give the same number of
79 results than testing the full length.
79 results than testing the full length.
80 """
80 """
81 from IPython.core.completer import _unicode_name_compute, _UNICODE_RANGES
81 from IPython.core.completer import _unicode_name_compute, _UNICODE_RANGES
82
82
83 expected_list = _unicode_name_compute([(0, 0x110000)])
83 expected_list = _unicode_name_compute([(0, 0x110000)])
84 test = _unicode_name_compute(_UNICODE_RANGES)
84 test = _unicode_name_compute(_UNICODE_RANGES)
85 len_exp = len(expected_list)
85 len_exp = len(expected_list)
86 len_test = len(test)
86 len_test = len(test)
87
87
88 # do not inline the len() or on error pytest will try to print the 130 000 +
88 # do not inline the len() or on error pytest will try to print the 130 000 +
89 # elements.
89 # elements.
90 message = None
90 message = None
91 if len_exp != len_test or len_exp > 131808:
91 if len_exp != len_test or len_exp > 131808:
92 size, start, stop, prct = recompute_unicode_ranges()
92 size, start, stop, prct = recompute_unicode_ranges()
93 message = f"""_UNICODE_RANGES likely wrong and need updating. This is
93 message = f"""_UNICODE_RANGES likely wrong and need updating. This is
94 likely due to a new release of Python. We've find that the biggest gap
94 likely due to a new release of Python. We've find that the biggest gap
95 in unicode characters has reduces in size to be {size} characters
95 in unicode characters has reduces in size to be {size} characters
96 ({prct}), from {start}, to {stop}. In completer.py likely update to
96 ({prct}), from {start}, to {stop}. In completer.py likely update to
97
97
98 _UNICODE_RANGES = [(32, {start}), ({stop}, 0xe01f0)]
98 _UNICODE_RANGES = [(32, {start}), ({stop}, 0xe01f0)]
99
99
100 And update the assertion below to use
100 And update the assertion below to use
101
101
102 len_exp <= {len_exp}
102 len_exp <= {len_exp}
103 """
103 """
104 assert len_exp == len_test, message
104 assert len_exp == len_test, message
105
105
106 # fail if new unicode symbols have been added.
106 # fail if new unicode symbols have been added.
107 assert len_exp <= 137714, message
107 assert len_exp <= 138552, message
108
108
109
109
110 @contextmanager
110 @contextmanager
111 def greedy_completion():
111 def greedy_completion():
112 ip = get_ipython()
112 ip = get_ipython()
113 greedy_original = ip.Completer.greedy
113 greedy_original = ip.Completer.greedy
114 try:
114 try:
115 ip.Completer.greedy = True
115 ip.Completer.greedy = True
116 yield
116 yield
117 finally:
117 finally:
118 ip.Completer.greedy = greedy_original
118 ip.Completer.greedy = greedy_original
119
119
120
120
121 def test_protect_filename():
121 def test_protect_filename():
122 if sys.platform == "win32":
122 if sys.platform == "win32":
123 pairs = [
123 pairs = [
124 ("abc", "abc"),
124 ("abc", "abc"),
125 (" abc", '" abc"'),
125 (" abc", '" abc"'),
126 ("a bc", '"a bc"'),
126 ("a bc", '"a bc"'),
127 ("a bc", '"a bc"'),
127 ("a bc", '"a bc"'),
128 (" bc", '" bc"'),
128 (" bc", '" bc"'),
129 ]
129 ]
130 else:
130 else:
131 pairs = [
131 pairs = [
132 ("abc", "abc"),
132 ("abc", "abc"),
133 (" abc", r"\ abc"),
133 (" abc", r"\ abc"),
134 ("a bc", r"a\ bc"),
134 ("a bc", r"a\ bc"),
135 ("a bc", r"a\ \ bc"),
135 ("a bc", r"a\ \ bc"),
136 (" bc", r"\ \ bc"),
136 (" bc", r"\ \ bc"),
137 # On posix, we also protect parens and other special characters.
137 # On posix, we also protect parens and other special characters.
138 ("a(bc", r"a\(bc"),
138 ("a(bc", r"a\(bc"),
139 ("a)bc", r"a\)bc"),
139 ("a)bc", r"a\)bc"),
140 ("a( )bc", r"a\(\ \)bc"),
140 ("a( )bc", r"a\(\ \)bc"),
141 ("a[1]bc", r"a\[1\]bc"),
141 ("a[1]bc", r"a\[1\]bc"),
142 ("a{1}bc", r"a\{1\}bc"),
142 ("a{1}bc", r"a\{1\}bc"),
143 ("a#bc", r"a\#bc"),
143 ("a#bc", r"a\#bc"),
144 ("a?bc", r"a\?bc"),
144 ("a?bc", r"a\?bc"),
145 ("a=bc", r"a\=bc"),
145 ("a=bc", r"a\=bc"),
146 ("a\\bc", r"a\\bc"),
146 ("a\\bc", r"a\\bc"),
147 ("a|bc", r"a\|bc"),
147 ("a|bc", r"a\|bc"),
148 ("a;bc", r"a\;bc"),
148 ("a;bc", r"a\;bc"),
149 ("a:bc", r"a\:bc"),
149 ("a:bc", r"a\:bc"),
150 ("a'bc", r"a\'bc"),
150 ("a'bc", r"a\'bc"),
151 ("a*bc", r"a\*bc"),
151 ("a*bc", r"a\*bc"),
152 ('a"bc', r"a\"bc"),
152 ('a"bc', r"a\"bc"),
153 ("a^bc", r"a\^bc"),
153 ("a^bc", r"a\^bc"),
154 ("a&bc", r"a\&bc"),
154 ("a&bc", r"a\&bc"),
155 ]
155 ]
156 # run the actual tests
156 # run the actual tests
157 for s1, s2 in pairs:
157 for s1, s2 in pairs:
158 s1p = completer.protect_filename(s1)
158 s1p = completer.protect_filename(s1)
159 assert s1p == s2
159 assert s1p == s2
160
160
161
161
162 def check_line_split(splitter, test_specs):
162 def check_line_split(splitter, test_specs):
163 for part1, part2, split in test_specs:
163 for part1, part2, split in test_specs:
164 cursor_pos = len(part1)
164 cursor_pos = len(part1)
165 line = part1 + part2
165 line = part1 + part2
166 out = splitter.split_line(line, cursor_pos)
166 out = splitter.split_line(line, cursor_pos)
167 assert out == split
167 assert out == split
168
168
169
169
170 def test_line_split():
170 def test_line_split():
171 """Basic line splitter test with default specs."""
171 """Basic line splitter test with default specs."""
172 sp = completer.CompletionSplitter()
172 sp = completer.CompletionSplitter()
173 # The format of the test specs is: part1, part2, expected answer. Parts 1
173 # The format of the test specs is: part1, part2, expected answer. Parts 1
174 # and 2 are joined into the 'line' sent to the splitter, as if the cursor
174 # and 2 are joined into the 'line' sent to the splitter, as if the cursor
175 # was at the end of part1. So an empty part2 represents someone hitting
175 # was at the end of part1. So an empty part2 represents someone hitting
176 # tab at the end of the line, the most common case.
176 # tab at the end of the line, the most common case.
177 t = [
177 t = [
178 ("run some/scrip", "", "some/scrip"),
178 ("run some/scrip", "", "some/scrip"),
179 ("run scripts/er", "ror.py foo", "scripts/er"),
179 ("run scripts/er", "ror.py foo", "scripts/er"),
180 ("echo $HOM", "", "HOM"),
180 ("echo $HOM", "", "HOM"),
181 ("print sys.pa", "", "sys.pa"),
181 ("print sys.pa", "", "sys.pa"),
182 ("print(sys.pa", "", "sys.pa"),
182 ("print(sys.pa", "", "sys.pa"),
183 ("execfile('scripts/er", "", "scripts/er"),
183 ("execfile('scripts/er", "", "scripts/er"),
184 ("a[x.", "", "x."),
184 ("a[x.", "", "x."),
185 ("a[x.", "y", "x."),
185 ("a[x.", "y", "x."),
186 ('cd "some_file/', "", "some_file/"),
186 ('cd "some_file/', "", "some_file/"),
187 ]
187 ]
188 check_line_split(sp, t)
188 check_line_split(sp, t)
189 # Ensure splitting works OK with unicode by re-running the tests with
189 # Ensure splitting works OK with unicode by re-running the tests with
190 # all inputs turned into unicode
190 # all inputs turned into unicode
191 check_line_split(sp, [map(str, p) for p in t])
191 check_line_split(sp, [map(str, p) for p in t])
192
192
193
193
194 class NamedInstanceMetaclass(type):
194 class NamedInstanceMetaclass(type):
195 def __getitem__(cls, item):
195 def __getitem__(cls, item):
196 return cls.get_instance(item)
196 return cls.get_instance(item)
197
197
198
198
199 class NamedInstanceClass(metaclass=NamedInstanceMetaclass):
199 class NamedInstanceClass(metaclass=NamedInstanceMetaclass):
200 def __init__(self, name):
200 def __init__(self, name):
201 if not hasattr(self.__class__, "instances"):
201 if not hasattr(self.__class__, "instances"):
202 self.__class__.instances = {}
202 self.__class__.instances = {}
203 self.__class__.instances[name] = self
203 self.__class__.instances[name] = self
204
204
205 @classmethod
205 @classmethod
206 def _ipython_key_completions_(cls):
206 def _ipython_key_completions_(cls):
207 return cls.instances.keys()
207 return cls.instances.keys()
208
208
209 @classmethod
209 @classmethod
210 def get_instance(cls, name):
210 def get_instance(cls, name):
211 return cls.instances[name]
211 return cls.instances[name]
212
212
213
213
214 class KeyCompletable:
214 class KeyCompletable:
215 def __init__(self, things=()):
215 def __init__(self, things=()):
216 self.things = things
216 self.things = things
217
217
218 def _ipython_key_completions_(self):
218 def _ipython_key_completions_(self):
219 return list(self.things)
219 return list(self.things)
220
220
221
221
222 @pytest.mark.xfail(
223 sys.version_info >= (3, 11),
224 reason="parso does not support 3.11 yet",
225 raises=NotImplementedError,
226 )
222 class TestCompleter(unittest.TestCase):
227 class TestCompleter(unittest.TestCase):
223 def setUp(self):
228 def setUp(self):
224 """
229 """
225 We want to silence all PendingDeprecationWarning when testing the completer
230 We want to silence all PendingDeprecationWarning when testing the completer
226 """
231 """
227 self._assertwarns = self.assertWarns(PendingDeprecationWarning)
232 self._assertwarns = self.assertWarns(PendingDeprecationWarning)
228 self._assertwarns.__enter__()
233 self._assertwarns.__enter__()
229
234
230 def tearDown(self):
235 def tearDown(self):
231 try:
236 try:
232 self._assertwarns.__exit__(None, None, None)
237 self._assertwarns.__exit__(None, None, None)
233 except AssertionError:
238 except AssertionError:
234 pass
239 pass
235
240
236 def test_custom_completion_error(self):
241 def test_custom_completion_error(self):
237 """Test that errors from custom attribute completers are silenced."""
242 """Test that errors from custom attribute completers are silenced."""
238 ip = get_ipython()
243 ip = get_ipython()
239
244
240 class A:
245 class A:
241 pass
246 pass
242
247
243 ip.user_ns["x"] = A()
248 ip.user_ns["x"] = A()
244
249
245 @complete_object.register(A)
250 @complete_object.register(A)
246 def complete_A(a, existing_completions):
251 def complete_A(a, existing_completions):
247 raise TypeError("this should be silenced")
252 raise TypeError("this should be silenced")
248
253
249 ip.complete("x.")
254 ip.complete("x.")
250
255
251 def test_custom_completion_ordering(self):
256 def test_custom_completion_ordering(self):
252 """Test that errors from custom attribute completers are silenced."""
257 """Test that errors from custom attribute completers are silenced."""
253 ip = get_ipython()
258 ip = get_ipython()
254
259
255 _, matches = ip.complete('in')
260 _, matches = ip.complete('in')
256 assert matches.index('input') < matches.index('int')
261 assert matches.index('input') < matches.index('int')
257
262
258 def complete_example(a):
263 def complete_example(a):
259 return ['example2', 'example1']
264 return ['example2', 'example1']
260
265
261 ip.Completer.custom_completers.add_re('ex*', complete_example)
266 ip.Completer.custom_completers.add_re('ex*', complete_example)
262 _, matches = ip.complete('ex')
267 _, matches = ip.complete('ex')
263 assert matches.index('example2') < matches.index('example1')
268 assert matches.index('example2') < matches.index('example1')
264
269
265 def test_unicode_completions(self):
270 def test_unicode_completions(self):
266 ip = get_ipython()
271 ip = get_ipython()
267 # Some strings that trigger different types of completion. Check them both
272 # Some strings that trigger different types of completion. Check them both
268 # in str and unicode forms
273 # in str and unicode forms
269 s = ["ru", "%ru", "cd /", "floa", "float(x)/"]
274 s = ["ru", "%ru", "cd /", "floa", "float(x)/"]
270 for t in s + list(map(str, s)):
275 for t in s + list(map(str, s)):
271 # We don't need to check exact completion values (they may change
276 # We don't need to check exact completion values (they may change
272 # depending on the state of the namespace, but at least no exceptions
277 # depending on the state of the namespace, but at least no exceptions
273 # should be thrown and the return value should be a pair of text, list
278 # should be thrown and the return value should be a pair of text, list
274 # values.
279 # values.
275 text, matches = ip.complete(t)
280 text, matches = ip.complete(t)
276 self.assertIsInstance(text, str)
281 self.assertIsInstance(text, str)
277 self.assertIsInstance(matches, list)
282 self.assertIsInstance(matches, list)
278
283
279 def test_latex_completions(self):
284 def test_latex_completions(self):
280 from IPython.core.latex_symbols import latex_symbols
285 from IPython.core.latex_symbols import latex_symbols
281 import random
286 import random
282
287
283 ip = get_ipython()
288 ip = get_ipython()
284 # Test some random unicode symbols
289 # Test some random unicode symbols
285 keys = random.sample(latex_symbols.keys(), 10)
290 keys = random.sample(sorted(latex_symbols), 10)
286 for k in keys:
291 for k in keys:
287 text, matches = ip.complete(k)
292 text, matches = ip.complete(k)
288 self.assertEqual(text, k)
293 self.assertEqual(text, k)
289 self.assertEqual(matches, [latex_symbols[k]])
294 self.assertEqual(matches, [latex_symbols[k]])
290 # Test a more complex line
295 # Test a more complex line
291 text, matches = ip.complete("print(\\alpha")
296 text, matches = ip.complete("print(\\alpha")
292 self.assertEqual(text, "\\alpha")
297 self.assertEqual(text, "\\alpha")
293 self.assertEqual(matches[0], latex_symbols["\\alpha"])
298 self.assertEqual(matches[0], latex_symbols["\\alpha"])
294 # Test multiple matching latex symbols
299 # Test multiple matching latex symbols
295 text, matches = ip.complete("\\al")
300 text, matches = ip.complete("\\al")
296 self.assertIn("\\alpha", matches)
301 self.assertIn("\\alpha", matches)
297 self.assertIn("\\aleph", matches)
302 self.assertIn("\\aleph", matches)
298
303
299 def test_latex_no_results(self):
304 def test_latex_no_results(self):
300 """
305 """
301 forward latex should really return nothing in either field if nothing is found.
306 forward latex should really return nothing in either field if nothing is found.
302 """
307 """
303 ip = get_ipython()
308 ip = get_ipython()
304 text, matches = ip.Completer.latex_matches("\\really_i_should_match_nothing")
309 text, matches = ip.Completer.latex_matches("\\really_i_should_match_nothing")
305 self.assertEqual(text, "")
310 self.assertEqual(text, "")
306 self.assertEqual(matches, ())
311 self.assertEqual(matches, ())
307
312
308 def test_back_latex_completion(self):
313 def test_back_latex_completion(self):
309 ip = get_ipython()
314 ip = get_ipython()
310
315
311 # do not return more than 1 matches for \beta, only the latex one.
316 # do not return more than 1 matches for \beta, only the latex one.
312 name, matches = ip.complete("\\β")
317 name, matches = ip.complete("\\β")
313 self.assertEqual(matches, ["\\beta"])
318 self.assertEqual(matches, ["\\beta"])
314
319
315 def test_back_unicode_completion(self):
320 def test_back_unicode_completion(self):
316 ip = get_ipython()
321 ip = get_ipython()
317
322
318 name, matches = ip.complete("\\Ⅴ")
323 name, matches = ip.complete("\\Ⅴ")
319 self.assertEqual(matches, ("\\ROMAN NUMERAL FIVE",))
324 self.assertEqual(matches, ("\\ROMAN NUMERAL FIVE",))
320
325
321 def test_forward_unicode_completion(self):
326 def test_forward_unicode_completion(self):
322 ip = get_ipython()
327 ip = get_ipython()
323
328
324 name, matches = ip.complete("\\ROMAN NUMERAL FIVE")
329 name, matches = ip.complete("\\ROMAN NUMERAL FIVE")
325 self.assertEqual(matches, ["Ⅴ"]) # This is not a V
330 self.assertEqual(matches, ["Ⅴ"]) # This is not a V
326 self.assertEqual(matches, ["\u2164"]) # same as above but explicit.
331 self.assertEqual(matches, ["\u2164"]) # same as above but explicit.
327
332
328 def test_delim_setting(self):
333 def test_delim_setting(self):
329 sp = completer.CompletionSplitter()
334 sp = completer.CompletionSplitter()
330 sp.delims = " "
335 sp.delims = " "
331 self.assertEqual(sp.delims, " ")
336 self.assertEqual(sp.delims, " ")
332 self.assertEqual(sp._delim_expr, r"[\ ]")
337 self.assertEqual(sp._delim_expr, r"[\ ]")
333
338
334 def test_spaces(self):
339 def test_spaces(self):
335 """Test with only spaces as split chars."""
340 """Test with only spaces as split chars."""
336 sp = completer.CompletionSplitter()
341 sp = completer.CompletionSplitter()
337 sp.delims = " "
342 sp.delims = " "
338 t = [("foo", "", "foo"), ("run foo", "", "foo"), ("run foo", "bar", "foo")]
343 t = [("foo", "", "foo"), ("run foo", "", "foo"), ("run foo", "bar", "foo")]
339 check_line_split(sp, t)
344 check_line_split(sp, t)
340
345
341 def test_has_open_quotes1(self):
346 def test_has_open_quotes1(self):
342 for s in ["'", "'''", "'hi' '"]:
347 for s in ["'", "'''", "'hi' '"]:
343 self.assertEqual(completer.has_open_quotes(s), "'")
348 self.assertEqual(completer.has_open_quotes(s), "'")
344
349
345 def test_has_open_quotes2(self):
350 def test_has_open_quotes2(self):
346 for s in ['"', '"""', '"hi" "']:
351 for s in ['"', '"""', '"hi" "']:
347 self.assertEqual(completer.has_open_quotes(s), '"')
352 self.assertEqual(completer.has_open_quotes(s), '"')
348
353
349 def test_has_open_quotes3(self):
354 def test_has_open_quotes3(self):
350 for s in ["''", "''' '''", "'hi' 'ipython'"]:
355 for s in ["''", "''' '''", "'hi' 'ipython'"]:
351 self.assertFalse(completer.has_open_quotes(s))
356 self.assertFalse(completer.has_open_quotes(s))
352
357
353 def test_has_open_quotes4(self):
358 def test_has_open_quotes4(self):
354 for s in ['""', '""" """', '"hi" "ipython"']:
359 for s in ['""', '""" """', '"hi" "ipython"']:
355 self.assertFalse(completer.has_open_quotes(s))
360 self.assertFalse(completer.has_open_quotes(s))
356
361
357 @pytest.mark.xfail(
362 @pytest.mark.xfail(
358 sys.platform == "win32", reason="abspath completions fail on Windows"
363 sys.platform == "win32", reason="abspath completions fail on Windows"
359 )
364 )
360 def test_abspath_file_completions(self):
365 def test_abspath_file_completions(self):
361 ip = get_ipython()
366 ip = get_ipython()
362 with TemporaryDirectory() as tmpdir:
367 with TemporaryDirectory() as tmpdir:
363 prefix = os.path.join(tmpdir, "foo")
368 prefix = os.path.join(tmpdir, "foo")
364 suffixes = ["1", "2"]
369 suffixes = ["1", "2"]
365 names = [prefix + s for s in suffixes]
370 names = [prefix + s for s in suffixes]
366 for n in names:
371 for n in names:
367 open(n, "w").close()
372 open(n, "w").close()
368
373
369 # Check simple completion
374 # Check simple completion
370 c = ip.complete(prefix)[1]
375 c = ip.complete(prefix)[1]
371 self.assertEqual(c, names)
376 self.assertEqual(c, names)
372
377
373 # Now check with a function call
378 # Now check with a function call
374 cmd = 'a = f("%s' % prefix
379 cmd = 'a = f("%s' % prefix
375 c = ip.complete(prefix, cmd)[1]
380 c = ip.complete(prefix, cmd)[1]
376 comp = [prefix + s for s in suffixes]
381 comp = [prefix + s for s in suffixes]
377 self.assertEqual(c, comp)
382 self.assertEqual(c, comp)
378
383
379 def test_local_file_completions(self):
384 def test_local_file_completions(self):
380 ip = get_ipython()
385 ip = get_ipython()
381 with TemporaryWorkingDirectory():
386 with TemporaryWorkingDirectory():
382 prefix = "./foo"
387 prefix = "./foo"
383 suffixes = ["1", "2"]
388 suffixes = ["1", "2"]
384 names = [prefix + s for s in suffixes]
389 names = [prefix + s for s in suffixes]
385 for n in names:
390 for n in names:
386 open(n, "w").close()
391 open(n, "w").close()
387
392
388 # Check simple completion
393 # Check simple completion
389 c = ip.complete(prefix)[1]
394 c = ip.complete(prefix)[1]
390 self.assertEqual(c, names)
395 self.assertEqual(c, names)
391
396
392 # Now check with a function call
397 # Now check with a function call
393 cmd = 'a = f("%s' % prefix
398 cmd = 'a = f("%s' % prefix
394 c = ip.complete(prefix, cmd)[1]
399 c = ip.complete(prefix, cmd)[1]
395 comp = {prefix + s for s in suffixes}
400 comp = {prefix + s for s in suffixes}
396 self.assertTrue(comp.issubset(set(c)))
401 self.assertTrue(comp.issubset(set(c)))
397
402
398 def test_quoted_file_completions(self):
403 def test_quoted_file_completions(self):
399 ip = get_ipython()
404 ip = get_ipython()
400 with TemporaryWorkingDirectory():
405 with TemporaryWorkingDirectory():
401 name = "foo'bar"
406 name = "foo'bar"
402 open(name, "w").close()
407 open(name, "w").close()
403
408
404 # Don't escape Windows
409 # Don't escape Windows
405 escaped = name if sys.platform == "win32" else "foo\\'bar"
410 escaped = name if sys.platform == "win32" else "foo\\'bar"
406
411
407 # Single quote matches embedded single quote
412 # Single quote matches embedded single quote
408 text = "open('foo"
413 text = "open('foo"
409 c = ip.Completer._complete(
414 c = ip.Completer._complete(
410 cursor_line=0, cursor_pos=len(text), full_text=text
415 cursor_line=0, cursor_pos=len(text), full_text=text
411 )[1]
416 )[1]
412 self.assertEqual(c, [escaped])
417 self.assertEqual(c, [escaped])
413
418
414 # Double quote requires no escape
419 # Double quote requires no escape
415 text = 'open("foo'
420 text = 'open("foo'
416 c = ip.Completer._complete(
421 c = ip.Completer._complete(
417 cursor_line=0, cursor_pos=len(text), full_text=text
422 cursor_line=0, cursor_pos=len(text), full_text=text
418 )[1]
423 )[1]
419 self.assertEqual(c, [name])
424 self.assertEqual(c, [name])
420
425
421 # No quote requires an escape
426 # No quote requires an escape
422 text = "%ls foo"
427 text = "%ls foo"
423 c = ip.Completer._complete(
428 c = ip.Completer._complete(
424 cursor_line=0, cursor_pos=len(text), full_text=text
429 cursor_line=0, cursor_pos=len(text), full_text=text
425 )[1]
430 )[1]
426 self.assertEqual(c, [escaped])
431 self.assertEqual(c, [escaped])
427
432
428 def test_all_completions_dups(self):
433 def test_all_completions_dups(self):
429 """
434 """
430 Make sure the output of `IPCompleter.all_completions` does not have
435 Make sure the output of `IPCompleter.all_completions` does not have
431 duplicated prefixes.
436 duplicated prefixes.
432 """
437 """
433 ip = get_ipython()
438 ip = get_ipython()
434 c = ip.Completer
439 c = ip.Completer
435 ip.ex("class TestClass():\n\ta=1\n\ta1=2")
440 ip.ex("class TestClass():\n\ta=1\n\ta1=2")
436 for jedi_status in [True, False]:
441 for jedi_status in [True, False]:
437 with provisionalcompleter():
442 with provisionalcompleter():
438 ip.Completer.use_jedi = jedi_status
443 ip.Completer.use_jedi = jedi_status
439 matches = c.all_completions("TestCl")
444 matches = c.all_completions("TestCl")
440 assert matches == ['TestClass'], jedi_status
445 assert matches == ['TestClass'], jedi_status
441 matches = c.all_completions("TestClass.")
446 matches = c.all_completions("TestClass.")
442 if jedi_status and jedi_issue:
447 if jedi_status and jedi_issue:
443 continue
448 continue
444 assert len(matches) > 2, jedi_status
449 assert len(matches) > 2, jedi_status
445 matches = c.all_completions("TestClass.a")
450 matches = c.all_completions("TestClass.a")
446 assert matches == ['TestClass.a', 'TestClass.a1'], jedi_status
451 assert matches == ['TestClass.a', 'TestClass.a1'], jedi_status
447
452
448 def test_jedi(self):
453 def test_jedi(self):
449 """
454 """
450 A couple of issue we had with Jedi
455 A couple of issue we had with Jedi
451 """
456 """
452 ip = get_ipython()
457 ip = get_ipython()
453
458
454 def _test_complete(reason, s, comp, start=None, end=None):
459 def _test_complete(reason, s, comp, start=None, end=None):
455 l = len(s)
460 l = len(s)
456 start = start if start is not None else l
461 start = start if start is not None else l
457 end = end if end is not None else l
462 end = end if end is not None else l
458 with provisionalcompleter():
463 with provisionalcompleter():
459 ip.Completer.use_jedi = True
464 ip.Completer.use_jedi = True
460 completions = set(ip.Completer.completions(s, l))
465 completions = set(ip.Completer.completions(s, l))
461 ip.Completer.use_jedi = False
466 ip.Completer.use_jedi = False
462 assert Completion(start, end, comp) in completions, reason
467 assert Completion(start, end, comp) in completions, reason
463
468
464 def _test_not_complete(reason, s, comp):
469 def _test_not_complete(reason, s, comp):
465 l = len(s)
470 l = len(s)
466 with provisionalcompleter():
471 with provisionalcompleter():
467 ip.Completer.use_jedi = True
472 ip.Completer.use_jedi = True
468 completions = set(ip.Completer.completions(s, l))
473 completions = set(ip.Completer.completions(s, l))
469 ip.Completer.use_jedi = False
474 ip.Completer.use_jedi = False
470 assert Completion(l, l, comp) not in completions, reason
475 assert Completion(l, l, comp) not in completions, reason
471
476
472 import jedi
477 import jedi
473
478
474 jedi_version = tuple(int(i) for i in jedi.__version__.split(".")[:3])
479 jedi_version = tuple(int(i) for i in jedi.__version__.split(".")[:3])
475 if jedi_version > (0, 10):
480 if jedi_version > (0, 10):
476 _test_complete("jedi >0.9 should complete and not crash", "a=1;a.", "real")
481 _test_complete("jedi >0.9 should complete and not crash", "a=1;a.", "real")
477 _test_complete("can infer first argument", 'a=(1,"foo");a[0].', "real")
482 _test_complete("can infer first argument", 'a=(1,"foo");a[0].', "real")
478 _test_complete("can infer second argument", 'a=(1,"foo");a[1].', "capitalize")
483 _test_complete("can infer second argument", 'a=(1,"foo");a[1].', "capitalize")
479 _test_complete("cover duplicate completions", "im", "import", 0, 2)
484 _test_complete("cover duplicate completions", "im", "import", 0, 2)
480
485
481 _test_not_complete("does not mix types", 'a=(1,"foo");a[0].', "capitalize")
486 _test_not_complete("does not mix types", 'a=(1,"foo");a[0].', "capitalize")
482
487
483 def test_completion_have_signature(self):
488 def test_completion_have_signature(self):
484 """
489 """
485 Lets make sure jedi is capable of pulling out the signature of the function we are completing.
490 Lets make sure jedi is capable of pulling out the signature of the function we are completing.
486 """
491 """
487 ip = get_ipython()
492 ip = get_ipython()
488 with provisionalcompleter():
493 with provisionalcompleter():
489 ip.Completer.use_jedi = True
494 ip.Completer.use_jedi = True
490 completions = ip.Completer.completions("ope", 3)
495 completions = ip.Completer.completions("ope", 3)
491 c = next(completions) # should be `open`
496 c = next(completions) # should be `open`
492 ip.Completer.use_jedi = False
497 ip.Completer.use_jedi = False
493 assert "file" in c.signature, "Signature of function was not found by completer"
498 assert "file" in c.signature, "Signature of function was not found by completer"
494 assert (
499 assert (
495 "encoding" in c.signature
500 "encoding" in c.signature
496 ), "Signature of function was not found by completer"
501 ), "Signature of function was not found by completer"
497
502
498 @pytest.mark.xfail(jedi_issue, reason="Known failure on jedi<=0.18.0")
503 @pytest.mark.xfail(jedi_issue, reason="Known failure on jedi<=0.18.0")
499 def test_deduplicate_completions(self):
504 def test_deduplicate_completions(self):
500 """
505 """
501 Test that completions are correctly deduplicated (even if ranges are not the same)
506 Test that completions are correctly deduplicated (even if ranges are not the same)
502 """
507 """
503 ip = get_ipython()
508 ip = get_ipython()
504 ip.ex(
509 ip.ex(
505 textwrap.dedent(
510 textwrap.dedent(
506 """
511 """
507 class Z:
512 class Z:
508 zoo = 1
513 zoo = 1
509 """
514 """
510 )
515 )
511 )
516 )
512 with provisionalcompleter():
517 with provisionalcompleter():
513 ip.Completer.use_jedi = True
518 ip.Completer.use_jedi = True
514 l = list(
519 l = list(
515 _deduplicate_completions("Z.z", ip.Completer.completions("Z.z", 3))
520 _deduplicate_completions("Z.z", ip.Completer.completions("Z.z", 3))
516 )
521 )
517 ip.Completer.use_jedi = False
522 ip.Completer.use_jedi = False
518
523
519 assert len(l) == 1, "Completions (Z.z<tab>) correctly deduplicate: %s " % l
524 assert len(l) == 1, "Completions (Z.z<tab>) correctly deduplicate: %s " % l
520 assert l[0].text == "zoo" # and not `it.accumulate`
525 assert l[0].text == "zoo" # and not `it.accumulate`
521
526
522 def test_greedy_completions(self):
527 def test_greedy_completions(self):
523 """
528 """
524 Test the capability of the Greedy completer.
529 Test the capability of the Greedy completer.
525
530
526 Most of the test here does not really show off the greedy completer, for proof
531 Most of the test here does not really show off the greedy completer, for proof
527 each of the text below now pass with Jedi. The greedy completer is capable of more.
532 each of the text below now pass with Jedi. The greedy completer is capable of more.
528
533
529 See the :any:`test_dict_key_completion_contexts`
534 See the :any:`test_dict_key_completion_contexts`
530
535
531 """
536 """
532 ip = get_ipython()
537 ip = get_ipython()
533 ip.ex("a=list(range(5))")
538 ip.ex("a=list(range(5))")
534 _, c = ip.complete(".", line="a[0].")
539 _, c = ip.complete(".", line="a[0].")
535 self.assertFalse(".real" in c, "Shouldn't have completed on a[0]: %s" % c)
540 self.assertFalse(".real" in c, "Shouldn't have completed on a[0]: %s" % c)
536
541
537 def _(line, cursor_pos, expect, message, completion):
542 def _(line, cursor_pos, expect, message, completion):
538 with greedy_completion(), provisionalcompleter():
543 with greedy_completion(), provisionalcompleter():
539 ip.Completer.use_jedi = False
544 ip.Completer.use_jedi = False
540 _, c = ip.complete(".", line=line, cursor_pos=cursor_pos)
545 _, c = ip.complete(".", line=line, cursor_pos=cursor_pos)
541 self.assertIn(expect, c, message % c)
546 self.assertIn(expect, c, message % c)
542
547
543 ip.Completer.use_jedi = True
548 ip.Completer.use_jedi = True
544 with provisionalcompleter():
549 with provisionalcompleter():
545 completions = ip.Completer.completions(line, cursor_pos)
550 completions = ip.Completer.completions(line, cursor_pos)
546 self.assertIn(completion, completions)
551 self.assertIn(completion, completions)
547
552
548 with provisionalcompleter():
553 with provisionalcompleter():
549 _(
554 _(
550 "a[0].",
555 "a[0].",
551 5,
556 5,
552 "a[0].real",
557 "a[0].real",
553 "Should have completed on a[0].: %s",
558 "Should have completed on a[0].: %s",
554 Completion(5, 5, "real"),
559 Completion(5, 5, "real"),
555 )
560 )
556 _(
561 _(
557 "a[0].r",
562 "a[0].r",
558 6,
563 6,
559 "a[0].real",
564 "a[0].real",
560 "Should have completed on a[0].r: %s",
565 "Should have completed on a[0].r: %s",
561 Completion(5, 6, "real"),
566 Completion(5, 6, "real"),
562 )
567 )
563
568
564 _(
569 _(
565 "a[0].from_",
570 "a[0].from_",
566 10,
571 10,
567 "a[0].from_bytes",
572 "a[0].from_bytes",
568 "Should have completed on a[0].from_: %s",
573 "Should have completed on a[0].from_: %s",
569 Completion(5, 10, "from_bytes"),
574 Completion(5, 10, "from_bytes"),
570 )
575 )
571
576
572 def test_omit__names(self):
577 def test_omit__names(self):
573 # also happens to test IPCompleter as a configurable
578 # also happens to test IPCompleter as a configurable
574 ip = get_ipython()
579 ip = get_ipython()
575 ip._hidden_attr = 1
580 ip._hidden_attr = 1
576 ip._x = {}
581 ip._x = {}
577 c = ip.Completer
582 c = ip.Completer
578 ip.ex("ip=get_ipython()")
583 ip.ex("ip=get_ipython()")
579 cfg = Config()
584 cfg = Config()
580 cfg.IPCompleter.omit__names = 0
585 cfg.IPCompleter.omit__names = 0
581 c.update_config(cfg)
586 c.update_config(cfg)
582 with provisionalcompleter():
587 with provisionalcompleter():
583 c.use_jedi = False
588 c.use_jedi = False
584 s, matches = c.complete("ip.")
589 s, matches = c.complete("ip.")
585 self.assertIn("ip.__str__", matches)
590 self.assertIn("ip.__str__", matches)
586 self.assertIn("ip._hidden_attr", matches)
591 self.assertIn("ip._hidden_attr", matches)
587
592
588 # c.use_jedi = True
593 # c.use_jedi = True
589 # completions = set(c.completions('ip.', 3))
594 # completions = set(c.completions('ip.', 3))
590 # self.assertIn(Completion(3, 3, '__str__'), completions)
595 # self.assertIn(Completion(3, 3, '__str__'), completions)
591 # self.assertIn(Completion(3,3, "_hidden_attr"), completions)
596 # self.assertIn(Completion(3,3, "_hidden_attr"), completions)
592
597
593 cfg = Config()
598 cfg = Config()
594 cfg.IPCompleter.omit__names = 1
599 cfg.IPCompleter.omit__names = 1
595 c.update_config(cfg)
600 c.update_config(cfg)
596 with provisionalcompleter():
601 with provisionalcompleter():
597 c.use_jedi = False
602 c.use_jedi = False
598 s, matches = c.complete("ip.")
603 s, matches = c.complete("ip.")
599 self.assertNotIn("ip.__str__", matches)
604 self.assertNotIn("ip.__str__", matches)
600 # self.assertIn('ip._hidden_attr', matches)
605 # self.assertIn('ip._hidden_attr', matches)
601
606
602 # c.use_jedi = True
607 # c.use_jedi = True
603 # completions = set(c.completions('ip.', 3))
608 # completions = set(c.completions('ip.', 3))
604 # self.assertNotIn(Completion(3,3,'__str__'), completions)
609 # self.assertNotIn(Completion(3,3,'__str__'), completions)
605 # self.assertIn(Completion(3,3, "_hidden_attr"), completions)
610 # self.assertIn(Completion(3,3, "_hidden_attr"), completions)
606
611
607 cfg = Config()
612 cfg = Config()
608 cfg.IPCompleter.omit__names = 2
613 cfg.IPCompleter.omit__names = 2
609 c.update_config(cfg)
614 c.update_config(cfg)
610 with provisionalcompleter():
615 with provisionalcompleter():
611 c.use_jedi = False
616 c.use_jedi = False
612 s, matches = c.complete("ip.")
617 s, matches = c.complete("ip.")
613 self.assertNotIn("ip.__str__", matches)
618 self.assertNotIn("ip.__str__", matches)
614 self.assertNotIn("ip._hidden_attr", matches)
619 self.assertNotIn("ip._hidden_attr", matches)
615
620
616 # c.use_jedi = True
621 # c.use_jedi = True
617 # completions = set(c.completions('ip.', 3))
622 # completions = set(c.completions('ip.', 3))
618 # self.assertNotIn(Completion(3,3,'__str__'), completions)
623 # self.assertNotIn(Completion(3,3,'__str__'), completions)
619 # self.assertNotIn(Completion(3,3, "_hidden_attr"), completions)
624 # self.assertNotIn(Completion(3,3, "_hidden_attr"), completions)
620
625
621 with provisionalcompleter():
626 with provisionalcompleter():
622 c.use_jedi = False
627 c.use_jedi = False
623 s, matches = c.complete("ip._x.")
628 s, matches = c.complete("ip._x.")
624 self.assertIn("ip._x.keys", matches)
629 self.assertIn("ip._x.keys", matches)
625
630
626 # c.use_jedi = True
631 # c.use_jedi = True
627 # completions = set(c.completions('ip._x.', 6))
632 # completions = set(c.completions('ip._x.', 6))
628 # self.assertIn(Completion(6,6, "keys"), completions)
633 # self.assertIn(Completion(6,6, "keys"), completions)
629
634
630 del ip._hidden_attr
635 del ip._hidden_attr
631 del ip._x
636 del ip._x
632
637
633 def test_limit_to__all__False_ok(self):
638 def test_limit_to__all__False_ok(self):
634 """
639 """
635 Limit to all is deprecated, once we remove it this test can go away.
640 Limit to all is deprecated, once we remove it this test can go away.
636 """
641 """
637 ip = get_ipython()
642 ip = get_ipython()
638 c = ip.Completer
643 c = ip.Completer
639 c.use_jedi = False
644 c.use_jedi = False
640 ip.ex("class D: x=24")
645 ip.ex("class D: x=24")
641 ip.ex("d=D()")
646 ip.ex("d=D()")
642 cfg = Config()
647 cfg = Config()
643 cfg.IPCompleter.limit_to__all__ = False
648 cfg.IPCompleter.limit_to__all__ = False
644 c.update_config(cfg)
649 c.update_config(cfg)
645 s, matches = c.complete("d.")
650 s, matches = c.complete("d.")
646 self.assertIn("d.x", matches)
651 self.assertIn("d.x", matches)
647
652
648 def test_get__all__entries_ok(self):
653 def test_get__all__entries_ok(self):
649 class A:
654 class A:
650 __all__ = ["x", 1]
655 __all__ = ["x", 1]
651
656
652 words = completer.get__all__entries(A())
657 words = completer.get__all__entries(A())
653 self.assertEqual(words, ["x"])
658 self.assertEqual(words, ["x"])
654
659
655 def test_get__all__entries_no__all__ok(self):
660 def test_get__all__entries_no__all__ok(self):
656 class A:
661 class A:
657 pass
662 pass
658
663
659 words = completer.get__all__entries(A())
664 words = completer.get__all__entries(A())
660 self.assertEqual(words, [])
665 self.assertEqual(words, [])
661
666
662 def test_func_kw_completions(self):
667 def test_func_kw_completions(self):
663 ip = get_ipython()
668 ip = get_ipython()
664 c = ip.Completer
669 c = ip.Completer
665 c.use_jedi = False
670 c.use_jedi = False
666 ip.ex("def myfunc(a=1,b=2): return a+b")
671 ip.ex("def myfunc(a=1,b=2): return a+b")
667 s, matches = c.complete(None, "myfunc(1,b")
672 s, matches = c.complete(None, "myfunc(1,b")
668 self.assertIn("b=", matches)
673 self.assertIn("b=", matches)
669 # Simulate completing with cursor right after b (pos==10):
674 # Simulate completing with cursor right after b (pos==10):
670 s, matches = c.complete(None, "myfunc(1,b)", 10)
675 s, matches = c.complete(None, "myfunc(1,b)", 10)
671 self.assertIn("b=", matches)
676 self.assertIn("b=", matches)
672 s, matches = c.complete(None, 'myfunc(a="escaped\\")string",b')
677 s, matches = c.complete(None, 'myfunc(a="escaped\\")string",b')
673 self.assertIn("b=", matches)
678 self.assertIn("b=", matches)
674 # builtin function
679 # builtin function
675 s, matches = c.complete(None, "min(k, k")
680 s, matches = c.complete(None, "min(k, k")
676 self.assertIn("key=", matches)
681 self.assertIn("key=", matches)
677
682
678 def test_default_arguments_from_docstring(self):
683 def test_default_arguments_from_docstring(self):
679 ip = get_ipython()
684 ip = get_ipython()
680 c = ip.Completer
685 c = ip.Completer
681 kwd = c._default_arguments_from_docstring("min(iterable[, key=func]) -> value")
686 kwd = c._default_arguments_from_docstring("min(iterable[, key=func]) -> value")
682 self.assertEqual(kwd, ["key"])
687 self.assertEqual(kwd, ["key"])
683 # with cython type etc
688 # with cython type etc
684 kwd = c._default_arguments_from_docstring(
689 kwd = c._default_arguments_from_docstring(
685 "Minuit.migrad(self, int ncall=10000, resume=True, int nsplit=1)\n"
690 "Minuit.migrad(self, int ncall=10000, resume=True, int nsplit=1)\n"
686 )
691 )
687 self.assertEqual(kwd, ["ncall", "resume", "nsplit"])
692 self.assertEqual(kwd, ["ncall", "resume", "nsplit"])
688 # white spaces
693 # white spaces
689 kwd = c._default_arguments_from_docstring(
694 kwd = c._default_arguments_from_docstring(
690 "\n Minuit.migrad(self, int ncall=10000, resume=True, int nsplit=1)\n"
695 "\n Minuit.migrad(self, int ncall=10000, resume=True, int nsplit=1)\n"
691 )
696 )
692 self.assertEqual(kwd, ["ncall", "resume", "nsplit"])
697 self.assertEqual(kwd, ["ncall", "resume", "nsplit"])
693
698
694 def test_line_magics(self):
699 def test_line_magics(self):
695 ip = get_ipython()
700 ip = get_ipython()
696 c = ip.Completer
701 c = ip.Completer
697 s, matches = c.complete(None, "lsmag")
702 s, matches = c.complete(None, "lsmag")
698 self.assertIn("%lsmagic", matches)
703 self.assertIn("%lsmagic", matches)
699 s, matches = c.complete(None, "%lsmag")
704 s, matches = c.complete(None, "%lsmag")
700 self.assertIn("%lsmagic", matches)
705 self.assertIn("%lsmagic", matches)
701
706
702 def test_cell_magics(self):
707 def test_cell_magics(self):
703 from IPython.core.magic import register_cell_magic
708 from IPython.core.magic import register_cell_magic
704
709
705 @register_cell_magic
710 @register_cell_magic
706 def _foo_cellm(line, cell):
711 def _foo_cellm(line, cell):
707 pass
712 pass
708
713
709 ip = get_ipython()
714 ip = get_ipython()
710 c = ip.Completer
715 c = ip.Completer
711
716
712 s, matches = c.complete(None, "_foo_ce")
717 s, matches = c.complete(None, "_foo_ce")
713 self.assertIn("%%_foo_cellm", matches)
718 self.assertIn("%%_foo_cellm", matches)
714 s, matches = c.complete(None, "%%_foo_ce")
719 s, matches = c.complete(None, "%%_foo_ce")
715 self.assertIn("%%_foo_cellm", matches)
720 self.assertIn("%%_foo_cellm", matches)
716
721
717 def test_line_cell_magics(self):
722 def test_line_cell_magics(self):
718 from IPython.core.magic import register_line_cell_magic
723 from IPython.core.magic import register_line_cell_magic
719
724
720 @register_line_cell_magic
725 @register_line_cell_magic
721 def _bar_cellm(line, cell):
726 def _bar_cellm(line, cell):
722 pass
727 pass
723
728
724 ip = get_ipython()
729 ip = get_ipython()
725 c = ip.Completer
730 c = ip.Completer
726
731
727 # The policy here is trickier, see comments in completion code. The
732 # The policy here is trickier, see comments in completion code. The
728 # returned values depend on whether the user passes %% or not explicitly,
733 # returned values depend on whether the user passes %% or not explicitly,
729 # and this will show a difference if the same name is both a line and cell
734 # and this will show a difference if the same name is both a line and cell
730 # magic.
735 # magic.
731 s, matches = c.complete(None, "_bar_ce")
736 s, matches = c.complete(None, "_bar_ce")
732 self.assertIn("%_bar_cellm", matches)
737 self.assertIn("%_bar_cellm", matches)
733 self.assertIn("%%_bar_cellm", matches)
738 self.assertIn("%%_bar_cellm", matches)
734 s, matches = c.complete(None, "%_bar_ce")
739 s, matches = c.complete(None, "%_bar_ce")
735 self.assertIn("%_bar_cellm", matches)
740 self.assertIn("%_bar_cellm", matches)
736 self.assertIn("%%_bar_cellm", matches)
741 self.assertIn("%%_bar_cellm", matches)
737 s, matches = c.complete(None, "%%_bar_ce")
742 s, matches = c.complete(None, "%%_bar_ce")
738 self.assertNotIn("%_bar_cellm", matches)
743 self.assertNotIn("%_bar_cellm", matches)
739 self.assertIn("%%_bar_cellm", matches)
744 self.assertIn("%%_bar_cellm", matches)
740
745
741 def test_magic_completion_order(self):
746 def test_magic_completion_order(self):
742 ip = get_ipython()
747 ip = get_ipython()
743 c = ip.Completer
748 c = ip.Completer
744
749
745 # Test ordering of line and cell magics.
750 # Test ordering of line and cell magics.
746 text, matches = c.complete("timeit")
751 text, matches = c.complete("timeit")
747 self.assertEqual(matches, ["%timeit", "%%timeit"])
752 self.assertEqual(matches, ["%timeit", "%%timeit"])
748
753
749 def test_magic_completion_shadowing(self):
754 def test_magic_completion_shadowing(self):
750 ip = get_ipython()
755 ip = get_ipython()
751 c = ip.Completer
756 c = ip.Completer
752 c.use_jedi = False
757 c.use_jedi = False
753
758
754 # Before importing matplotlib, %matplotlib magic should be the only option.
759 # Before importing matplotlib, %matplotlib magic should be the only option.
755 text, matches = c.complete("mat")
760 text, matches = c.complete("mat")
756 self.assertEqual(matches, ["%matplotlib"])
761 self.assertEqual(matches, ["%matplotlib"])
757
762
758 # The newly introduced name should shadow the magic.
763 # The newly introduced name should shadow the magic.
759 ip.run_cell("matplotlib = 1")
764 ip.run_cell("matplotlib = 1")
760 text, matches = c.complete("mat")
765 text, matches = c.complete("mat")
761 self.assertEqual(matches, ["matplotlib"])
766 self.assertEqual(matches, ["matplotlib"])
762
767
763 # After removing matplotlib from namespace, the magic should again be
768 # After removing matplotlib from namespace, the magic should again be
764 # the only option.
769 # the only option.
765 del ip.user_ns["matplotlib"]
770 del ip.user_ns["matplotlib"]
766 text, matches = c.complete("mat")
771 text, matches = c.complete("mat")
767 self.assertEqual(matches, ["%matplotlib"])
772 self.assertEqual(matches, ["%matplotlib"])
768
773
769 def test_magic_completion_shadowing_explicit(self):
774 def test_magic_completion_shadowing_explicit(self):
770 """
775 """
771 If the user try to complete a shadowed magic, and explicit % start should
776 If the user try to complete a shadowed magic, and explicit % start should
772 still return the completions.
777 still return the completions.
773 """
778 """
774 ip = get_ipython()
779 ip = get_ipython()
775 c = ip.Completer
780 c = ip.Completer
776
781
777 # Before importing matplotlib, %matplotlib magic should be the only option.
782 # Before importing matplotlib, %matplotlib magic should be the only option.
778 text, matches = c.complete("%mat")
783 text, matches = c.complete("%mat")
779 self.assertEqual(matches, ["%matplotlib"])
784 self.assertEqual(matches, ["%matplotlib"])
780
785
781 ip.run_cell("matplotlib = 1")
786 ip.run_cell("matplotlib = 1")
782
787
783 # After removing matplotlib from namespace, the magic should still be
788 # After removing matplotlib from namespace, the magic should still be
784 # the only option.
789 # the only option.
785 text, matches = c.complete("%mat")
790 text, matches = c.complete("%mat")
786 self.assertEqual(matches, ["%matplotlib"])
791 self.assertEqual(matches, ["%matplotlib"])
787
792
788 def test_magic_config(self):
793 def test_magic_config(self):
789 ip = get_ipython()
794 ip = get_ipython()
790 c = ip.Completer
795 c = ip.Completer
791
796
792 s, matches = c.complete(None, "conf")
797 s, matches = c.complete(None, "conf")
793 self.assertIn("%config", matches)
798 self.assertIn("%config", matches)
794 s, matches = c.complete(None, "conf")
799 s, matches = c.complete(None, "conf")
795 self.assertNotIn("AliasManager", matches)
800 self.assertNotIn("AliasManager", matches)
796 s, matches = c.complete(None, "config ")
801 s, matches = c.complete(None, "config ")
797 self.assertIn("AliasManager", matches)
802 self.assertIn("AliasManager", matches)
798 s, matches = c.complete(None, "%config ")
803 s, matches = c.complete(None, "%config ")
799 self.assertIn("AliasManager", matches)
804 self.assertIn("AliasManager", matches)
800 s, matches = c.complete(None, "config Ali")
805 s, matches = c.complete(None, "config Ali")
801 self.assertListEqual(["AliasManager"], matches)
806 self.assertListEqual(["AliasManager"], matches)
802 s, matches = c.complete(None, "%config Ali")
807 s, matches = c.complete(None, "%config Ali")
803 self.assertListEqual(["AliasManager"], matches)
808 self.assertListEqual(["AliasManager"], matches)
804 s, matches = c.complete(None, "config AliasManager")
809 s, matches = c.complete(None, "config AliasManager")
805 self.assertListEqual(["AliasManager"], matches)
810 self.assertListEqual(["AliasManager"], matches)
806 s, matches = c.complete(None, "%config AliasManager")
811 s, matches = c.complete(None, "%config AliasManager")
807 self.assertListEqual(["AliasManager"], matches)
812 self.assertListEqual(["AliasManager"], matches)
808 s, matches = c.complete(None, "config AliasManager.")
813 s, matches = c.complete(None, "config AliasManager.")
809 self.assertIn("AliasManager.default_aliases", matches)
814 self.assertIn("AliasManager.default_aliases", matches)
810 s, matches = c.complete(None, "%config AliasManager.")
815 s, matches = c.complete(None, "%config AliasManager.")
811 self.assertIn("AliasManager.default_aliases", matches)
816 self.assertIn("AliasManager.default_aliases", matches)
812 s, matches = c.complete(None, "config AliasManager.de")
817 s, matches = c.complete(None, "config AliasManager.de")
813 self.assertListEqual(["AliasManager.default_aliases"], matches)
818 self.assertListEqual(["AliasManager.default_aliases"], matches)
814 s, matches = c.complete(None, "config AliasManager.de")
819 s, matches = c.complete(None, "config AliasManager.de")
815 self.assertListEqual(["AliasManager.default_aliases"], matches)
820 self.assertListEqual(["AliasManager.default_aliases"], matches)
816
821
817 def test_magic_color(self):
822 def test_magic_color(self):
818 ip = get_ipython()
823 ip = get_ipython()
819 c = ip.Completer
824 c = ip.Completer
820
825
821 s, matches = c.complete(None, "colo")
826 s, matches = c.complete(None, "colo")
822 self.assertIn("%colors", matches)
827 self.assertIn("%colors", matches)
823 s, matches = c.complete(None, "colo")
828 s, matches = c.complete(None, "colo")
824 self.assertNotIn("NoColor", matches)
829 self.assertNotIn("NoColor", matches)
825 s, matches = c.complete(None, "%colors") # No trailing space
830 s, matches = c.complete(None, "%colors") # No trailing space
826 self.assertNotIn("NoColor", matches)
831 self.assertNotIn("NoColor", matches)
827 s, matches = c.complete(None, "colors ")
832 s, matches = c.complete(None, "colors ")
828 self.assertIn("NoColor", matches)
833 self.assertIn("NoColor", matches)
829 s, matches = c.complete(None, "%colors ")
834 s, matches = c.complete(None, "%colors ")
830 self.assertIn("NoColor", matches)
835 self.assertIn("NoColor", matches)
831 s, matches = c.complete(None, "colors NoCo")
836 s, matches = c.complete(None, "colors NoCo")
832 self.assertListEqual(["NoColor"], matches)
837 self.assertListEqual(["NoColor"], matches)
833 s, matches = c.complete(None, "%colors NoCo")
838 s, matches = c.complete(None, "%colors NoCo")
834 self.assertListEqual(["NoColor"], matches)
839 self.assertListEqual(["NoColor"], matches)
835
840
836 def test_match_dict_keys(self):
841 def test_match_dict_keys(self):
837 """
842 """
838 Test that match_dict_keys works on a couple of use case does return what
843 Test that match_dict_keys works on a couple of use case does return what
839 expected, and does not crash
844 expected, and does not crash
840 """
845 """
841 delims = " \t\n`!@#$^&*()=+[{]}\\|;:'\",<>?"
846 delims = " \t\n`!@#$^&*()=+[{]}\\|;:'\",<>?"
842
847
843 keys = ["foo", b"far"]
848 keys = ["foo", b"far"]
844 assert match_dict_keys(keys, "b'", delims=delims) == ("'", 2, ["far"])
849 assert match_dict_keys(keys, "b'", delims=delims) == ("'", 2, ["far"])
845 assert match_dict_keys(keys, "b'f", delims=delims) == ("'", 2, ["far"])
850 assert match_dict_keys(keys, "b'f", delims=delims) == ("'", 2, ["far"])
846 assert match_dict_keys(keys, 'b"', delims=delims) == ('"', 2, ["far"])
851 assert match_dict_keys(keys, 'b"', delims=delims) == ('"', 2, ["far"])
847 assert match_dict_keys(keys, 'b"f', delims=delims) == ('"', 2, ["far"])
852 assert match_dict_keys(keys, 'b"f', delims=delims) == ('"', 2, ["far"])
848
853
849 assert match_dict_keys(keys, "'", delims=delims) == ("'", 1, ["foo"])
854 assert match_dict_keys(keys, "'", delims=delims) == ("'", 1, ["foo"])
850 assert match_dict_keys(keys, "'f", delims=delims) == ("'", 1, ["foo"])
855 assert match_dict_keys(keys, "'f", delims=delims) == ("'", 1, ["foo"])
851 assert match_dict_keys(keys, '"', delims=delims) == ('"', 1, ["foo"])
856 assert match_dict_keys(keys, '"', delims=delims) == ('"', 1, ["foo"])
852 assert match_dict_keys(keys, '"f', delims=delims) == ('"', 1, ["foo"])
857 assert match_dict_keys(keys, '"f', delims=delims) == ('"', 1, ["foo"])
853
858
854 match_dict_keys
859 match_dict_keys
855
860
856 def test_match_dict_keys_tuple(self):
861 def test_match_dict_keys_tuple(self):
857 """
862 """
858 Test that match_dict_keys called with extra prefix works on a couple of use case,
863 Test that match_dict_keys called with extra prefix works on a couple of use case,
859 does return what expected, and does not crash.
864 does return what expected, and does not crash.
860 """
865 """
861 delims = " \t\n`!@#$^&*()=+[{]}\\|;:'\",<>?"
866 delims = " \t\n`!@#$^&*()=+[{]}\\|;:'\",<>?"
862
867
863 keys = [("foo", "bar"), ("foo", "oof"), ("foo", b"bar"), ('other', 'test')]
868 keys = [("foo", "bar"), ("foo", "oof"), ("foo", b"bar"), ('other', 'test')]
864
869
865 # Completion on first key == "foo"
870 # Completion on first key == "foo"
866 assert match_dict_keys(keys, "'", delims=delims, extra_prefix=("foo",)) == ("'", 1, ["bar", "oof"])
871 assert match_dict_keys(keys, "'", delims=delims, extra_prefix=("foo",)) == ("'", 1, ["bar", "oof"])
867 assert match_dict_keys(keys, "\"", delims=delims, extra_prefix=("foo",)) == ("\"", 1, ["bar", "oof"])
872 assert match_dict_keys(keys, "\"", delims=delims, extra_prefix=("foo",)) == ("\"", 1, ["bar", "oof"])
868 assert match_dict_keys(keys, "'o", delims=delims, extra_prefix=("foo",)) == ("'", 1, ["oof"])
873 assert match_dict_keys(keys, "'o", delims=delims, extra_prefix=("foo",)) == ("'", 1, ["oof"])
869 assert match_dict_keys(keys, "\"o", delims=delims, extra_prefix=("foo",)) == ("\"", 1, ["oof"])
874 assert match_dict_keys(keys, "\"o", delims=delims, extra_prefix=("foo",)) == ("\"", 1, ["oof"])
870 assert match_dict_keys(keys, "b'", delims=delims, extra_prefix=("foo",)) == ("'", 2, ["bar"])
875 assert match_dict_keys(keys, "b'", delims=delims, extra_prefix=("foo",)) == ("'", 2, ["bar"])
871 assert match_dict_keys(keys, "b\"", delims=delims, extra_prefix=("foo",)) == ("\"", 2, ["bar"])
876 assert match_dict_keys(keys, "b\"", delims=delims, extra_prefix=("foo",)) == ("\"", 2, ["bar"])
872 assert match_dict_keys(keys, "b'b", delims=delims, extra_prefix=("foo",)) == ("'", 2, ["bar"])
877 assert match_dict_keys(keys, "b'b", delims=delims, extra_prefix=("foo",)) == ("'", 2, ["bar"])
873 assert match_dict_keys(keys, "b\"b", delims=delims, extra_prefix=("foo",)) == ("\"", 2, ["bar"])
878 assert match_dict_keys(keys, "b\"b", delims=delims, extra_prefix=("foo",)) == ("\"", 2, ["bar"])
874
879
875 # No Completion
880 # No Completion
876 assert match_dict_keys(keys, "'", delims=delims, extra_prefix=("no_foo",)) == ("'", 1, [])
881 assert match_dict_keys(keys, "'", delims=delims, extra_prefix=("no_foo",)) == ("'", 1, [])
877 assert match_dict_keys(keys, "'", delims=delims, extra_prefix=("fo",)) == ("'", 1, [])
882 assert match_dict_keys(keys, "'", delims=delims, extra_prefix=("fo",)) == ("'", 1, [])
878
883
879 keys = [('foo1', 'foo2', 'foo3', 'foo4'), ('foo1', 'foo2', 'bar', 'foo4')]
884 keys = [('foo1', 'foo2', 'foo3', 'foo4'), ('foo1', 'foo2', 'bar', 'foo4')]
880 assert match_dict_keys(keys, "'foo", delims=delims, extra_prefix=('foo1',)) == ("'", 1, ["foo2", "foo2"])
885 assert match_dict_keys(keys, "'foo", delims=delims, extra_prefix=('foo1',)) == ("'", 1, ["foo2", "foo2"])
881 assert match_dict_keys(keys, "'foo", delims=delims, extra_prefix=('foo1', 'foo2')) == ("'", 1, ["foo3"])
886 assert match_dict_keys(keys, "'foo", delims=delims, extra_prefix=('foo1', 'foo2')) == ("'", 1, ["foo3"])
882 assert match_dict_keys(keys, "'foo", delims=delims, extra_prefix=('foo1', 'foo2', 'foo3')) == ("'", 1, ["foo4"])
887 assert match_dict_keys(keys, "'foo", delims=delims, extra_prefix=('foo1', 'foo2', 'foo3')) == ("'", 1, ["foo4"])
883 assert match_dict_keys(keys, "'foo", delims=delims, extra_prefix=('foo1', 'foo2', 'foo3', 'foo4')) == ("'", 1, [])
888 assert match_dict_keys(keys, "'foo", delims=delims, extra_prefix=('foo1', 'foo2', 'foo3', 'foo4')) == ("'", 1, [])
884
889
885 def test_dict_key_completion_string(self):
890 def test_dict_key_completion_string(self):
886 """Test dictionary key completion for string keys"""
891 """Test dictionary key completion for string keys"""
887 ip = get_ipython()
892 ip = get_ipython()
888 complete = ip.Completer.complete
893 complete = ip.Completer.complete
889
894
890 ip.user_ns["d"] = {"abc": None}
895 ip.user_ns["d"] = {"abc": None}
891
896
892 # check completion at different stages
897 # check completion at different stages
893 _, matches = complete(line_buffer="d[")
898 _, matches = complete(line_buffer="d[")
894 self.assertIn("'abc'", matches)
899 self.assertIn("'abc'", matches)
895 self.assertNotIn("'abc']", matches)
900 self.assertNotIn("'abc']", matches)
896
901
897 _, matches = complete(line_buffer="d['")
902 _, matches = complete(line_buffer="d['")
898 self.assertIn("abc", matches)
903 self.assertIn("abc", matches)
899 self.assertNotIn("abc']", matches)
904 self.assertNotIn("abc']", matches)
900
905
901 _, matches = complete(line_buffer="d['a")
906 _, matches = complete(line_buffer="d['a")
902 self.assertIn("abc", matches)
907 self.assertIn("abc", matches)
903 self.assertNotIn("abc']", matches)
908 self.assertNotIn("abc']", matches)
904
909
905 # check use of different quoting
910 # check use of different quoting
906 _, matches = complete(line_buffer='d["')
911 _, matches = complete(line_buffer='d["')
907 self.assertIn("abc", matches)
912 self.assertIn("abc", matches)
908 self.assertNotIn('abc"]', matches)
913 self.assertNotIn('abc"]', matches)
909
914
910 _, matches = complete(line_buffer='d["a')
915 _, matches = complete(line_buffer='d["a')
911 self.assertIn("abc", matches)
916 self.assertIn("abc", matches)
912 self.assertNotIn('abc"]', matches)
917 self.assertNotIn('abc"]', matches)
913
918
914 # check sensitivity to following context
919 # check sensitivity to following context
915 _, matches = complete(line_buffer="d[]", cursor_pos=2)
920 _, matches = complete(line_buffer="d[]", cursor_pos=2)
916 self.assertIn("'abc'", matches)
921 self.assertIn("'abc'", matches)
917
922
918 _, matches = complete(line_buffer="d['']", cursor_pos=3)
923 _, matches = complete(line_buffer="d['']", cursor_pos=3)
919 self.assertIn("abc", matches)
924 self.assertIn("abc", matches)
920 self.assertNotIn("abc'", matches)
925 self.assertNotIn("abc'", matches)
921 self.assertNotIn("abc']", matches)
926 self.assertNotIn("abc']", matches)
922
927
923 # check multiple solutions are correctly returned and that noise is not
928 # check multiple solutions are correctly returned and that noise is not
924 ip.user_ns["d"] = {
929 ip.user_ns["d"] = {
925 "abc": None,
930 "abc": None,
926 "abd": None,
931 "abd": None,
927 "bad": None,
932 "bad": None,
928 object(): None,
933 object(): None,
929 5: None,
934 5: None,
930 ("abe", None): None,
935 ("abe", None): None,
931 (None, "abf"): None
936 (None, "abf"): None
932 }
937 }
933
938
934 _, matches = complete(line_buffer="d['a")
939 _, matches = complete(line_buffer="d['a")
935 self.assertIn("abc", matches)
940 self.assertIn("abc", matches)
936 self.assertIn("abd", matches)
941 self.assertIn("abd", matches)
937 self.assertNotIn("bad", matches)
942 self.assertNotIn("bad", matches)
938 self.assertNotIn("abe", matches)
943 self.assertNotIn("abe", matches)
939 self.assertNotIn("abf", matches)
944 self.assertNotIn("abf", matches)
940 assert not any(m.endswith(("]", '"', "'")) for m in matches), matches
945 assert not any(m.endswith(("]", '"', "'")) for m in matches), matches
941
946
942 # check escaping and whitespace
947 # check escaping and whitespace
943 ip.user_ns["d"] = {"a\nb": None, "a'b": None, 'a"b': None, "a word": None}
948 ip.user_ns["d"] = {"a\nb": None, "a'b": None, 'a"b': None, "a word": None}
944 _, matches = complete(line_buffer="d['a")
949 _, matches = complete(line_buffer="d['a")
945 self.assertIn("a\\nb", matches)
950 self.assertIn("a\\nb", matches)
946 self.assertIn("a\\'b", matches)
951 self.assertIn("a\\'b", matches)
947 self.assertIn('a"b', matches)
952 self.assertIn('a"b', matches)
948 self.assertIn("a word", matches)
953 self.assertIn("a word", matches)
949 assert not any(m.endswith(("]", '"', "'")) for m in matches), matches
954 assert not any(m.endswith(("]", '"', "'")) for m in matches), matches
950
955
951 # - can complete on non-initial word of the string
956 # - can complete on non-initial word of the string
952 _, matches = complete(line_buffer="d['a w")
957 _, matches = complete(line_buffer="d['a w")
953 self.assertIn("word", matches)
958 self.assertIn("word", matches)
954
959
955 # - understands quote escaping
960 # - understands quote escaping
956 _, matches = complete(line_buffer="d['a\\'")
961 _, matches = complete(line_buffer="d['a\\'")
957 self.assertIn("b", matches)
962 self.assertIn("b", matches)
958
963
959 # - default quoting should work like repr
964 # - default quoting should work like repr
960 _, matches = complete(line_buffer="d[")
965 _, matches = complete(line_buffer="d[")
961 self.assertIn('"a\'b"', matches)
966 self.assertIn('"a\'b"', matches)
962
967
963 # - when opening quote with ", possible to match with unescaped apostrophe
968 # - when opening quote with ", possible to match with unescaped apostrophe
964 _, matches = complete(line_buffer="d[\"a'")
969 _, matches = complete(line_buffer="d[\"a'")
965 self.assertIn("b", matches)
970 self.assertIn("b", matches)
966
971
967 # need to not split at delims that readline won't split at
972 # need to not split at delims that readline won't split at
968 if "-" not in ip.Completer.splitter.delims:
973 if "-" not in ip.Completer.splitter.delims:
969 ip.user_ns["d"] = {"before-after": None}
974 ip.user_ns["d"] = {"before-after": None}
970 _, matches = complete(line_buffer="d['before-af")
975 _, matches = complete(line_buffer="d['before-af")
971 self.assertIn("before-after", matches)
976 self.assertIn("before-after", matches)
972
977
973 # check completion on tuple-of-string keys at different stage - on first key
978 # check completion on tuple-of-string keys at different stage - on first key
974 ip.user_ns["d"] = {('foo', 'bar'): None}
979 ip.user_ns["d"] = {('foo', 'bar'): None}
975 _, matches = complete(line_buffer="d[")
980 _, matches = complete(line_buffer="d[")
976 self.assertIn("'foo'", matches)
981 self.assertIn("'foo'", matches)
977 self.assertNotIn("'foo']", matches)
982 self.assertNotIn("'foo']", matches)
978 self.assertNotIn("'bar'", matches)
983 self.assertNotIn("'bar'", matches)
979 self.assertNotIn("foo", matches)
984 self.assertNotIn("foo", matches)
980 self.assertNotIn("bar", matches)
985 self.assertNotIn("bar", matches)
981
986
982 # - match the prefix
987 # - match the prefix
983 _, matches = complete(line_buffer="d['f")
988 _, matches = complete(line_buffer="d['f")
984 self.assertIn("foo", matches)
989 self.assertIn("foo", matches)
985 self.assertNotIn("foo']", matches)
990 self.assertNotIn("foo']", matches)
986 self.assertNotIn('foo"]', matches)
991 self.assertNotIn('foo"]', matches)
987 _, matches = complete(line_buffer="d['foo")
992 _, matches = complete(line_buffer="d['foo")
988 self.assertIn("foo", matches)
993 self.assertIn("foo", matches)
989
994
990 # - can complete on second key
995 # - can complete on second key
991 _, matches = complete(line_buffer="d['foo', ")
996 _, matches = complete(line_buffer="d['foo', ")
992 self.assertIn("'bar'", matches)
997 self.assertIn("'bar'", matches)
993 _, matches = complete(line_buffer="d['foo', 'b")
998 _, matches = complete(line_buffer="d['foo', 'b")
994 self.assertIn("bar", matches)
999 self.assertIn("bar", matches)
995 self.assertNotIn("foo", matches)
1000 self.assertNotIn("foo", matches)
996
1001
997 # - does not propose missing keys
1002 # - does not propose missing keys
998 _, matches = complete(line_buffer="d['foo', 'f")
1003 _, matches = complete(line_buffer="d['foo', 'f")
999 self.assertNotIn("bar", matches)
1004 self.assertNotIn("bar", matches)
1000 self.assertNotIn("foo", matches)
1005 self.assertNotIn("foo", matches)
1001
1006
1002 # check sensitivity to following context
1007 # check sensitivity to following context
1003 _, matches = complete(line_buffer="d['foo',]", cursor_pos=8)
1008 _, matches = complete(line_buffer="d['foo',]", cursor_pos=8)
1004 self.assertIn("'bar'", matches)
1009 self.assertIn("'bar'", matches)
1005 self.assertNotIn("bar", matches)
1010 self.assertNotIn("bar", matches)
1006 self.assertNotIn("'foo'", matches)
1011 self.assertNotIn("'foo'", matches)
1007 self.assertNotIn("foo", matches)
1012 self.assertNotIn("foo", matches)
1008
1013
1009 _, matches = complete(line_buffer="d['']", cursor_pos=3)
1014 _, matches = complete(line_buffer="d['']", cursor_pos=3)
1010 self.assertIn("foo", matches)
1015 self.assertIn("foo", matches)
1011 assert not any(m.endswith(("]", '"', "'")) for m in matches), matches
1016 assert not any(m.endswith(("]", '"', "'")) for m in matches), matches
1012
1017
1013 _, matches = complete(line_buffer='d[""]', cursor_pos=3)
1018 _, matches = complete(line_buffer='d[""]', cursor_pos=3)
1014 self.assertIn("foo", matches)
1019 self.assertIn("foo", matches)
1015 assert not any(m.endswith(("]", '"', "'")) for m in matches), matches
1020 assert not any(m.endswith(("]", '"', "'")) for m in matches), matches
1016
1021
1017 _, matches = complete(line_buffer='d["foo","]', cursor_pos=9)
1022 _, matches = complete(line_buffer='d["foo","]', cursor_pos=9)
1018 self.assertIn("bar", matches)
1023 self.assertIn("bar", matches)
1019 assert not any(m.endswith(("]", '"', "'")) for m in matches), matches
1024 assert not any(m.endswith(("]", '"', "'")) for m in matches), matches
1020
1025
1021 _, matches = complete(line_buffer='d["foo",]', cursor_pos=8)
1026 _, matches = complete(line_buffer='d["foo",]', cursor_pos=8)
1022 self.assertIn("'bar'", matches)
1027 self.assertIn("'bar'", matches)
1023 self.assertNotIn("bar", matches)
1028 self.assertNotIn("bar", matches)
1024
1029
1025 # Can complete with longer tuple keys
1030 # Can complete with longer tuple keys
1026 ip.user_ns["d"] = {('foo', 'bar', 'foobar'): None}
1031 ip.user_ns["d"] = {('foo', 'bar', 'foobar'): None}
1027
1032
1028 # - can complete second key
1033 # - can complete second key
1029 _, matches = complete(line_buffer="d['foo', 'b")
1034 _, matches = complete(line_buffer="d['foo', 'b")
1030 self.assertIn("bar", matches)
1035 self.assertIn("bar", matches)
1031 self.assertNotIn("foo", matches)
1036 self.assertNotIn("foo", matches)
1032 self.assertNotIn("foobar", matches)
1037 self.assertNotIn("foobar", matches)
1033
1038
1034 # - can complete third key
1039 # - can complete third key
1035 _, matches = complete(line_buffer="d['foo', 'bar', 'fo")
1040 _, matches = complete(line_buffer="d['foo', 'bar', 'fo")
1036 self.assertIn("foobar", matches)
1041 self.assertIn("foobar", matches)
1037 self.assertNotIn("foo", matches)
1042 self.assertNotIn("foo", matches)
1038 self.assertNotIn("bar", matches)
1043 self.assertNotIn("bar", matches)
1039
1044
1040 def test_dict_key_completion_contexts(self):
1045 def test_dict_key_completion_contexts(self):
1041 """Test expression contexts in which dict key completion occurs"""
1046 """Test expression contexts in which dict key completion occurs"""
1042 ip = get_ipython()
1047 ip = get_ipython()
1043 complete = ip.Completer.complete
1048 complete = ip.Completer.complete
1044 d = {"abc": None}
1049 d = {"abc": None}
1045 ip.user_ns["d"] = d
1050 ip.user_ns["d"] = d
1046
1051
1047 class C:
1052 class C:
1048 data = d
1053 data = d
1049
1054
1050 ip.user_ns["C"] = C
1055 ip.user_ns["C"] = C
1051 ip.user_ns["get"] = lambda: d
1056 ip.user_ns["get"] = lambda: d
1052
1057
1053 def assert_no_completion(**kwargs):
1058 def assert_no_completion(**kwargs):
1054 _, matches = complete(**kwargs)
1059 _, matches = complete(**kwargs)
1055 self.assertNotIn("abc", matches)
1060 self.assertNotIn("abc", matches)
1056 self.assertNotIn("abc'", matches)
1061 self.assertNotIn("abc'", matches)
1057 self.assertNotIn("abc']", matches)
1062 self.assertNotIn("abc']", matches)
1058 self.assertNotIn("'abc'", matches)
1063 self.assertNotIn("'abc'", matches)
1059 self.assertNotIn("'abc']", matches)
1064 self.assertNotIn("'abc']", matches)
1060
1065
1061 def assert_completion(**kwargs):
1066 def assert_completion(**kwargs):
1062 _, matches = complete(**kwargs)
1067 _, matches = complete(**kwargs)
1063 self.assertIn("'abc'", matches)
1068 self.assertIn("'abc'", matches)
1064 self.assertNotIn("'abc']", matches)
1069 self.assertNotIn("'abc']", matches)
1065
1070
1066 # no completion after string closed, even if reopened
1071 # no completion after string closed, even if reopened
1067 assert_no_completion(line_buffer="d['a'")
1072 assert_no_completion(line_buffer="d['a'")
1068 assert_no_completion(line_buffer='d["a"')
1073 assert_no_completion(line_buffer='d["a"')
1069 assert_no_completion(line_buffer="d['a' + ")
1074 assert_no_completion(line_buffer="d['a' + ")
1070 assert_no_completion(line_buffer="d['a' + '")
1075 assert_no_completion(line_buffer="d['a' + '")
1071
1076
1072 # completion in non-trivial expressions
1077 # completion in non-trivial expressions
1073 assert_completion(line_buffer="+ d[")
1078 assert_completion(line_buffer="+ d[")
1074 assert_completion(line_buffer="(d[")
1079 assert_completion(line_buffer="(d[")
1075 assert_completion(line_buffer="C.data[")
1080 assert_completion(line_buffer="C.data[")
1076
1081
1077 # greedy flag
1082 # greedy flag
1078 def assert_completion(**kwargs):
1083 def assert_completion(**kwargs):
1079 _, matches = complete(**kwargs)
1084 _, matches = complete(**kwargs)
1080 self.assertIn("get()['abc']", matches)
1085 self.assertIn("get()['abc']", matches)
1081
1086
1082 assert_no_completion(line_buffer="get()[")
1087 assert_no_completion(line_buffer="get()[")
1083 with greedy_completion():
1088 with greedy_completion():
1084 assert_completion(line_buffer="get()[")
1089 assert_completion(line_buffer="get()[")
1085 assert_completion(line_buffer="get()['")
1090 assert_completion(line_buffer="get()['")
1086 assert_completion(line_buffer="get()['a")
1091 assert_completion(line_buffer="get()['a")
1087 assert_completion(line_buffer="get()['ab")
1092 assert_completion(line_buffer="get()['ab")
1088 assert_completion(line_buffer="get()['abc")
1093 assert_completion(line_buffer="get()['abc")
1089
1094
1090 def test_dict_key_completion_bytes(self):
1095 def test_dict_key_completion_bytes(self):
1091 """Test handling of bytes in dict key completion"""
1096 """Test handling of bytes in dict key completion"""
1092 ip = get_ipython()
1097 ip = get_ipython()
1093 complete = ip.Completer.complete
1098 complete = ip.Completer.complete
1094
1099
1095 ip.user_ns["d"] = {"abc": None, b"abd": None}
1100 ip.user_ns["d"] = {"abc": None, b"abd": None}
1096
1101
1097 _, matches = complete(line_buffer="d[")
1102 _, matches = complete(line_buffer="d[")
1098 self.assertIn("'abc'", matches)
1103 self.assertIn("'abc'", matches)
1099 self.assertIn("b'abd'", matches)
1104 self.assertIn("b'abd'", matches)
1100
1105
1101 if False: # not currently implemented
1106 if False: # not currently implemented
1102 _, matches = complete(line_buffer="d[b")
1107 _, matches = complete(line_buffer="d[b")
1103 self.assertIn("b'abd'", matches)
1108 self.assertIn("b'abd'", matches)
1104 self.assertNotIn("b'abc'", matches)
1109 self.assertNotIn("b'abc'", matches)
1105
1110
1106 _, matches = complete(line_buffer="d[b'")
1111 _, matches = complete(line_buffer="d[b'")
1107 self.assertIn("abd", matches)
1112 self.assertIn("abd", matches)
1108 self.assertNotIn("abc", matches)
1113 self.assertNotIn("abc", matches)
1109
1114
1110 _, matches = complete(line_buffer="d[B'")
1115 _, matches = complete(line_buffer="d[B'")
1111 self.assertIn("abd", matches)
1116 self.assertIn("abd", matches)
1112 self.assertNotIn("abc", matches)
1117 self.assertNotIn("abc", matches)
1113
1118
1114 _, matches = complete(line_buffer="d['")
1119 _, matches = complete(line_buffer="d['")
1115 self.assertIn("abc", matches)
1120 self.assertIn("abc", matches)
1116 self.assertNotIn("abd", matches)
1121 self.assertNotIn("abd", matches)
1117
1122
1118 def test_dict_key_completion_unicode_py3(self):
1123 def test_dict_key_completion_unicode_py3(self):
1119 """Test handling of unicode in dict key completion"""
1124 """Test handling of unicode in dict key completion"""
1120 ip = get_ipython()
1125 ip = get_ipython()
1121 complete = ip.Completer.complete
1126 complete = ip.Completer.complete
1122
1127
1123 ip.user_ns["d"] = {"a\u05d0": None}
1128 ip.user_ns["d"] = {"a\u05d0": None}
1124
1129
1125 # query using escape
1130 # query using escape
1126 if sys.platform != "win32":
1131 if sys.platform != "win32":
1127 # Known failure on Windows
1132 # Known failure on Windows
1128 _, matches = complete(line_buffer="d['a\\u05d0")
1133 _, matches = complete(line_buffer="d['a\\u05d0")
1129 self.assertIn("u05d0", matches) # tokenized after \\
1134 self.assertIn("u05d0", matches) # tokenized after \\
1130
1135
1131 # query using character
1136 # query using character
1132 _, matches = complete(line_buffer="d['a\u05d0")
1137 _, matches = complete(line_buffer="d['a\u05d0")
1133 self.assertIn("a\u05d0", matches)
1138 self.assertIn("a\u05d0", matches)
1134
1139
1135 with greedy_completion():
1140 with greedy_completion():
1136 # query using escape
1141 # query using escape
1137 _, matches = complete(line_buffer="d['a\\u05d0")
1142 _, matches = complete(line_buffer="d['a\\u05d0")
1138 self.assertIn("d['a\\u05d0']", matches) # tokenized after \\
1143 self.assertIn("d['a\\u05d0']", matches) # tokenized after \\
1139
1144
1140 # query using character
1145 # query using character
1141 _, matches = complete(line_buffer="d['a\u05d0")
1146 _, matches = complete(line_buffer="d['a\u05d0")
1142 self.assertIn("d['a\u05d0']", matches)
1147 self.assertIn("d['a\u05d0']", matches)
1143
1148
1144 @dec.skip_without("numpy")
1149 @dec.skip_without("numpy")
1145 def test_struct_array_key_completion(self):
1150 def test_struct_array_key_completion(self):
1146 """Test dict key completion applies to numpy struct arrays"""
1151 """Test dict key completion applies to numpy struct arrays"""
1147 import numpy
1152 import numpy
1148
1153
1149 ip = get_ipython()
1154 ip = get_ipython()
1150 complete = ip.Completer.complete
1155 complete = ip.Completer.complete
1151 ip.user_ns["d"] = numpy.array([], dtype=[("hello", "f"), ("world", "f")])
1156 ip.user_ns["d"] = numpy.array([], dtype=[("hello", "f"), ("world", "f")])
1152 _, matches = complete(line_buffer="d['")
1157 _, matches = complete(line_buffer="d['")
1153 self.assertIn("hello", matches)
1158 self.assertIn("hello", matches)
1154 self.assertIn("world", matches)
1159 self.assertIn("world", matches)
1155 # complete on the numpy struct itself
1160 # complete on the numpy struct itself
1156 dt = numpy.dtype(
1161 dt = numpy.dtype(
1157 [("my_head", [("my_dt", ">u4"), ("my_df", ">u4")]), ("my_data", ">f4", 5)]
1162 [("my_head", [("my_dt", ">u4"), ("my_df", ">u4")]), ("my_data", ">f4", 5)]
1158 )
1163 )
1159 x = numpy.zeros(2, dtype=dt)
1164 x = numpy.zeros(2, dtype=dt)
1160 ip.user_ns["d"] = x[1]
1165 ip.user_ns["d"] = x[1]
1161 _, matches = complete(line_buffer="d['")
1166 _, matches = complete(line_buffer="d['")
1162 self.assertIn("my_head", matches)
1167 self.assertIn("my_head", matches)
1163 self.assertIn("my_data", matches)
1168 self.assertIn("my_data", matches)
1164 # complete on a nested level
1169 # complete on a nested level
1165 with greedy_completion():
1170 with greedy_completion():
1166 ip.user_ns["d"] = numpy.zeros(2, dtype=dt)
1171 ip.user_ns["d"] = numpy.zeros(2, dtype=dt)
1167 _, matches = complete(line_buffer="d[1]['my_head']['")
1172 _, matches = complete(line_buffer="d[1]['my_head']['")
1168 self.assertTrue(any(["my_dt" in m for m in matches]))
1173 self.assertTrue(any(["my_dt" in m for m in matches]))
1169 self.assertTrue(any(["my_df" in m for m in matches]))
1174 self.assertTrue(any(["my_df" in m for m in matches]))
1170
1175
1171 @dec.skip_without("pandas")
1176 @dec.skip_without("pandas")
1172 def test_dataframe_key_completion(self):
1177 def test_dataframe_key_completion(self):
1173 """Test dict key completion applies to pandas DataFrames"""
1178 """Test dict key completion applies to pandas DataFrames"""
1174 import pandas
1179 import pandas
1175
1180
1176 ip = get_ipython()
1181 ip = get_ipython()
1177 complete = ip.Completer.complete
1182 complete = ip.Completer.complete
1178 ip.user_ns["d"] = pandas.DataFrame({"hello": [1], "world": [2]})
1183 ip.user_ns["d"] = pandas.DataFrame({"hello": [1], "world": [2]})
1179 _, matches = complete(line_buffer="d['")
1184 _, matches = complete(line_buffer="d['")
1180 self.assertIn("hello", matches)
1185 self.assertIn("hello", matches)
1181 self.assertIn("world", matches)
1186 self.assertIn("world", matches)
1182
1187
1183 def test_dict_key_completion_invalids(self):
1188 def test_dict_key_completion_invalids(self):
1184 """Smoke test cases dict key completion can't handle"""
1189 """Smoke test cases dict key completion can't handle"""
1185 ip = get_ipython()
1190 ip = get_ipython()
1186 complete = ip.Completer.complete
1191 complete = ip.Completer.complete
1187
1192
1188 ip.user_ns["no_getitem"] = None
1193 ip.user_ns["no_getitem"] = None
1189 ip.user_ns["no_keys"] = []
1194 ip.user_ns["no_keys"] = []
1190 ip.user_ns["cant_call_keys"] = dict
1195 ip.user_ns["cant_call_keys"] = dict
1191 ip.user_ns["empty"] = {}
1196 ip.user_ns["empty"] = {}
1192 ip.user_ns["d"] = {"abc": 5}
1197 ip.user_ns["d"] = {"abc": 5}
1193
1198
1194 _, matches = complete(line_buffer="no_getitem['")
1199 _, matches = complete(line_buffer="no_getitem['")
1195 _, matches = complete(line_buffer="no_keys['")
1200 _, matches = complete(line_buffer="no_keys['")
1196 _, matches = complete(line_buffer="cant_call_keys['")
1201 _, matches = complete(line_buffer="cant_call_keys['")
1197 _, matches = complete(line_buffer="empty['")
1202 _, matches = complete(line_buffer="empty['")
1198 _, matches = complete(line_buffer="name_error['")
1203 _, matches = complete(line_buffer="name_error['")
1199 _, matches = complete(line_buffer="d['\\") # incomplete escape
1204 _, matches = complete(line_buffer="d['\\") # incomplete escape
1200
1205
1201 def test_object_key_completion(self):
1206 def test_object_key_completion(self):
1202 ip = get_ipython()
1207 ip = get_ipython()
1203 ip.user_ns["key_completable"] = KeyCompletable(["qwerty", "qwick"])
1208 ip.user_ns["key_completable"] = KeyCompletable(["qwerty", "qwick"])
1204
1209
1205 _, matches = ip.Completer.complete(line_buffer="key_completable['qw")
1210 _, matches = ip.Completer.complete(line_buffer="key_completable['qw")
1206 self.assertIn("qwerty", matches)
1211 self.assertIn("qwerty", matches)
1207 self.assertIn("qwick", matches)
1212 self.assertIn("qwick", matches)
1208
1213
1209 def test_class_key_completion(self):
1214 def test_class_key_completion(self):
1210 ip = get_ipython()
1215 ip = get_ipython()
1211 NamedInstanceClass("qwerty")
1216 NamedInstanceClass("qwerty")
1212 NamedInstanceClass("qwick")
1217 NamedInstanceClass("qwick")
1213 ip.user_ns["named_instance_class"] = NamedInstanceClass
1218 ip.user_ns["named_instance_class"] = NamedInstanceClass
1214
1219
1215 _, matches = ip.Completer.complete(line_buffer="named_instance_class['qw")
1220 _, matches = ip.Completer.complete(line_buffer="named_instance_class['qw")
1216 self.assertIn("qwerty", matches)
1221 self.assertIn("qwerty", matches)
1217 self.assertIn("qwick", matches)
1222 self.assertIn("qwick", matches)
1218
1223
1219 def test_tryimport(self):
1224 def test_tryimport(self):
1220 """
1225 """
1221 Test that try-import don't crash on trailing dot, and import modules before
1226 Test that try-import don't crash on trailing dot, and import modules before
1222 """
1227 """
1223 from IPython.core.completerlib import try_import
1228 from IPython.core.completerlib import try_import
1224
1229
1225 assert try_import("IPython.")
1230 assert try_import("IPython.")
1226
1231
1227 def test_aimport_module_completer(self):
1232 def test_aimport_module_completer(self):
1228 ip = get_ipython()
1233 ip = get_ipython()
1229 _, matches = ip.complete("i", "%aimport i")
1234 _, matches = ip.complete("i", "%aimport i")
1230 self.assertIn("io", matches)
1235 self.assertIn("io", matches)
1231 self.assertNotIn("int", matches)
1236 self.assertNotIn("int", matches)
1232
1237
1233 def test_nested_import_module_completer(self):
1238 def test_nested_import_module_completer(self):
1234 ip = get_ipython()
1239 ip = get_ipython()
1235 _, matches = ip.complete(None, "import IPython.co", 17)
1240 _, matches = ip.complete(None, "import IPython.co", 17)
1236 self.assertIn("IPython.core", matches)
1241 self.assertIn("IPython.core", matches)
1237 self.assertNotIn("import IPython.core", matches)
1242 self.assertNotIn("import IPython.core", matches)
1238 self.assertNotIn("IPython.display", matches)
1243 self.assertNotIn("IPython.display", matches)
1239
1244
1240 def test_import_module_completer(self):
1245 def test_import_module_completer(self):
1241 ip = get_ipython()
1246 ip = get_ipython()
1242 _, matches = ip.complete("i", "import i")
1247 _, matches = ip.complete("i", "import i")
1243 self.assertIn("io", matches)
1248 self.assertIn("io", matches)
1244 self.assertNotIn("int", matches)
1249 self.assertNotIn("int", matches)
1245
1250
1246 def test_from_module_completer(self):
1251 def test_from_module_completer(self):
1247 ip = get_ipython()
1252 ip = get_ipython()
1248 _, matches = ip.complete("B", "from io import B", 16)
1253 _, matches = ip.complete("B", "from io import B", 16)
1249 self.assertIn("BytesIO", matches)
1254 self.assertIn("BytesIO", matches)
1250 self.assertNotIn("BaseException", matches)
1255 self.assertNotIn("BaseException", matches)
1251
1256
1252 def test_snake_case_completion(self):
1257 def test_snake_case_completion(self):
1253 ip = get_ipython()
1258 ip = get_ipython()
1254 ip.Completer.use_jedi = False
1259 ip.Completer.use_jedi = False
1255 ip.user_ns["some_three"] = 3
1260 ip.user_ns["some_three"] = 3
1256 ip.user_ns["some_four"] = 4
1261 ip.user_ns["some_four"] = 4
1257 _, matches = ip.complete("s_", "print(s_f")
1262 _, matches = ip.complete("s_", "print(s_f")
1258 self.assertIn("some_three", matches)
1263 self.assertIn("some_three", matches)
1259 self.assertIn("some_four", matches)
1264 self.assertIn("some_four", matches)
1260
1265
1261 def test_mix_terms(self):
1266 def test_mix_terms(self):
1262 ip = get_ipython()
1267 ip = get_ipython()
1263 from textwrap import dedent
1268 from textwrap import dedent
1264
1269
1265 ip.Completer.use_jedi = False
1270 ip.Completer.use_jedi = False
1266 ip.ex(
1271 ip.ex(
1267 dedent(
1272 dedent(
1268 """
1273 """
1269 class Test:
1274 class Test:
1270 def meth(self, meth_arg1):
1275 def meth(self, meth_arg1):
1271 print("meth")
1276 print("meth")
1272
1277
1273 def meth_1(self, meth1_arg1, meth1_arg2):
1278 def meth_1(self, meth1_arg1, meth1_arg2):
1274 print("meth1")
1279 print("meth1")
1275
1280
1276 def meth_2(self, meth2_arg1, meth2_arg2):
1281 def meth_2(self, meth2_arg1, meth2_arg2):
1277 print("meth2")
1282 print("meth2")
1278 test = Test()
1283 test = Test()
1279 """
1284 """
1280 )
1285 )
1281 )
1286 )
1282 _, matches = ip.complete(None, "test.meth(")
1287 _, matches = ip.complete(None, "test.meth(")
1283 self.assertIn("meth_arg1=", matches)
1288 self.assertIn("meth_arg1=", matches)
1284 self.assertNotIn("meth2_arg1=", matches)
1289 self.assertNotIn("meth2_arg1=", matches)
@@ -1,636 +1,641 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """Tests for the inputsplitter module."""
2 """Tests for the inputsplitter module."""
3
3
4
4
5 # Copyright (c) IPython Development Team.
5 # Copyright (c) IPython Development Team.
6 # Distributed under the terms of the Modified BSD License.
6 # Distributed under the terms of the Modified BSD License.
7
7
8 import unittest
8 import unittest
9 import pytest
9 import sys
10 import sys
10
11
11 from IPython.core import inputsplitter as isp
12 from IPython.core import inputsplitter as isp
12 from IPython.core.inputtransformer import InputTransformer
13 from IPython.core.inputtransformer import InputTransformer
13 from IPython.core.tests.test_inputtransformer import syntax, syntax_ml
14 from IPython.core.tests.test_inputtransformer import syntax, syntax_ml
14 from IPython.testing import tools as tt
15 from IPython.testing import tools as tt
15 from IPython.testing.decorators import skipif
16
16
17 #-----------------------------------------------------------------------------
17 #-----------------------------------------------------------------------------
18 # Semi-complete examples (also used as tests)
18 # Semi-complete examples (also used as tests)
19 #-----------------------------------------------------------------------------
19 #-----------------------------------------------------------------------------
20
20
21 # Note: at the bottom, there's a slightly more complete version of this that
21 # Note: at the bottom, there's a slightly more complete version of this that
22 # can be useful during development of code here.
22 # can be useful during development of code here.
23
23
24 def mini_interactive_loop(input_func):
24 def mini_interactive_loop(input_func):
25 """Minimal example of the logic of an interactive interpreter loop.
25 """Minimal example of the logic of an interactive interpreter loop.
26
26
27 This serves as an example, and it is used by the test system with a fake
27 This serves as an example, and it is used by the test system with a fake
28 raw_input that simulates interactive input."""
28 raw_input that simulates interactive input."""
29
29
30 from IPython.core.inputsplitter import InputSplitter
30 from IPython.core.inputsplitter import InputSplitter
31
31
32 isp = InputSplitter()
32 isp = InputSplitter()
33 # In practice, this input loop would be wrapped in an outside loop to read
33 # In practice, this input loop would be wrapped in an outside loop to read
34 # input indefinitely, until some exit/quit command was issued. Here we
34 # input indefinitely, until some exit/quit command was issued. Here we
35 # only illustrate the basic inner loop.
35 # only illustrate the basic inner loop.
36 while isp.push_accepts_more():
36 while isp.push_accepts_more():
37 indent = ' '*isp.get_indent_spaces()
37 indent = ' '*isp.get_indent_spaces()
38 prompt = '>>> ' + indent
38 prompt = '>>> ' + indent
39 line = indent + input_func(prompt)
39 line = indent + input_func(prompt)
40 isp.push(line)
40 isp.push(line)
41
41
42 # Here we just return input so we can use it in a test suite, but a real
42 # Here we just return input so we can use it in a test suite, but a real
43 # interpreter would instead send it for execution somewhere.
43 # interpreter would instead send it for execution somewhere.
44 src = isp.source_reset()
44 src = isp.source_reset()
45 #print 'Input source was:\n', src # dbg
45 #print 'Input source was:\n', src # dbg
46 return src
46 return src
47
47
48 #-----------------------------------------------------------------------------
48 #-----------------------------------------------------------------------------
49 # Test utilities, just for local use
49 # Test utilities, just for local use
50 #-----------------------------------------------------------------------------
50 #-----------------------------------------------------------------------------
51
51
52
52
53 def pseudo_input(lines):
53 def pseudo_input(lines):
54 """Return a function that acts like raw_input but feeds the input list."""
54 """Return a function that acts like raw_input but feeds the input list."""
55 ilines = iter(lines)
55 ilines = iter(lines)
56 def raw_in(prompt):
56 def raw_in(prompt):
57 try:
57 try:
58 return next(ilines)
58 return next(ilines)
59 except StopIteration:
59 except StopIteration:
60 return ''
60 return ''
61 return raw_in
61 return raw_in
62
62
63 #-----------------------------------------------------------------------------
63 #-----------------------------------------------------------------------------
64 # Tests
64 # Tests
65 #-----------------------------------------------------------------------------
65 #-----------------------------------------------------------------------------
66 def test_spaces():
66 def test_spaces():
67 tests = [('', 0),
67 tests = [('', 0),
68 (' ', 1),
68 (' ', 1),
69 ('\n', 0),
69 ('\n', 0),
70 (' \n', 1),
70 (' \n', 1),
71 ('x', 0),
71 ('x', 0),
72 (' x', 1),
72 (' x', 1),
73 (' x',2),
73 (' x',2),
74 (' x',4),
74 (' x',4),
75 # Note: tabs are counted as a single whitespace!
75 # Note: tabs are counted as a single whitespace!
76 ('\tx', 1),
76 ('\tx', 1),
77 ('\t x', 2),
77 ('\t x', 2),
78 ]
78 ]
79 tt.check_pairs(isp.num_ini_spaces, tests)
79 tt.check_pairs(isp.num_ini_spaces, tests)
80
80
81
81
82 def test_remove_comments():
82 def test_remove_comments():
83 tests = [('text', 'text'),
83 tests = [('text', 'text'),
84 ('text # comment', 'text '),
84 ('text # comment', 'text '),
85 ('text # comment\n', 'text \n'),
85 ('text # comment\n', 'text \n'),
86 ('text # comment \n', 'text \n'),
86 ('text # comment \n', 'text \n'),
87 ('line # c \nline\n','line \nline\n'),
87 ('line # c \nline\n','line \nline\n'),
88 ('line # c \nline#c2 \nline\nline #c\n\n',
88 ('line # c \nline#c2 \nline\nline #c\n\n',
89 'line \nline\nline\nline \n\n'),
89 'line \nline\nline\nline \n\n'),
90 ]
90 ]
91 tt.check_pairs(isp.remove_comments, tests)
91 tt.check_pairs(isp.remove_comments, tests)
92
92
93
93
94 def test_get_input_encoding():
94 def test_get_input_encoding():
95 encoding = isp.get_input_encoding()
95 encoding = isp.get_input_encoding()
96 assert isinstance(encoding, str)
96 assert isinstance(encoding, str)
97 # simple-minded check that at least encoding a simple string works with the
97 # simple-minded check that at least encoding a simple string works with the
98 # encoding we got.
98 # encoding we got.
99 assert "test".encode(encoding) == b"test"
99 assert "test".encode(encoding) == b"test"
100
100
101
101
102 class NoInputEncodingTestCase(unittest.TestCase):
102 class NoInputEncodingTestCase(unittest.TestCase):
103 def setUp(self):
103 def setUp(self):
104 self.old_stdin = sys.stdin
104 self.old_stdin = sys.stdin
105 class X: pass
105 class X: pass
106 fake_stdin = X()
106 fake_stdin = X()
107 sys.stdin = fake_stdin
107 sys.stdin = fake_stdin
108
108
109 def test(self):
109 def test(self):
110 # Verify that if sys.stdin has no 'encoding' attribute we do the right
110 # Verify that if sys.stdin has no 'encoding' attribute we do the right
111 # thing
111 # thing
112 enc = isp.get_input_encoding()
112 enc = isp.get_input_encoding()
113 self.assertEqual(enc, 'ascii')
113 self.assertEqual(enc, 'ascii')
114
114
115 def tearDown(self):
115 def tearDown(self):
116 sys.stdin = self.old_stdin
116 sys.stdin = self.old_stdin
117
117
118
118
119 class InputSplitterTestCase(unittest.TestCase):
119 class InputSplitterTestCase(unittest.TestCase):
120 def setUp(self):
120 def setUp(self):
121 self.isp = isp.InputSplitter()
121 self.isp = isp.InputSplitter()
122
122
123 def test_reset(self):
123 def test_reset(self):
124 isp = self.isp
124 isp = self.isp
125 isp.push('x=1')
125 isp.push('x=1')
126 isp.reset()
126 isp.reset()
127 self.assertEqual(isp._buffer, [])
127 self.assertEqual(isp._buffer, [])
128 self.assertEqual(isp.get_indent_spaces(), 0)
128 self.assertEqual(isp.get_indent_spaces(), 0)
129 self.assertEqual(isp.source, '')
129 self.assertEqual(isp.source, '')
130 self.assertEqual(isp.code, None)
130 self.assertEqual(isp.code, None)
131 self.assertEqual(isp._is_complete, False)
131 self.assertEqual(isp._is_complete, False)
132
132
133 def test_source(self):
133 def test_source(self):
134 self.isp._store('1')
134 self.isp._store('1')
135 self.isp._store('2')
135 self.isp._store('2')
136 self.assertEqual(self.isp.source, '1\n2\n')
136 self.assertEqual(self.isp.source, '1\n2\n')
137 self.assertEqual(len(self.isp._buffer)>0, True)
137 self.assertEqual(len(self.isp._buffer)>0, True)
138 self.assertEqual(self.isp.source_reset(), '1\n2\n')
138 self.assertEqual(self.isp.source_reset(), '1\n2\n')
139 self.assertEqual(self.isp._buffer, [])
139 self.assertEqual(self.isp._buffer, [])
140 self.assertEqual(self.isp.source, '')
140 self.assertEqual(self.isp.source, '')
141
141
142 def test_indent(self):
142 def test_indent(self):
143 isp = self.isp # shorthand
143 isp = self.isp # shorthand
144 isp.push('x=1')
144 isp.push('x=1')
145 self.assertEqual(isp.get_indent_spaces(), 0)
145 self.assertEqual(isp.get_indent_spaces(), 0)
146 isp.push('if 1:\n x=1')
146 isp.push('if 1:\n x=1')
147 self.assertEqual(isp.get_indent_spaces(), 4)
147 self.assertEqual(isp.get_indent_spaces(), 4)
148 isp.push('y=2\n')
148 isp.push('y=2\n')
149 self.assertEqual(isp.get_indent_spaces(), 0)
149 self.assertEqual(isp.get_indent_spaces(), 0)
150
150
151 def test_indent2(self):
151 def test_indent2(self):
152 isp = self.isp
152 isp = self.isp
153 isp.push('if 1:')
153 isp.push('if 1:')
154 self.assertEqual(isp.get_indent_spaces(), 4)
154 self.assertEqual(isp.get_indent_spaces(), 4)
155 isp.push(' x=1')
155 isp.push(' x=1')
156 self.assertEqual(isp.get_indent_spaces(), 4)
156 self.assertEqual(isp.get_indent_spaces(), 4)
157 # Blank lines shouldn't change the indent level
157 # Blank lines shouldn't change the indent level
158 isp.push(' '*2)
158 isp.push(' '*2)
159 self.assertEqual(isp.get_indent_spaces(), 4)
159 self.assertEqual(isp.get_indent_spaces(), 4)
160
160
161 def test_indent3(self):
161 def test_indent3(self):
162 isp = self.isp
162 isp = self.isp
163 # When a multiline statement contains parens or multiline strings, we
163 # When a multiline statement contains parens or multiline strings, we
164 # shouldn't get confused.
164 # shouldn't get confused.
165 isp.push("if 1:")
165 isp.push("if 1:")
166 isp.push(" x = (1+\n 2)")
166 isp.push(" x = (1+\n 2)")
167 self.assertEqual(isp.get_indent_spaces(), 4)
167 self.assertEqual(isp.get_indent_spaces(), 4)
168
168
169 def test_indent4(self):
169 def test_indent4(self):
170 isp = self.isp
170 isp = self.isp
171 # whitespace after ':' should not screw up indent level
171 # whitespace after ':' should not screw up indent level
172 isp.push('if 1: \n x=1')
172 isp.push('if 1: \n x=1')
173 self.assertEqual(isp.get_indent_spaces(), 4)
173 self.assertEqual(isp.get_indent_spaces(), 4)
174 isp.push('y=2\n')
174 isp.push('y=2\n')
175 self.assertEqual(isp.get_indent_spaces(), 0)
175 self.assertEqual(isp.get_indent_spaces(), 0)
176 isp.push('if 1:\t\n x=1')
176 isp.push('if 1:\t\n x=1')
177 self.assertEqual(isp.get_indent_spaces(), 4)
177 self.assertEqual(isp.get_indent_spaces(), 4)
178 isp.push('y=2\n')
178 isp.push('y=2\n')
179 self.assertEqual(isp.get_indent_spaces(), 0)
179 self.assertEqual(isp.get_indent_spaces(), 0)
180
180
181 def test_dedent_pass(self):
181 def test_dedent_pass(self):
182 isp = self.isp # shorthand
182 isp = self.isp # shorthand
183 # should NOT cause dedent
183 # should NOT cause dedent
184 isp.push('if 1:\n passes = 5')
184 isp.push('if 1:\n passes = 5')
185 self.assertEqual(isp.get_indent_spaces(), 4)
185 self.assertEqual(isp.get_indent_spaces(), 4)
186 isp.push('if 1:\n pass')
186 isp.push('if 1:\n pass')
187 self.assertEqual(isp.get_indent_spaces(), 0)
187 self.assertEqual(isp.get_indent_spaces(), 0)
188 isp.push('if 1:\n pass ')
188 isp.push('if 1:\n pass ')
189 self.assertEqual(isp.get_indent_spaces(), 0)
189 self.assertEqual(isp.get_indent_spaces(), 0)
190
190
191 def test_dedent_break(self):
191 def test_dedent_break(self):
192 isp = self.isp # shorthand
192 isp = self.isp # shorthand
193 # should NOT cause dedent
193 # should NOT cause dedent
194 isp.push('while 1:\n breaks = 5')
194 isp.push('while 1:\n breaks = 5')
195 self.assertEqual(isp.get_indent_spaces(), 4)
195 self.assertEqual(isp.get_indent_spaces(), 4)
196 isp.push('while 1:\n break')
196 isp.push('while 1:\n break')
197 self.assertEqual(isp.get_indent_spaces(), 0)
197 self.assertEqual(isp.get_indent_spaces(), 0)
198 isp.push('while 1:\n break ')
198 isp.push('while 1:\n break ')
199 self.assertEqual(isp.get_indent_spaces(), 0)
199 self.assertEqual(isp.get_indent_spaces(), 0)
200
200
201 def test_dedent_continue(self):
201 def test_dedent_continue(self):
202 isp = self.isp # shorthand
202 isp = self.isp # shorthand
203 # should NOT cause dedent
203 # should NOT cause dedent
204 isp.push('while 1:\n continues = 5')
204 isp.push('while 1:\n continues = 5')
205 self.assertEqual(isp.get_indent_spaces(), 4)
205 self.assertEqual(isp.get_indent_spaces(), 4)
206 isp.push('while 1:\n continue')
206 isp.push('while 1:\n continue')
207 self.assertEqual(isp.get_indent_spaces(), 0)
207 self.assertEqual(isp.get_indent_spaces(), 0)
208 isp.push('while 1:\n continue ')
208 isp.push('while 1:\n continue ')
209 self.assertEqual(isp.get_indent_spaces(), 0)
209 self.assertEqual(isp.get_indent_spaces(), 0)
210
210
211 def test_dedent_raise(self):
211 def test_dedent_raise(self):
212 isp = self.isp # shorthand
212 isp = self.isp # shorthand
213 # should NOT cause dedent
213 # should NOT cause dedent
214 isp.push('if 1:\n raised = 4')
214 isp.push('if 1:\n raised = 4')
215 self.assertEqual(isp.get_indent_spaces(), 4)
215 self.assertEqual(isp.get_indent_spaces(), 4)
216 isp.push('if 1:\n raise TypeError()')
216 isp.push('if 1:\n raise TypeError()')
217 self.assertEqual(isp.get_indent_spaces(), 0)
217 self.assertEqual(isp.get_indent_spaces(), 0)
218 isp.push('if 1:\n raise')
218 isp.push('if 1:\n raise')
219 self.assertEqual(isp.get_indent_spaces(), 0)
219 self.assertEqual(isp.get_indent_spaces(), 0)
220 isp.push('if 1:\n raise ')
220 isp.push('if 1:\n raise ')
221 self.assertEqual(isp.get_indent_spaces(), 0)
221 self.assertEqual(isp.get_indent_spaces(), 0)
222
222
223 def test_dedent_return(self):
223 def test_dedent_return(self):
224 isp = self.isp # shorthand
224 isp = self.isp # shorthand
225 # should NOT cause dedent
225 # should NOT cause dedent
226 isp.push('if 1:\n returning = 4')
226 isp.push('if 1:\n returning = 4')
227 self.assertEqual(isp.get_indent_spaces(), 4)
227 self.assertEqual(isp.get_indent_spaces(), 4)
228 isp.push('if 1:\n return 5 + 493')
228 isp.push('if 1:\n return 5 + 493')
229 self.assertEqual(isp.get_indent_spaces(), 0)
229 self.assertEqual(isp.get_indent_spaces(), 0)
230 isp.push('if 1:\n return')
230 isp.push('if 1:\n return')
231 self.assertEqual(isp.get_indent_spaces(), 0)
231 self.assertEqual(isp.get_indent_spaces(), 0)
232 isp.push('if 1:\n return ')
232 isp.push('if 1:\n return ')
233 self.assertEqual(isp.get_indent_spaces(), 0)
233 self.assertEqual(isp.get_indent_spaces(), 0)
234 isp.push('if 1:\n return(0)')
234 isp.push('if 1:\n return(0)')
235 self.assertEqual(isp.get_indent_spaces(), 0)
235 self.assertEqual(isp.get_indent_spaces(), 0)
236
236
237 def test_push(self):
237 def test_push(self):
238 isp = self.isp
238 isp = self.isp
239 self.assertEqual(isp.push('x=1'), True)
239 self.assertEqual(isp.push('x=1'), True)
240
240
241 def test_push2(self):
241 def test_push2(self):
242 isp = self.isp
242 isp = self.isp
243 self.assertEqual(isp.push('if 1:'), False)
243 self.assertEqual(isp.push('if 1:'), False)
244 for line in [' x=1', '# a comment', ' y=2']:
244 for line in [' x=1', '# a comment', ' y=2']:
245 print(line)
245 print(line)
246 self.assertEqual(isp.push(line), True)
246 self.assertEqual(isp.push(line), True)
247
247
248 def test_push3(self):
248 def test_push3(self):
249 isp = self.isp
249 isp = self.isp
250 isp.push('if True:')
250 isp.push('if True:')
251 isp.push(' a = 1')
251 isp.push(' a = 1')
252 self.assertEqual(isp.push('b = [1,'), False)
252 self.assertEqual(isp.push('b = [1,'), False)
253
253
254 def test_push_accepts_more(self):
254 def test_push_accepts_more(self):
255 isp = self.isp
255 isp = self.isp
256 isp.push('x=1')
256 isp.push('x=1')
257 self.assertEqual(isp.push_accepts_more(), False)
257 self.assertEqual(isp.push_accepts_more(), False)
258
258
259 def test_push_accepts_more2(self):
259 def test_push_accepts_more2(self):
260 isp = self.isp
260 isp = self.isp
261 isp.push('if 1:')
261 isp.push('if 1:')
262 self.assertEqual(isp.push_accepts_more(), True)
262 self.assertEqual(isp.push_accepts_more(), True)
263 isp.push(' x=1')
263 isp.push(' x=1')
264 self.assertEqual(isp.push_accepts_more(), True)
264 self.assertEqual(isp.push_accepts_more(), True)
265 isp.push('')
265 isp.push('')
266 self.assertEqual(isp.push_accepts_more(), False)
266 self.assertEqual(isp.push_accepts_more(), False)
267
267
268 def test_push_accepts_more3(self):
268 def test_push_accepts_more3(self):
269 isp = self.isp
269 isp = self.isp
270 isp.push("x = (2+\n3)")
270 isp.push("x = (2+\n3)")
271 self.assertEqual(isp.push_accepts_more(), False)
271 self.assertEqual(isp.push_accepts_more(), False)
272
272
273 def test_push_accepts_more4(self):
273 def test_push_accepts_more4(self):
274 isp = self.isp
274 isp = self.isp
275 # When a multiline statement contains parens or multiline strings, we
275 # When a multiline statement contains parens or multiline strings, we
276 # shouldn't get confused.
276 # shouldn't get confused.
277 # FIXME: we should be able to better handle de-dents in statements like
277 # FIXME: we should be able to better handle de-dents in statements like
278 # multiline strings and multiline expressions (continued with \ or
278 # multiline strings and multiline expressions (continued with \ or
279 # parens). Right now we aren't handling the indentation tracking quite
279 # parens). Right now we aren't handling the indentation tracking quite
280 # correctly with this, though in practice it may not be too much of a
280 # correctly with this, though in practice it may not be too much of a
281 # problem. We'll need to see.
281 # problem. We'll need to see.
282 isp.push("if 1:")
282 isp.push("if 1:")
283 isp.push(" x = (2+")
283 isp.push(" x = (2+")
284 isp.push(" 3)")
284 isp.push(" 3)")
285 self.assertEqual(isp.push_accepts_more(), True)
285 self.assertEqual(isp.push_accepts_more(), True)
286 isp.push(" y = 3")
286 isp.push(" y = 3")
287 self.assertEqual(isp.push_accepts_more(), True)
287 self.assertEqual(isp.push_accepts_more(), True)
288 isp.push('')
288 isp.push('')
289 self.assertEqual(isp.push_accepts_more(), False)
289 self.assertEqual(isp.push_accepts_more(), False)
290
290
291 def test_push_accepts_more5(self):
291 def test_push_accepts_more5(self):
292 isp = self.isp
292 isp = self.isp
293 isp.push('try:')
293 isp.push('try:')
294 isp.push(' a = 5')
294 isp.push(' a = 5')
295 isp.push('except:')
295 isp.push('except:')
296 isp.push(' raise')
296 isp.push(' raise')
297 # We want to be able to add an else: block at this point, so it should
297 # We want to be able to add an else: block at this point, so it should
298 # wait for a blank line.
298 # wait for a blank line.
299 self.assertEqual(isp.push_accepts_more(), True)
299 self.assertEqual(isp.push_accepts_more(), True)
300
300
301 def test_continuation(self):
301 def test_continuation(self):
302 isp = self.isp
302 isp = self.isp
303 isp.push("import os, \\")
303 isp.push("import os, \\")
304 self.assertEqual(isp.push_accepts_more(), True)
304 self.assertEqual(isp.push_accepts_more(), True)
305 isp.push("sys")
305 isp.push("sys")
306 self.assertEqual(isp.push_accepts_more(), False)
306 self.assertEqual(isp.push_accepts_more(), False)
307
307
308 def test_syntax_error(self):
308 def test_syntax_error(self):
309 isp = self.isp
309 isp = self.isp
310 # Syntax errors immediately produce a 'ready' block, so the invalid
310 # Syntax errors immediately produce a 'ready' block, so the invalid
311 # Python can be sent to the kernel for evaluation with possible ipython
311 # Python can be sent to the kernel for evaluation with possible ipython
312 # special-syntax conversion.
312 # special-syntax conversion.
313 isp.push('run foo')
313 isp.push('run foo')
314 self.assertEqual(isp.push_accepts_more(), False)
314 self.assertEqual(isp.push_accepts_more(), False)
315
315
316 def test_unicode(self):
316 def test_unicode(self):
317 self.isp.push(u"Pérez")
317 self.isp.push(u"Pérez")
318 self.isp.push(u'\xc3\xa9')
318 self.isp.push(u'\xc3\xa9')
319 self.isp.push(u"u'\xc3\xa9'")
319 self.isp.push(u"u'\xc3\xa9'")
320
320
321 @skipif(sys.version_info[:3] == (3, 9, 8))
321 @pytest.mark.xfail(
322 reason="Bug in python 3.9.8 – bpo 45738",
323 condition=sys.version_info in [(3, 9, 8, "final", 0), (3, 11, 0, "alpha", 2)],
324 raises=SystemError,
325 strict=True,
326 )
322 def test_line_continuation(self):
327 def test_line_continuation(self):
323 """ Test issue #2108."""
328 """ Test issue #2108."""
324 isp = self.isp
329 isp = self.isp
325 # A blank line after a line continuation should not accept more
330 # A blank line after a line continuation should not accept more
326 isp.push("1 \\\n\n")
331 isp.push("1 \\\n\n")
327 self.assertEqual(isp.push_accepts_more(), False)
332 self.assertEqual(isp.push_accepts_more(), False)
328 # Whitespace after a \ is a SyntaxError. The only way to test that
333 # Whitespace after a \ is a SyntaxError. The only way to test that
329 # here is to test that push doesn't accept more (as with
334 # here is to test that push doesn't accept more (as with
330 # test_syntax_error() above).
335 # test_syntax_error() above).
331 isp.push(r"1 \ ")
336 isp.push(r"1 \ ")
332 self.assertEqual(isp.push_accepts_more(), False)
337 self.assertEqual(isp.push_accepts_more(), False)
333 # Even if the line is continuable (c.f. the regular Python
338 # Even if the line is continuable (c.f. the regular Python
334 # interpreter)
339 # interpreter)
335 isp.push(r"(1 \ ")
340 isp.push(r"(1 \ ")
336 self.assertEqual(isp.push_accepts_more(), False)
341 self.assertEqual(isp.push_accepts_more(), False)
337
342
338 def test_check_complete(self):
343 def test_check_complete(self):
339 isp = self.isp
344 isp = self.isp
340 self.assertEqual(isp.check_complete("a = 1"), ('complete', None))
345 self.assertEqual(isp.check_complete("a = 1"), ('complete', None))
341 self.assertEqual(isp.check_complete("for a in range(5):"), ('incomplete', 4))
346 self.assertEqual(isp.check_complete("for a in range(5):"), ('incomplete', 4))
342 self.assertEqual(isp.check_complete("raise = 2"), ('invalid', None))
347 self.assertEqual(isp.check_complete("raise = 2"), ('invalid', None))
343 self.assertEqual(isp.check_complete("a = [1,\n2,"), ('incomplete', 0))
348 self.assertEqual(isp.check_complete("a = [1,\n2,"), ('incomplete', 0))
344 self.assertEqual(isp.check_complete("def a():\n x=1\n global x"), ('invalid', None))
349 self.assertEqual(isp.check_complete("def a():\n x=1\n global x"), ('invalid', None))
345
350
346 class InteractiveLoopTestCase(unittest.TestCase):
351 class InteractiveLoopTestCase(unittest.TestCase):
347 """Tests for an interactive loop like a python shell.
352 """Tests for an interactive loop like a python shell.
348 """
353 """
349 def check_ns(self, lines, ns):
354 def check_ns(self, lines, ns):
350 """Validate that the given input lines produce the resulting namespace.
355 """Validate that the given input lines produce the resulting namespace.
351
356
352 Note: the input lines are given exactly as they would be typed in an
357 Note: the input lines are given exactly as they would be typed in an
353 auto-indenting environment, as mini_interactive_loop above already does
358 auto-indenting environment, as mini_interactive_loop above already does
354 auto-indenting and prepends spaces to the input.
359 auto-indenting and prepends spaces to the input.
355 """
360 """
356 src = mini_interactive_loop(pseudo_input(lines))
361 src = mini_interactive_loop(pseudo_input(lines))
357 test_ns = {}
362 test_ns = {}
358 exec(src, test_ns)
363 exec(src, test_ns)
359 # We can't check that the provided ns is identical to the test_ns,
364 # We can't check that the provided ns is identical to the test_ns,
360 # because Python fills test_ns with extra keys (copyright, etc). But
365 # because Python fills test_ns with extra keys (copyright, etc). But
361 # we can check that the given dict is *contained* in test_ns
366 # we can check that the given dict is *contained* in test_ns
362 for k,v in ns.items():
367 for k,v in ns.items():
363 self.assertEqual(test_ns[k], v)
368 self.assertEqual(test_ns[k], v)
364
369
365 def test_simple(self):
370 def test_simple(self):
366 self.check_ns(['x=1'], dict(x=1))
371 self.check_ns(['x=1'], dict(x=1))
367
372
368 def test_simple2(self):
373 def test_simple2(self):
369 self.check_ns(['if 1:', 'x=2'], dict(x=2))
374 self.check_ns(['if 1:', 'x=2'], dict(x=2))
370
375
371 def test_xy(self):
376 def test_xy(self):
372 self.check_ns(['x=1; y=2'], dict(x=1, y=2))
377 self.check_ns(['x=1; y=2'], dict(x=1, y=2))
373
378
374 def test_abc(self):
379 def test_abc(self):
375 self.check_ns(['if 1:','a=1','b=2','c=3'], dict(a=1, b=2, c=3))
380 self.check_ns(['if 1:','a=1','b=2','c=3'], dict(a=1, b=2, c=3))
376
381
377 def test_multi(self):
382 def test_multi(self):
378 self.check_ns(['x =(1+','1+','2)'], dict(x=4))
383 self.check_ns(['x =(1+','1+','2)'], dict(x=4))
379
384
380
385
381 class IPythonInputTestCase(InputSplitterTestCase):
386 class IPythonInputTestCase(InputSplitterTestCase):
382 """By just creating a new class whose .isp is a different instance, we
387 """By just creating a new class whose .isp is a different instance, we
383 re-run the same test battery on the new input splitter.
388 re-run the same test battery on the new input splitter.
384
389
385 In addition, this runs the tests over the syntax and syntax_ml dicts that
390 In addition, this runs the tests over the syntax and syntax_ml dicts that
386 were tested by individual functions, as part of the OO interface.
391 were tested by individual functions, as part of the OO interface.
387
392
388 It also makes some checks on the raw buffer storage.
393 It also makes some checks on the raw buffer storage.
389 """
394 """
390
395
391 def setUp(self):
396 def setUp(self):
392 self.isp = isp.IPythonInputSplitter()
397 self.isp = isp.IPythonInputSplitter()
393
398
394 def test_syntax(self):
399 def test_syntax(self):
395 """Call all single-line syntax tests from the main object"""
400 """Call all single-line syntax tests from the main object"""
396 isp = self.isp
401 isp = self.isp
397 for example in syntax.values():
402 for example in syntax.values():
398 for raw, out_t in example:
403 for raw, out_t in example:
399 if raw.startswith(' '):
404 if raw.startswith(' '):
400 continue
405 continue
401
406
402 isp.push(raw+'\n')
407 isp.push(raw+'\n')
403 out_raw = isp.source_raw
408 out_raw = isp.source_raw
404 out = isp.source_reset()
409 out = isp.source_reset()
405 self.assertEqual(out.rstrip(), out_t,
410 self.assertEqual(out.rstrip(), out_t,
406 tt.pair_fail_msg.format("inputsplitter",raw, out_t, out))
411 tt.pair_fail_msg.format("inputsplitter",raw, out_t, out))
407 self.assertEqual(out_raw.rstrip(), raw.rstrip())
412 self.assertEqual(out_raw.rstrip(), raw.rstrip())
408
413
409 def test_syntax_multiline(self):
414 def test_syntax_multiline(self):
410 isp = self.isp
415 isp = self.isp
411 for example in syntax_ml.values():
416 for example in syntax_ml.values():
412 for line_pairs in example:
417 for line_pairs in example:
413 out_t_parts = []
418 out_t_parts = []
414 raw_parts = []
419 raw_parts = []
415 for lraw, out_t_part in line_pairs:
420 for lraw, out_t_part in line_pairs:
416 if out_t_part is not None:
421 if out_t_part is not None:
417 out_t_parts.append(out_t_part)
422 out_t_parts.append(out_t_part)
418
423
419 if lraw is not None:
424 if lraw is not None:
420 isp.push(lraw)
425 isp.push(lraw)
421 raw_parts.append(lraw)
426 raw_parts.append(lraw)
422
427
423 out_raw = isp.source_raw
428 out_raw = isp.source_raw
424 out = isp.source_reset()
429 out = isp.source_reset()
425 out_t = '\n'.join(out_t_parts).rstrip()
430 out_t = '\n'.join(out_t_parts).rstrip()
426 raw = '\n'.join(raw_parts).rstrip()
431 raw = '\n'.join(raw_parts).rstrip()
427 self.assertEqual(out.rstrip(), out_t)
432 self.assertEqual(out.rstrip(), out_t)
428 self.assertEqual(out_raw.rstrip(), raw)
433 self.assertEqual(out_raw.rstrip(), raw)
429
434
430 def test_syntax_multiline_cell(self):
435 def test_syntax_multiline_cell(self):
431 isp = self.isp
436 isp = self.isp
432 for example in syntax_ml.values():
437 for example in syntax_ml.values():
433
438
434 out_t_parts = []
439 out_t_parts = []
435 for line_pairs in example:
440 for line_pairs in example:
436 raw = '\n'.join(r for r, _ in line_pairs if r is not None)
441 raw = '\n'.join(r for r, _ in line_pairs if r is not None)
437 out_t = '\n'.join(t for _,t in line_pairs if t is not None)
442 out_t = '\n'.join(t for _,t in line_pairs if t is not None)
438 out = isp.transform_cell(raw)
443 out = isp.transform_cell(raw)
439 # Match ignoring trailing whitespace
444 # Match ignoring trailing whitespace
440 self.assertEqual(out.rstrip(), out_t.rstrip())
445 self.assertEqual(out.rstrip(), out_t.rstrip())
441
446
442 def test_cellmagic_preempt(self):
447 def test_cellmagic_preempt(self):
443 isp = self.isp
448 isp = self.isp
444 for raw, name, line, cell in [
449 for raw, name, line, cell in [
445 ("%%cellm a\nIn[1]:", u'cellm', u'a', u'In[1]:'),
450 ("%%cellm a\nIn[1]:", u'cellm', u'a', u'In[1]:'),
446 ("%%cellm \nline\n>>> hi", u'cellm', u'', u'line\n>>> hi'),
451 ("%%cellm \nline\n>>> hi", u'cellm', u'', u'line\n>>> hi'),
447 (">>> %%cellm \nline\n>>> hi", u'cellm', u'', u'line\nhi'),
452 (">>> %%cellm \nline\n>>> hi", u'cellm', u'', u'line\nhi'),
448 ("%%cellm \n>>> hi", u'cellm', u'', u'>>> hi'),
453 ("%%cellm \n>>> hi", u'cellm', u'', u'>>> hi'),
449 ("%%cellm \nline1\nline2", u'cellm', u'', u'line1\nline2'),
454 ("%%cellm \nline1\nline2", u'cellm', u'', u'line1\nline2'),
450 ("%%cellm \nline1\\\\\nline2", u'cellm', u'', u'line1\\\\\nline2'),
455 ("%%cellm \nline1\\\\\nline2", u'cellm', u'', u'line1\\\\\nline2'),
451 ]:
456 ]:
452 expected = "get_ipython().run_cell_magic(%r, %r, %r)" % (
457 expected = "get_ipython().run_cell_magic(%r, %r, %r)" % (
453 name, line, cell
458 name, line, cell
454 )
459 )
455 out = isp.transform_cell(raw)
460 out = isp.transform_cell(raw)
456 self.assertEqual(out.rstrip(), expected.rstrip())
461 self.assertEqual(out.rstrip(), expected.rstrip())
457
462
458 def test_multiline_passthrough(self):
463 def test_multiline_passthrough(self):
459 isp = self.isp
464 isp = self.isp
460 class CommentTransformer(InputTransformer):
465 class CommentTransformer(InputTransformer):
461 def __init__(self):
466 def __init__(self):
462 self._lines = []
467 self._lines = []
463
468
464 def push(self, line):
469 def push(self, line):
465 self._lines.append(line + '#')
470 self._lines.append(line + '#')
466
471
467 def reset(self):
472 def reset(self):
468 text = '\n'.join(self._lines)
473 text = '\n'.join(self._lines)
469 self._lines = []
474 self._lines = []
470 return text
475 return text
471
476
472 isp.physical_line_transforms.insert(0, CommentTransformer())
477 isp.physical_line_transforms.insert(0, CommentTransformer())
473
478
474 for raw, expected in [
479 for raw, expected in [
475 ("a=5", "a=5#"),
480 ("a=5", "a=5#"),
476 ("%ls foo", "get_ipython().run_line_magic(%r, %r)" % (u'ls', u'foo#')),
481 ("%ls foo", "get_ipython().run_line_magic(%r, %r)" % (u'ls', u'foo#')),
477 ("!ls foo\n%ls bar", "get_ipython().system(%r)\nget_ipython().run_line_magic(%r, %r)" % (
482 ("!ls foo\n%ls bar", "get_ipython().system(%r)\nget_ipython().run_line_magic(%r, %r)" % (
478 u'ls foo#', u'ls', u'bar#'
483 u'ls foo#', u'ls', u'bar#'
479 )),
484 )),
480 ("1\n2\n3\n%ls foo\n4\n5", "1#\n2#\n3#\nget_ipython().run_line_magic(%r, %r)\n4#\n5#" % (u'ls', u'foo#')),
485 ("1\n2\n3\n%ls foo\n4\n5", "1#\n2#\n3#\nget_ipython().run_line_magic(%r, %r)\n4#\n5#" % (u'ls', u'foo#')),
481 ]:
486 ]:
482 out = isp.transform_cell(raw)
487 out = isp.transform_cell(raw)
483 self.assertEqual(out.rstrip(), expected.rstrip())
488 self.assertEqual(out.rstrip(), expected.rstrip())
484
489
485 #-----------------------------------------------------------------------------
490 #-----------------------------------------------------------------------------
486 # Main - use as a script, mostly for developer experiments
491 # Main - use as a script, mostly for developer experiments
487 #-----------------------------------------------------------------------------
492 #-----------------------------------------------------------------------------
488
493
489 if __name__ == '__main__':
494 if __name__ == '__main__':
490 # A simple demo for interactive experimentation. This code will not get
495 # A simple demo for interactive experimentation. This code will not get
491 # picked up by any test suite.
496 # picked up by any test suite.
492 from IPython.core.inputsplitter import IPythonInputSplitter
497 from IPython.core.inputsplitter import IPythonInputSplitter
493
498
494 # configure here the syntax to use, prompt and whether to autoindent
499 # configure here the syntax to use, prompt and whether to autoindent
495 #isp, start_prompt = InputSplitter(), '>>> '
500 #isp, start_prompt = InputSplitter(), '>>> '
496 isp, start_prompt = IPythonInputSplitter(), 'In> '
501 isp, start_prompt = IPythonInputSplitter(), 'In> '
497
502
498 autoindent = True
503 autoindent = True
499 #autoindent = False
504 #autoindent = False
500
505
501 try:
506 try:
502 while True:
507 while True:
503 prompt = start_prompt
508 prompt = start_prompt
504 while isp.push_accepts_more():
509 while isp.push_accepts_more():
505 indent = ' '*isp.get_indent_spaces()
510 indent = ' '*isp.get_indent_spaces()
506 if autoindent:
511 if autoindent:
507 line = indent + input(prompt+indent)
512 line = indent + input(prompt+indent)
508 else:
513 else:
509 line = input(prompt)
514 line = input(prompt)
510 isp.push(line)
515 isp.push(line)
511 prompt = '... '
516 prompt = '... '
512
517
513 # Here we just return input so we can use it in a test suite, but a
518 # Here we just return input so we can use it in a test suite, but a
514 # real interpreter would instead send it for execution somewhere.
519 # real interpreter would instead send it for execution somewhere.
515 #src = isp.source; raise EOFError # dbg
520 #src = isp.source; raise EOFError # dbg
516 raw = isp.source_raw
521 raw = isp.source_raw
517 src = isp.source_reset()
522 src = isp.source_reset()
518 print('Input source was:\n', src)
523 print('Input source was:\n', src)
519 print('Raw source was:\n', raw)
524 print('Raw source was:\n', raw)
520 except EOFError:
525 except EOFError:
521 print('Bye')
526 print('Bye')
522
527
523 # Tests for cell magics support
528 # Tests for cell magics support
524
529
525 def test_last_blank():
530 def test_last_blank():
526 assert isp.last_blank("") is False
531 assert isp.last_blank("") is False
527 assert isp.last_blank("abc") is False
532 assert isp.last_blank("abc") is False
528 assert isp.last_blank("abc\n") is False
533 assert isp.last_blank("abc\n") is False
529 assert isp.last_blank("abc\na") is False
534 assert isp.last_blank("abc\na") is False
530
535
531 assert isp.last_blank("\n") is True
536 assert isp.last_blank("\n") is True
532 assert isp.last_blank("\n ") is True
537 assert isp.last_blank("\n ") is True
533 assert isp.last_blank("abc\n ") is True
538 assert isp.last_blank("abc\n ") is True
534 assert isp.last_blank("abc\n\n") is True
539 assert isp.last_blank("abc\n\n") is True
535 assert isp.last_blank("abc\nd\n\n") is True
540 assert isp.last_blank("abc\nd\n\n") is True
536 assert isp.last_blank("abc\nd\ne\n\n") is True
541 assert isp.last_blank("abc\nd\ne\n\n") is True
537 assert isp.last_blank("abc \n \n \n\n") is True
542 assert isp.last_blank("abc \n \n \n\n") is True
538
543
539
544
540 def test_last_two_blanks():
545 def test_last_two_blanks():
541 assert isp.last_two_blanks("") is False
546 assert isp.last_two_blanks("") is False
542 assert isp.last_two_blanks("abc") is False
547 assert isp.last_two_blanks("abc") is False
543 assert isp.last_two_blanks("abc\n") is False
548 assert isp.last_two_blanks("abc\n") is False
544 assert isp.last_two_blanks("abc\n\na") is False
549 assert isp.last_two_blanks("abc\n\na") is False
545 assert isp.last_two_blanks("abc\n \n") is False
550 assert isp.last_two_blanks("abc\n \n") is False
546 assert isp.last_two_blanks("abc\n\n") is False
551 assert isp.last_two_blanks("abc\n\n") is False
547
552
548 assert isp.last_two_blanks("\n\n") is True
553 assert isp.last_two_blanks("\n\n") is True
549 assert isp.last_two_blanks("\n\n ") is True
554 assert isp.last_two_blanks("\n\n ") is True
550 assert isp.last_two_blanks("\n \n") is True
555 assert isp.last_two_blanks("\n \n") is True
551 assert isp.last_two_blanks("abc\n\n ") is True
556 assert isp.last_two_blanks("abc\n\n ") is True
552 assert isp.last_two_blanks("abc\n\n\n") is True
557 assert isp.last_two_blanks("abc\n\n\n") is True
553 assert isp.last_two_blanks("abc\n\n \n") is True
558 assert isp.last_two_blanks("abc\n\n \n") is True
554 assert isp.last_two_blanks("abc\n\n \n ") is True
559 assert isp.last_two_blanks("abc\n\n \n ") is True
555 assert isp.last_two_blanks("abc\n\n \n \n") is True
560 assert isp.last_two_blanks("abc\n\n \n \n") is True
556 assert isp.last_two_blanks("abc\nd\n\n\n") is True
561 assert isp.last_two_blanks("abc\nd\n\n\n") is True
557 assert isp.last_two_blanks("abc\nd\ne\nf\n\n\n") is True
562 assert isp.last_two_blanks("abc\nd\ne\nf\n\n\n") is True
558
563
559
564
560 class CellMagicsCommon(object):
565 class CellMagicsCommon(object):
561
566
562 def test_whole_cell(self):
567 def test_whole_cell(self):
563 src = "%%cellm line\nbody\n"
568 src = "%%cellm line\nbody\n"
564 out = self.sp.transform_cell(src)
569 out = self.sp.transform_cell(src)
565 ref = "get_ipython().run_cell_magic('cellm', 'line', 'body')\n"
570 ref = "get_ipython().run_cell_magic('cellm', 'line', 'body')\n"
566 assert out == ref
571 assert out == ref
567
572
568 def test_cellmagic_help(self):
573 def test_cellmagic_help(self):
569 self.sp.push('%%cellm?')
574 self.sp.push('%%cellm?')
570 assert self.sp.push_accepts_more() is False
575 assert self.sp.push_accepts_more() is False
571
576
572 def tearDown(self):
577 def tearDown(self):
573 self.sp.reset()
578 self.sp.reset()
574
579
575
580
576 class CellModeCellMagics(CellMagicsCommon, unittest.TestCase):
581 class CellModeCellMagics(CellMagicsCommon, unittest.TestCase):
577 sp = isp.IPythonInputSplitter(line_input_checker=False)
582 sp = isp.IPythonInputSplitter(line_input_checker=False)
578
583
579 def test_incremental(self):
584 def test_incremental(self):
580 sp = self.sp
585 sp = self.sp
581 sp.push("%%cellm firstline\n")
586 sp.push("%%cellm firstline\n")
582 assert sp.push_accepts_more() is True # 1
587 assert sp.push_accepts_more() is True # 1
583 sp.push("line2\n")
588 sp.push("line2\n")
584 assert sp.push_accepts_more() is True # 2
589 assert sp.push_accepts_more() is True # 2
585 sp.push("\n")
590 sp.push("\n")
586 # This should accept a blank line and carry on until the cell is reset
591 # This should accept a blank line and carry on until the cell is reset
587 assert sp.push_accepts_more() is True # 3
592 assert sp.push_accepts_more() is True # 3
588
593
589 def test_no_strip_coding(self):
594 def test_no_strip_coding(self):
590 src = '\n'.join([
595 src = '\n'.join([
591 '%%writefile foo.py',
596 '%%writefile foo.py',
592 '# coding: utf-8',
597 '# coding: utf-8',
593 'print(u"üñîçø∂é")',
598 'print(u"üñîçø∂é")',
594 ])
599 ])
595 out = self.sp.transform_cell(src)
600 out = self.sp.transform_cell(src)
596 assert "# coding: utf-8" in out
601 assert "# coding: utf-8" in out
597
602
598
603
599 class LineModeCellMagics(CellMagicsCommon, unittest.TestCase):
604 class LineModeCellMagics(CellMagicsCommon, unittest.TestCase):
600 sp = isp.IPythonInputSplitter(line_input_checker=True)
605 sp = isp.IPythonInputSplitter(line_input_checker=True)
601
606
602 def test_incremental(self):
607 def test_incremental(self):
603 sp = self.sp
608 sp = self.sp
604 sp.push("%%cellm line2\n")
609 sp.push("%%cellm line2\n")
605 assert sp.push_accepts_more() is True # 1
610 assert sp.push_accepts_more() is True # 1
606 sp.push("\n")
611 sp.push("\n")
607 # In this case, a blank line should end the cell magic
612 # In this case, a blank line should end the cell magic
608 assert sp.push_accepts_more() is False # 2
613 assert sp.push_accepts_more() is False # 2
609
614
610
615
611 indentation_samples = [
616 indentation_samples = [
612 ('a = 1', 0),
617 ('a = 1', 0),
613 ('for a in b:', 4),
618 ('for a in b:', 4),
614 ('def f():', 4),
619 ('def f():', 4),
615 ('def f(): #comment', 4),
620 ('def f(): #comment', 4),
616 ('a = ":#not a comment"', 0),
621 ('a = ":#not a comment"', 0),
617 ('def f():\n a = 1', 4),
622 ('def f():\n a = 1', 4),
618 ('def f():\n return 1', 0),
623 ('def f():\n return 1', 0),
619 ('for a in b:\n'
624 ('for a in b:\n'
620 ' if a < 0:'
625 ' if a < 0:'
621 ' continue', 3),
626 ' continue', 3),
622 ('a = {', 4),
627 ('a = {', 4),
623 ('a = {\n'
628 ('a = {\n'
624 ' 1,', 5),
629 ' 1,', 5),
625 ('b = """123', 0),
630 ('b = """123', 0),
626 ('', 0),
631 ('', 0),
627 ('def f():\n pass', 0),
632 ('def f():\n pass', 0),
628 ('class Bar:\n def f():\n pass', 4),
633 ('class Bar:\n def f():\n pass', 4),
629 ('class Bar:\n def f():\n raise', 4),
634 ('class Bar:\n def f():\n raise', 4),
630 ]
635 ]
631
636
632 def test_find_next_indent():
637 def test_find_next_indent():
633 for code, exp in indentation_samples:
638 for code, exp in indentation_samples:
634 res = isp.find_next_indent(code)
639 res = isp.find_next_indent(code)
635 msg = "{!r} != {!r} (expected)\n Code: {!r}".format(res, exp, code)
640 msg = "{!r} != {!r} (expected)\n Code: {!r}".format(res, exp, code)
636 assert res == exp, msg
641 assert res == exp, msg
@@ -1,388 +1,391 b''
1 """Tests for the token-based transformers in IPython.core.inputtransformer2
1 """Tests for the token-based transformers in IPython.core.inputtransformer2
2
2
3 Line-based transformers are the simpler ones; token-based transformers are
3 Line-based transformers are the simpler ones; token-based transformers are
4 more complex. See test_inputtransformer2_line for tests for line-based
4 more complex. See test_inputtransformer2_line for tests for line-based
5 transformations.
5 transformations.
6 """
6 """
7 import string
7 import string
8 import sys
8 import sys
9 from textwrap import dedent
9 from textwrap import dedent
10
10
11 import pytest
11 import pytest
12
12
13 from IPython.core import inputtransformer2 as ipt2
13 from IPython.core import inputtransformer2 as ipt2
14 from IPython.core.inputtransformer2 import _find_assign_op, make_tokens_by_line
14 from IPython.core.inputtransformer2 import _find_assign_op, make_tokens_by_line
15 from IPython.testing.decorators import skip_iptest_but_not_pytest
15 from IPython.testing.decorators import skip_iptest_but_not_pytest
16
16
17 MULTILINE_MAGIC = ("""\
17 MULTILINE_MAGIC = ("""\
18 a = f()
18 a = f()
19 %foo \\
19 %foo \\
20 bar
20 bar
21 g()
21 g()
22 """.splitlines(keepends=True), (2, 0), """\
22 """.splitlines(keepends=True), (2, 0), """\
23 a = f()
23 a = f()
24 get_ipython().run_line_magic('foo', ' bar')
24 get_ipython().run_line_magic('foo', ' bar')
25 g()
25 g()
26 """.splitlines(keepends=True))
26 """.splitlines(keepends=True))
27
27
28 INDENTED_MAGIC = ("""\
28 INDENTED_MAGIC = ("""\
29 for a in range(5):
29 for a in range(5):
30 %ls
30 %ls
31 """.splitlines(keepends=True), (2, 4), """\
31 """.splitlines(keepends=True), (2, 4), """\
32 for a in range(5):
32 for a in range(5):
33 get_ipython().run_line_magic('ls', '')
33 get_ipython().run_line_magic('ls', '')
34 """.splitlines(keepends=True))
34 """.splitlines(keepends=True))
35
35
36 CRLF_MAGIC = ([
36 CRLF_MAGIC = ([
37 "a = f()\n",
37 "a = f()\n",
38 "%ls\r\n",
38 "%ls\r\n",
39 "g()\n"
39 "g()\n"
40 ], (2, 0), [
40 ], (2, 0), [
41 "a = f()\n",
41 "a = f()\n",
42 "get_ipython().run_line_magic('ls', '')\n",
42 "get_ipython().run_line_magic('ls', '')\n",
43 "g()\n"
43 "g()\n"
44 ])
44 ])
45
45
46 MULTILINE_MAGIC_ASSIGN = ("""\
46 MULTILINE_MAGIC_ASSIGN = ("""\
47 a = f()
47 a = f()
48 b = %foo \\
48 b = %foo \\
49 bar
49 bar
50 g()
50 g()
51 """.splitlines(keepends=True), (2, 4), """\
51 """.splitlines(keepends=True), (2, 4), """\
52 a = f()
52 a = f()
53 b = get_ipython().run_line_magic('foo', ' bar')
53 b = get_ipython().run_line_magic('foo', ' bar')
54 g()
54 g()
55 """.splitlines(keepends=True))
55 """.splitlines(keepends=True))
56
56
57 MULTILINE_SYSTEM_ASSIGN = ("""\
57 MULTILINE_SYSTEM_ASSIGN = ("""\
58 a = f()
58 a = f()
59 b = !foo \\
59 b = !foo \\
60 bar
60 bar
61 g()
61 g()
62 """.splitlines(keepends=True), (2, 4), """\
62 """.splitlines(keepends=True), (2, 4), """\
63 a = f()
63 a = f()
64 b = get_ipython().getoutput('foo bar')
64 b = get_ipython().getoutput('foo bar')
65 g()
65 g()
66 """.splitlines(keepends=True))
66 """.splitlines(keepends=True))
67
67
68 #####
68 #####
69
69
70 MULTILINE_SYSTEM_ASSIGN_AFTER_DEDENT = ("""\
70 MULTILINE_SYSTEM_ASSIGN_AFTER_DEDENT = ("""\
71 def test():
71 def test():
72 for i in range(1):
72 for i in range(1):
73 print(i)
73 print(i)
74 res =! ls
74 res =! ls
75 """.splitlines(keepends=True), (4, 7), '''\
75 """.splitlines(keepends=True), (4, 7), '''\
76 def test():
76 def test():
77 for i in range(1):
77 for i in range(1):
78 print(i)
78 print(i)
79 res =get_ipython().getoutput(\' ls\')
79 res =get_ipython().getoutput(\' ls\')
80 '''.splitlines(keepends=True))
80 '''.splitlines(keepends=True))
81
81
82 ######
82 ######
83
83
84 AUTOCALL_QUOTE = (
84 AUTOCALL_QUOTE = (
85 [",f 1 2 3\n"], (1, 0),
85 [",f 1 2 3\n"], (1, 0),
86 ['f("1", "2", "3")\n']
86 ['f("1", "2", "3")\n']
87 )
87 )
88
88
89 AUTOCALL_QUOTE2 = (
89 AUTOCALL_QUOTE2 = (
90 [";f 1 2 3\n"], (1, 0),
90 [";f 1 2 3\n"], (1, 0),
91 ['f("1 2 3")\n']
91 ['f("1 2 3")\n']
92 )
92 )
93
93
94 AUTOCALL_PAREN = (
94 AUTOCALL_PAREN = (
95 ["/f 1 2 3\n"], (1, 0),
95 ["/f 1 2 3\n"], (1, 0),
96 ['f(1, 2, 3)\n']
96 ['f(1, 2, 3)\n']
97 )
97 )
98
98
99 SIMPLE_HELP = (
99 SIMPLE_HELP = (
100 ["foo?\n"], (1, 0),
100 ["foo?\n"], (1, 0),
101 ["get_ipython().run_line_magic('pinfo', 'foo')\n"]
101 ["get_ipython().run_line_magic('pinfo', 'foo')\n"]
102 )
102 )
103
103
104 DETAILED_HELP = (
104 DETAILED_HELP = (
105 ["foo??\n"], (1, 0),
105 ["foo??\n"], (1, 0),
106 ["get_ipython().run_line_magic('pinfo2', 'foo')\n"]
106 ["get_ipython().run_line_magic('pinfo2', 'foo')\n"]
107 )
107 )
108
108
109 MAGIC_HELP = (
109 MAGIC_HELP = (
110 ["%foo?\n"], (1, 0),
110 ["%foo?\n"], (1, 0),
111 ["get_ipython().run_line_magic('pinfo', '%foo')\n"]
111 ["get_ipython().run_line_magic('pinfo', '%foo')\n"]
112 )
112 )
113
113
114 HELP_IN_EXPR = (
114 HELP_IN_EXPR = (
115 ["a = b + c?\n"], (1, 0),
115 ["a = b + c?\n"], (1, 0),
116 ["get_ipython().set_next_input('a = b + c');"
116 ["get_ipython().set_next_input('a = b + c');"
117 "get_ipython().run_line_magic('pinfo', 'c')\n"]
117 "get_ipython().run_line_magic('pinfo', 'c')\n"]
118 )
118 )
119
119
120 HELP_CONTINUED_LINE = ("""\
120 HELP_CONTINUED_LINE = ("""\
121 a = \\
121 a = \\
122 zip?
122 zip?
123 """.splitlines(keepends=True), (1, 0),
123 """.splitlines(keepends=True), (1, 0),
124 [r"get_ipython().set_next_input('a = \\\nzip');get_ipython().run_line_magic('pinfo', 'zip')" + "\n"]
124 [r"get_ipython().set_next_input('a = \\\nzip');get_ipython().run_line_magic('pinfo', 'zip')" + "\n"]
125 )
125 )
126
126
127 HELP_MULTILINE = ("""\
127 HELP_MULTILINE = ("""\
128 (a,
128 (a,
129 b) = zip?
129 b) = zip?
130 """.splitlines(keepends=True), (1, 0),
130 """.splitlines(keepends=True), (1, 0),
131 [r"get_ipython().set_next_input('(a,\nb) = zip');get_ipython().run_line_magic('pinfo', 'zip')" + "\n"]
131 [r"get_ipython().set_next_input('(a,\nb) = zip');get_ipython().run_line_magic('pinfo', 'zip')" + "\n"]
132 )
132 )
133
133
134 HELP_UNICODE = (
134 HELP_UNICODE = (
135 ["π.foo?\n"], (1, 0),
135 ["π.foo?\n"], (1, 0),
136 ["get_ipython().run_line_magic('pinfo', 'π.foo')\n"]
136 ["get_ipython().run_line_magic('pinfo', 'π.foo')\n"]
137 )
137 )
138
138
139
139
140 def null_cleanup_transformer(lines):
140 def null_cleanup_transformer(lines):
141 """
141 """
142 A cleanup transform that returns an empty list.
142 A cleanup transform that returns an empty list.
143 """
143 """
144 return []
144 return []
145
145
146
146
147 def test_check_make_token_by_line_never_ends_empty():
147 def test_check_make_token_by_line_never_ends_empty():
148 """
148 """
149 Check that not sequence of single or double characters ends up leading to en empty list of tokens
149 Check that not sequence of single or double characters ends up leading to en empty list of tokens
150 """
150 """
151 from string import printable
151 from string import printable
152 for c in printable:
152 for c in printable:
153 assert make_tokens_by_line(c)[-1] != []
153 assert make_tokens_by_line(c)[-1] != []
154 for k in printable:
154 for k in printable:
155 assert make_tokens_by_line(c + k)[-1] != []
155 assert make_tokens_by_line(c + k)[-1] != []
156
156
157
157
158 def check_find(transformer, case, match=True):
158 def check_find(transformer, case, match=True):
159 sample, expected_start, _ = case
159 sample, expected_start, _ = case
160 tbl = make_tokens_by_line(sample)
160 tbl = make_tokens_by_line(sample)
161 res = transformer.find(tbl)
161 res = transformer.find(tbl)
162 if match:
162 if match:
163 # start_line is stored 0-indexed, expected values are 1-indexed
163 # start_line is stored 0-indexed, expected values are 1-indexed
164 assert (res.start_line + 1, res.start_col) == expected_start
164 assert (res.start_line + 1, res.start_col) == expected_start
165 return res
165 return res
166 else:
166 else:
167 assert res is None
167 assert res is None
168
168
169 def check_transform(transformer_cls, case):
169 def check_transform(transformer_cls, case):
170 lines, start, expected = case
170 lines, start, expected = case
171 transformer = transformer_cls(start)
171 transformer = transformer_cls(start)
172 assert transformer.transform(lines) == expected
172 assert transformer.transform(lines) == expected
173
173
174 def test_continued_line():
174 def test_continued_line():
175 lines = MULTILINE_MAGIC_ASSIGN[0]
175 lines = MULTILINE_MAGIC_ASSIGN[0]
176 assert ipt2.find_end_of_continued_line(lines, 1) == 2
176 assert ipt2.find_end_of_continued_line(lines, 1) == 2
177
177
178 assert ipt2.assemble_continued_line(lines, (1, 5), 2) == "foo bar"
178 assert ipt2.assemble_continued_line(lines, (1, 5), 2) == "foo bar"
179
179
180 def test_find_assign_magic():
180 def test_find_assign_magic():
181 check_find(ipt2.MagicAssign, MULTILINE_MAGIC_ASSIGN)
181 check_find(ipt2.MagicAssign, MULTILINE_MAGIC_ASSIGN)
182 check_find(ipt2.MagicAssign, MULTILINE_SYSTEM_ASSIGN, match=False)
182 check_find(ipt2.MagicAssign, MULTILINE_SYSTEM_ASSIGN, match=False)
183 check_find(ipt2.MagicAssign, MULTILINE_SYSTEM_ASSIGN_AFTER_DEDENT, match=False)
183 check_find(ipt2.MagicAssign, MULTILINE_SYSTEM_ASSIGN_AFTER_DEDENT, match=False)
184
184
185 def test_transform_assign_magic():
185 def test_transform_assign_magic():
186 check_transform(ipt2.MagicAssign, MULTILINE_MAGIC_ASSIGN)
186 check_transform(ipt2.MagicAssign, MULTILINE_MAGIC_ASSIGN)
187
187
188 def test_find_assign_system():
188 def test_find_assign_system():
189 check_find(ipt2.SystemAssign, MULTILINE_SYSTEM_ASSIGN)
189 check_find(ipt2.SystemAssign, MULTILINE_SYSTEM_ASSIGN)
190 check_find(ipt2.SystemAssign, MULTILINE_SYSTEM_ASSIGN_AFTER_DEDENT)
190 check_find(ipt2.SystemAssign, MULTILINE_SYSTEM_ASSIGN_AFTER_DEDENT)
191 check_find(ipt2.SystemAssign, (["a = !ls\n"], (1, 5), None))
191 check_find(ipt2.SystemAssign, (["a = !ls\n"], (1, 5), None))
192 check_find(ipt2.SystemAssign, (["a=!ls\n"], (1, 2), None))
192 check_find(ipt2.SystemAssign, (["a=!ls\n"], (1, 2), None))
193 check_find(ipt2.SystemAssign, MULTILINE_MAGIC_ASSIGN, match=False)
193 check_find(ipt2.SystemAssign, MULTILINE_MAGIC_ASSIGN, match=False)
194
194
195 def test_transform_assign_system():
195 def test_transform_assign_system():
196 check_transform(ipt2.SystemAssign, MULTILINE_SYSTEM_ASSIGN)
196 check_transform(ipt2.SystemAssign, MULTILINE_SYSTEM_ASSIGN)
197 check_transform(ipt2.SystemAssign, MULTILINE_SYSTEM_ASSIGN_AFTER_DEDENT)
197 check_transform(ipt2.SystemAssign, MULTILINE_SYSTEM_ASSIGN_AFTER_DEDENT)
198
198
199 def test_find_magic_escape():
199 def test_find_magic_escape():
200 check_find(ipt2.EscapedCommand, MULTILINE_MAGIC)
200 check_find(ipt2.EscapedCommand, MULTILINE_MAGIC)
201 check_find(ipt2.EscapedCommand, INDENTED_MAGIC)
201 check_find(ipt2.EscapedCommand, INDENTED_MAGIC)
202 check_find(ipt2.EscapedCommand, MULTILINE_MAGIC_ASSIGN, match=False)
202 check_find(ipt2.EscapedCommand, MULTILINE_MAGIC_ASSIGN, match=False)
203
203
204 def test_transform_magic_escape():
204 def test_transform_magic_escape():
205 check_transform(ipt2.EscapedCommand, MULTILINE_MAGIC)
205 check_transform(ipt2.EscapedCommand, MULTILINE_MAGIC)
206 check_transform(ipt2.EscapedCommand, INDENTED_MAGIC)
206 check_transform(ipt2.EscapedCommand, INDENTED_MAGIC)
207 check_transform(ipt2.EscapedCommand, CRLF_MAGIC)
207 check_transform(ipt2.EscapedCommand, CRLF_MAGIC)
208
208
209 def test_find_autocalls():
209 def test_find_autocalls():
210 for case in [AUTOCALL_QUOTE, AUTOCALL_QUOTE2, AUTOCALL_PAREN]:
210 for case in [AUTOCALL_QUOTE, AUTOCALL_QUOTE2, AUTOCALL_PAREN]:
211 print("Testing %r" % case[0])
211 print("Testing %r" % case[0])
212 check_find(ipt2.EscapedCommand, case)
212 check_find(ipt2.EscapedCommand, case)
213
213
214 def test_transform_autocall():
214 def test_transform_autocall():
215 for case in [AUTOCALL_QUOTE, AUTOCALL_QUOTE2, AUTOCALL_PAREN]:
215 for case in [AUTOCALL_QUOTE, AUTOCALL_QUOTE2, AUTOCALL_PAREN]:
216 print("Testing %r" % case[0])
216 print("Testing %r" % case[0])
217 check_transform(ipt2.EscapedCommand, case)
217 check_transform(ipt2.EscapedCommand, case)
218
218
219 def test_find_help():
219 def test_find_help():
220 for case in [SIMPLE_HELP, DETAILED_HELP, MAGIC_HELP, HELP_IN_EXPR]:
220 for case in [SIMPLE_HELP, DETAILED_HELP, MAGIC_HELP, HELP_IN_EXPR]:
221 check_find(ipt2.HelpEnd, case)
221 check_find(ipt2.HelpEnd, case)
222
222
223 tf = check_find(ipt2.HelpEnd, HELP_CONTINUED_LINE)
223 tf = check_find(ipt2.HelpEnd, HELP_CONTINUED_LINE)
224 assert tf.q_line == 1
224 assert tf.q_line == 1
225 assert tf.q_col == 3
225 assert tf.q_col == 3
226
226
227 tf = check_find(ipt2.HelpEnd, HELP_MULTILINE)
227 tf = check_find(ipt2.HelpEnd, HELP_MULTILINE)
228 assert tf.q_line == 1
228 assert tf.q_line == 1
229 assert tf.q_col == 8
229 assert tf.q_col == 8
230
230
231 # ? in a comment does not trigger help
231 # ? in a comment does not trigger help
232 check_find(ipt2.HelpEnd, (["foo # bar?\n"], None, None), match=False)
232 check_find(ipt2.HelpEnd, (["foo # bar?\n"], None, None), match=False)
233 # Nor in a string
233 # Nor in a string
234 check_find(ipt2.HelpEnd, (["foo = '''bar?\n"], None, None), match=False)
234 check_find(ipt2.HelpEnd, (["foo = '''bar?\n"], None, None), match=False)
235
235
236 def test_transform_help():
236 def test_transform_help():
237 tf = ipt2.HelpEnd((1, 0), (1, 9))
237 tf = ipt2.HelpEnd((1, 0), (1, 9))
238 assert tf.transform(HELP_IN_EXPR[0]) == HELP_IN_EXPR[2]
238 assert tf.transform(HELP_IN_EXPR[0]) == HELP_IN_EXPR[2]
239
239
240 tf = ipt2.HelpEnd((1, 0), (2, 3))
240 tf = ipt2.HelpEnd((1, 0), (2, 3))
241 assert tf.transform(HELP_CONTINUED_LINE[0]) == HELP_CONTINUED_LINE[2]
241 assert tf.transform(HELP_CONTINUED_LINE[0]) == HELP_CONTINUED_LINE[2]
242
242
243 tf = ipt2.HelpEnd((1, 0), (2, 8))
243 tf = ipt2.HelpEnd((1, 0), (2, 8))
244 assert tf.transform(HELP_MULTILINE[0]) == HELP_MULTILINE[2]
244 assert tf.transform(HELP_MULTILINE[0]) == HELP_MULTILINE[2]
245
245
246 tf = ipt2.HelpEnd((1, 0), (1, 0))
246 tf = ipt2.HelpEnd((1, 0), (1, 0))
247 assert tf.transform(HELP_UNICODE[0]) == HELP_UNICODE[2]
247 assert tf.transform(HELP_UNICODE[0]) == HELP_UNICODE[2]
248
248
249 def test_find_assign_op_dedent():
249 def test_find_assign_op_dedent():
250 """
250 """
251 be careful that empty token like dedent are not counted as parens
251 be careful that empty token like dedent are not counted as parens
252 """
252 """
253 class Tk:
253 class Tk:
254 def __init__(self, s):
254 def __init__(self, s):
255 self.string = s
255 self.string = s
256
256
257 assert _find_assign_op([Tk(s) for s in ("", "a", "=", "b")]) == 2
257 assert _find_assign_op([Tk(s) for s in ("", "a", "=", "b")]) == 2
258 assert (
258 assert (
259 _find_assign_op([Tk(s) for s in ("", "(", "a", "=", "b", ")", "=", "5")]) == 6
259 _find_assign_op([Tk(s) for s in ("", "(", "a", "=", "b", ")", "=", "5")]) == 6
260 )
260 )
261
261
262
262
263 examples = [
263 examples = [
264 pytest.param("a = 1", "complete", None),
264 pytest.param("a = 1", "complete", None),
265 pytest.param("for a in range(5):", "incomplete", 4),
265 pytest.param("for a in range(5):", "incomplete", 4),
266 pytest.param("for a in range(5):\n if a > 0:", "incomplete", 8),
266 pytest.param("for a in range(5):\n if a > 0:", "incomplete", 8),
267 pytest.param("raise = 2", "invalid", None),
267 pytest.param("raise = 2", "invalid", None),
268 pytest.param("a = [1,\n2,", "incomplete", 0),
268 pytest.param("a = [1,\n2,", "incomplete", 0),
269 pytest.param("(\n))", "incomplete", 0),
269 pytest.param("(\n))", "incomplete", 0),
270 pytest.param("\\\r\n", "incomplete", 0),
270 pytest.param("\\\r\n", "incomplete", 0),
271 pytest.param("a = '''\n hi", "incomplete", 3),
271 pytest.param("a = '''\n hi", "incomplete", 3),
272 pytest.param("def a():\n x=1\n global x", "invalid", None),
272 pytest.param("def a():\n x=1\n global x", "invalid", None),
273 pytest.param(
273 pytest.param(
274 "a \\ ",
274 "a \\ ",
275 "invalid",
275 "invalid",
276 None,
276 None,
277 marks=pytest.mark.xfail(
277 marks=pytest.mark.xfail(
278 reason="Bug in python 3.9.8 – bpo 45738",
278 reason="Bug in python 3.9.8 – bpo 45738",
279 condition=sys.version_info[:3] == (3, 9, 8),
279 condition=sys.version_info
280 in [(3, 9, 8, "final", 0), (3, 11, 0, "alpha", 2)],
280 raises=SystemError,
281 raises=SystemError,
281 strict=True,
282 strict=True,
282 ),
283 ),
283 ), # Nothing allowed after backslash,
284 ), # Nothing allowed after backslash,
284 pytest.param("1\\\n+2", "complete", None),
285 pytest.param("1\\\n+2", "complete", None),
285 ]
286 ]
286
287
287
288
288 @skip_iptest_but_not_pytest
289 @skip_iptest_but_not_pytest
289 @pytest.mark.parametrize("code, expected, number", examples)
290 @pytest.mark.parametrize("code, expected, number", examples)
290 def test_check_complete_param(code, expected, number):
291 def test_check_complete_param(code, expected, number):
291 cc = ipt2.TransformerManager().check_complete
292 cc = ipt2.TransformerManager().check_complete
292 assert cc(code) == (expected, number)
293 assert cc(code) == (expected, number)
293
294
294
295
295 @skip_iptest_but_not_pytest
296 @skip_iptest_but_not_pytest
296 @pytest.mark.xfail(
297 @pytest.mark.xfail(
297 reason="Bug in python 3.9.8 – bpo 45738",
298 reason="Bug in python 3.9.8 – bpo 45738",
298 condition=sys.version_info[:3] == (3, 9, 8),
299 condition=sys.version_info in [(3, 9, 8, "final", 0), (3, 11, 0, "alpha", 2)],
300 raises=SystemError,
301 strict=True,
299 )
302 )
300 def test_check_complete():
303 def test_check_complete():
301 cc = ipt2.TransformerManager().check_complete
304 cc = ipt2.TransformerManager().check_complete
302
305
303 example = dedent("""
306 example = dedent("""
304 if True:
307 if True:
305 a=1""" )
308 a=1""" )
306
309
307 assert cc(example) == ("incomplete", 4)
310 assert cc(example) == ("incomplete", 4)
308 assert cc(example + "\n") == ("complete", None)
311 assert cc(example + "\n") == ("complete", None)
309 assert cc(example + "\n ") == ("complete", None)
312 assert cc(example + "\n ") == ("complete", None)
310
313
311 # no need to loop on all the letters/numbers.
314 # no need to loop on all the letters/numbers.
312 short = '12abAB'+string.printable[62:]
315 short = '12abAB'+string.printable[62:]
313 for c in short:
316 for c in short:
314 # test does not raise:
317 # test does not raise:
315 cc(c)
318 cc(c)
316 for k in short:
319 for k in short:
317 cc(c+k)
320 cc(c+k)
318
321
319 assert cc("def f():\n x=0\n \\\n ") == ("incomplete", 2)
322 assert cc("def f():\n x=0\n \\\n ") == ("incomplete", 2)
320
323
321
324
322 def test_check_complete_II():
325 def test_check_complete_II():
323 """
326 """
324 Test that multiple line strings are properly handled.
327 Test that multiple line strings are properly handled.
325
328
326 Separate test function for convenience
329 Separate test function for convenience
327
330
328 """
331 """
329 cc = ipt2.TransformerManager().check_complete
332 cc = ipt2.TransformerManager().check_complete
330 assert cc('''def foo():\n """''') == ("incomplete", 4)
333 assert cc('''def foo():\n """''') == ("incomplete", 4)
331
334
332
335
333 def test_check_complete_invalidates_sunken_brackets():
336 def test_check_complete_invalidates_sunken_brackets():
334 """
337 """
335 Test that a single line with more closing brackets than the opening ones is
338 Test that a single line with more closing brackets than the opening ones is
336 interpreted as invalid
339 interpreted as invalid
337 """
340 """
338 cc = ipt2.TransformerManager().check_complete
341 cc = ipt2.TransformerManager().check_complete
339 assert cc(")") == ("invalid", None)
342 assert cc(")") == ("invalid", None)
340 assert cc("]") == ("invalid", None)
343 assert cc("]") == ("invalid", None)
341 assert cc("}") == ("invalid", None)
344 assert cc("}") == ("invalid", None)
342 assert cc(")(") == ("invalid", None)
345 assert cc(")(") == ("invalid", None)
343 assert cc("][") == ("invalid", None)
346 assert cc("][") == ("invalid", None)
344 assert cc("}{") == ("invalid", None)
347 assert cc("}{") == ("invalid", None)
345 assert cc("]()(") == ("invalid", None)
348 assert cc("]()(") == ("invalid", None)
346 assert cc("())(") == ("invalid", None)
349 assert cc("())(") == ("invalid", None)
347 assert cc(")[](") == ("invalid", None)
350 assert cc(")[](") == ("invalid", None)
348 assert cc("()](") == ("invalid", None)
351 assert cc("()](") == ("invalid", None)
349
352
350
353
351 def test_null_cleanup_transformer():
354 def test_null_cleanup_transformer():
352 manager = ipt2.TransformerManager()
355 manager = ipt2.TransformerManager()
353 manager.cleanup_transforms.insert(0, null_cleanup_transformer)
356 manager.cleanup_transforms.insert(0, null_cleanup_transformer)
354 assert manager.transform_cell("") == ""
357 assert manager.transform_cell("") == ""
355
358
356
359
357
360
358
361
359 def test_side_effects_I():
362 def test_side_effects_I():
360 count = 0
363 count = 0
361 def counter(lines):
364 def counter(lines):
362 nonlocal count
365 nonlocal count
363 count += 1
366 count += 1
364 return lines
367 return lines
365
368
366 counter.has_side_effects = True
369 counter.has_side_effects = True
367
370
368 manager = ipt2.TransformerManager()
371 manager = ipt2.TransformerManager()
369 manager.cleanup_transforms.insert(0, counter)
372 manager.cleanup_transforms.insert(0, counter)
370 assert manager.check_complete("a=1\n") == ('complete', None)
373 assert manager.check_complete("a=1\n") == ('complete', None)
371 assert count == 0
374 assert count == 0
372
375
373
376
374
377
375
378
376 def test_side_effects_II():
379 def test_side_effects_II():
377 count = 0
380 count = 0
378 def counter(lines):
381 def counter(lines):
379 nonlocal count
382 nonlocal count
380 count += 1
383 count += 1
381 return lines
384 return lines
382
385
383 counter.has_side_effects = True
386 counter.has_side_effects = True
384
387
385 manager = ipt2.TransformerManager()
388 manager = ipt2.TransformerManager()
386 manager.line_transforms.insert(0, counter)
389 manager.line_transforms.insert(0, counter)
387 assert manager.check_complete("b=1\n") == ('complete', None)
390 assert manager.check_complete("b=1\n") == ('complete', None)
388 assert count == 0
391 assert count == 0
@@ -1,454 +1,453 b''
1 """Tests for the object inspection functionality.
1 """Tests for the object inspection functionality.
2 """
2 """
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
7
8 from inspect import signature, Signature, Parameter
8 from inspect import signature, Signature, Parameter
9 import os
9 import os
10 import re
10 import re
11
11
12 from .. import oinspect
12 from .. import oinspect
13
13
14 from decorator import decorator
14 from decorator import decorator
15
15
16 from IPython.testing.tools import AssertPrints, AssertNotPrints
16 from IPython.testing.tools import AssertPrints, AssertNotPrints
17 from IPython.utils.path import compress_user
17 from IPython.utils.path import compress_user
18
18
19
19
20 #-----------------------------------------------------------------------------
20 #-----------------------------------------------------------------------------
21 # Globals and constants
21 # Globals and constants
22 #-----------------------------------------------------------------------------
22 #-----------------------------------------------------------------------------
23
23
24 inspector = None
24 inspector = None
25
25
26 def setup_module():
26 def setup_module():
27 global inspector
27 global inspector
28 inspector = oinspect.Inspector()
28 inspector = oinspect.Inspector()
29
29
30
30
31 #-----------------------------------------------------------------------------
31 #-----------------------------------------------------------------------------
32 # Local utilities
32 # Local utilities
33 #-----------------------------------------------------------------------------
33 #-----------------------------------------------------------------------------
34
34
35 # WARNING: since this test checks the line number where a function is
35 # WARNING: since this test checks the line number where a function is
36 # defined, if any code is inserted above, the following line will need to be
36 # defined, if any code is inserted above, the following line will need to be
37 # updated. Do NOT insert any whitespace between the next line and the function
37 # updated. Do NOT insert any whitespace between the next line and the function
38 # definition below.
38 # definition below.
39 THIS_LINE_NUMBER = 39 # Put here the actual number of this line
39 THIS_LINE_NUMBER = 39 # Put here the actual number of this line
40
40
41 from unittest import TestCase
41 from unittest import TestCase
42
42
43 class Test(TestCase):
43 class Test(TestCase):
44
44
45 def test_find_source_lines(self):
45 def test_find_source_lines(self):
46 self.assertEqual(oinspect.find_source_lines(Test.test_find_source_lines),
46 self.assertEqual(oinspect.find_source_lines(Test.test_find_source_lines),
47 THIS_LINE_NUMBER+6)
47 THIS_LINE_NUMBER+6)
48
48
49
49
50 # A couple of utilities to ensure these tests work the same from a source or a
50 # A couple of utilities to ensure these tests work the same from a source or a
51 # binary install
51 # binary install
52 def pyfile(fname):
52 def pyfile(fname):
53 return os.path.normcase(re.sub('.py[co]$', '.py', fname))
53 return os.path.normcase(re.sub('.py[co]$', '.py', fname))
54
54
55
55
56 def match_pyfiles(f1, f2):
56 def match_pyfiles(f1, f2):
57 assert pyfile(f1) == pyfile(f2)
57 assert pyfile(f1) == pyfile(f2)
58
58
59
59
60 def test_find_file():
60 def test_find_file():
61 match_pyfiles(oinspect.find_file(test_find_file), os.path.abspath(__file__))
61 match_pyfiles(oinspect.find_file(test_find_file), os.path.abspath(__file__))
62
62
63
63
64 def test_find_file_decorated1():
64 def test_find_file_decorated1():
65
65
66 @decorator
66 @decorator
67 def noop1(f):
67 def noop1(f):
68 def wrapper(*a, **kw):
68 def wrapper(*a, **kw):
69 return f(*a, **kw)
69 return f(*a, **kw)
70 return wrapper
70 return wrapper
71
71
72 @noop1
72 @noop1
73 def f(x):
73 def f(x):
74 "My docstring"
74 "My docstring"
75
75
76 match_pyfiles(oinspect.find_file(f), os.path.abspath(__file__))
76 match_pyfiles(oinspect.find_file(f), os.path.abspath(__file__))
77 assert f.__doc__ == "My docstring"
77 assert f.__doc__ == "My docstring"
78
78
79
79
80 def test_find_file_decorated2():
80 def test_find_file_decorated2():
81
81
82 @decorator
82 @decorator
83 def noop2(f, *a, **kw):
83 def noop2(f, *a, **kw):
84 return f(*a, **kw)
84 return f(*a, **kw)
85
85
86 @noop2
86 @noop2
87 @noop2
87 @noop2
88 @noop2
88 @noop2
89 def f(x):
89 def f(x):
90 "My docstring 2"
90 "My docstring 2"
91
91
92 match_pyfiles(oinspect.find_file(f), os.path.abspath(__file__))
92 match_pyfiles(oinspect.find_file(f), os.path.abspath(__file__))
93 assert f.__doc__ == "My docstring 2"
93 assert f.__doc__ == "My docstring 2"
94
94
95
95
96 def test_find_file_magic():
96 def test_find_file_magic():
97 run = ip.find_line_magic('run')
97 run = ip.find_line_magic('run')
98 assert oinspect.find_file(run) is not None
98 assert oinspect.find_file(run) is not None
99
99
100
100
101 # A few generic objects we can then inspect in the tests below
101 # A few generic objects we can then inspect in the tests below
102
102
103 class Call(object):
103 class Call(object):
104 """This is the class docstring."""
104 """This is the class docstring."""
105
105
106 def __init__(self, x, y=1):
106 def __init__(self, x, y=1):
107 """This is the constructor docstring."""
107 """This is the constructor docstring."""
108
108
109 def __call__(self, *a, **kw):
109 def __call__(self, *a, **kw):
110 """This is the call docstring."""
110 """This is the call docstring."""
111
111
112 def method(self, x, z=2):
112 def method(self, x, z=2):
113 """Some method's docstring"""
113 """Some method's docstring"""
114
114
115 class HasSignature(object):
115 class HasSignature(object):
116 """This is the class docstring."""
116 """This is the class docstring."""
117 __signature__ = Signature([Parameter('test', Parameter.POSITIONAL_OR_KEYWORD)])
117 __signature__ = Signature([Parameter('test', Parameter.POSITIONAL_OR_KEYWORD)])
118
118
119 def __init__(self, *args):
119 def __init__(self, *args):
120 """This is the init docstring"""
120 """This is the init docstring"""
121
121
122
122
123 class SimpleClass(object):
123 class SimpleClass(object):
124 def method(self, x, z=2):
124 def method(self, x, z=2):
125 """Some method's docstring"""
125 """Some method's docstring"""
126
126
127
127
128 class Awkward(object):
128 class Awkward(object):
129 def __getattr__(self, name):
129 def __getattr__(self, name):
130 raise Exception(name)
130 raise Exception(name)
131
131
132 class NoBoolCall:
132 class NoBoolCall:
133 """
133 """
134 callable with `__bool__` raising should still be inspect-able.
134 callable with `__bool__` raising should still be inspect-able.
135 """
135 """
136
136
137 def __call__(self):
137 def __call__(self):
138 """does nothing"""
138 """does nothing"""
139 pass
139 pass
140
140
141 def __bool__(self):
141 def __bool__(self):
142 """just raise NotImplemented"""
142 """just raise NotImplemented"""
143 raise NotImplementedError('Must be implemented')
143 raise NotImplementedError('Must be implemented')
144
144
145
145
146 class SerialLiar(object):
146 class SerialLiar(object):
147 """Attribute accesses always get another copy of the same class.
147 """Attribute accesses always get another copy of the same class.
148
148
149 unittest.mock.call does something similar, but it's not ideal for testing
149 unittest.mock.call does something similar, but it's not ideal for testing
150 as the failure mode is to eat all your RAM. This gives up after 10k levels.
150 as the failure mode is to eat all your RAM. This gives up after 10k levels.
151 """
151 """
152 def __init__(self, max_fibbing_twig, lies_told=0):
152 def __init__(self, max_fibbing_twig, lies_told=0):
153 if lies_told > 10000:
153 if lies_told > 10000:
154 raise RuntimeError('Nose too long, honesty is the best policy')
154 raise RuntimeError('Nose too long, honesty is the best policy')
155 self.max_fibbing_twig = max_fibbing_twig
155 self.max_fibbing_twig = max_fibbing_twig
156 self.lies_told = lies_told
156 self.lies_told = lies_told
157 max_fibbing_twig[0] = max(max_fibbing_twig[0], lies_told)
157 max_fibbing_twig[0] = max(max_fibbing_twig[0], lies_told)
158
158
159 def __getattr__(self, item):
159 def __getattr__(self, item):
160 return SerialLiar(self.max_fibbing_twig, self.lies_told + 1)
160 return SerialLiar(self.max_fibbing_twig, self.lies_told + 1)
161
161
162 #-----------------------------------------------------------------------------
162 #-----------------------------------------------------------------------------
163 # Tests
163 # Tests
164 #-----------------------------------------------------------------------------
164 #-----------------------------------------------------------------------------
165
165
166 def test_info():
166 def test_info():
167 "Check that Inspector.info fills out various fields as expected."
167 "Check that Inspector.info fills out various fields as expected."
168 i = inspector.info(Call, oname="Call")
168 i = inspector.info(Call, oname="Call")
169 assert i["type_name"] == "type"
169 assert i["type_name"] == "type"
170 expected_class = str(type(type)) # <class 'type'> (Python 3) or <type 'type'>
170 expected_class = str(type(type)) # <class 'type'> (Python 3) or <type 'type'>
171 assert i["base_class"] == expected_class
171 assert i["base_class"] == expected_class
172 assert re.search(
172 assert re.search(
173 "<class 'IPython.core.tests.test_oinspect.Call'( at 0x[0-9a-f]{1,9})?>",
173 "<class 'IPython.core.tests.test_oinspect.Call'( at 0x[0-9a-f]{1,9})?>",
174 i["string_form"],
174 i["string_form"],
175 )
175 )
176 fname = __file__
176 fname = __file__
177 if fname.endswith(".pyc"):
177 if fname.endswith(".pyc"):
178 fname = fname[:-1]
178 fname = fname[:-1]
179 # case-insensitive comparison needed on some filesystems
179 # case-insensitive comparison needed on some filesystems
180 # e.g. Windows:
180 # e.g. Windows:
181 assert i["file"].lower() == compress_user(fname).lower()
181 assert i["file"].lower() == compress_user(fname).lower()
182 assert i["definition"] == None
182 assert i["definition"] == None
183 assert i["docstring"] == Call.__doc__
183 assert i["docstring"] == Call.__doc__
184 assert i["source"] == None
184 assert i["source"] == None
185 assert i["isclass"] is True
185 assert i["isclass"] is True
186 assert i["init_definition"] == "Call(x, y=1)"
186 assert i["init_definition"] == "Call(x, y=1)"
187 assert i["init_docstring"] == Call.__init__.__doc__
187 assert i["init_docstring"] == Call.__init__.__doc__
188
188
189 i = inspector.info(Call, detail_level=1)
189 i = inspector.info(Call, detail_level=1)
190 assert i["source"] is not None
190 assert i["source"] is not None
191 assert i["docstring"] == None
191 assert i["docstring"] == None
192
192
193 c = Call(1)
193 c = Call(1)
194 c.__doc__ = "Modified instance docstring"
194 c.__doc__ = "Modified instance docstring"
195 i = inspector.info(c)
195 i = inspector.info(c)
196 assert i["type_name"] == "Call"
196 assert i["type_name"] == "Call"
197 assert i["docstring"] == "Modified instance docstring"
197 assert i["docstring"] == "Modified instance docstring"
198 assert i["class_docstring"] == Call.__doc__
198 assert i["class_docstring"] == Call.__doc__
199 assert i["init_docstring"] == Call.__init__.__doc__
199 assert i["init_docstring"] == Call.__init__.__doc__
200 assert i["call_docstring"] == Call.__call__.__doc__
200 assert i["call_docstring"] == Call.__call__.__doc__
201
201
202
202
203 def test_class_signature():
203 def test_class_signature():
204 info = inspector.info(HasSignature, "HasSignature")
204 info = inspector.info(HasSignature, "HasSignature")
205 assert info["init_definition"] == "HasSignature(test)"
205 assert info["init_definition"] == "HasSignature(test)"
206 assert info["init_docstring"] == HasSignature.__init__.__doc__
206 assert info["init_docstring"] == HasSignature.__init__.__doc__
207
207
208
208
209 def test_info_awkward():
209 def test_info_awkward():
210 # Just test that this doesn't throw an error.
210 # Just test that this doesn't throw an error.
211 inspector.info(Awkward())
211 inspector.info(Awkward())
212
212
213 def test_bool_raise():
213 def test_bool_raise():
214 inspector.info(NoBoolCall())
214 inspector.info(NoBoolCall())
215
215
216 def test_info_serialliar():
216 def test_info_serialliar():
217 fib_tracker = [0]
217 fib_tracker = [0]
218 inspector.info(SerialLiar(fib_tracker))
218 inspector.info(SerialLiar(fib_tracker))
219
219
220 # Nested attribute access should be cut off at 100 levels deep to avoid
220 # Nested attribute access should be cut off at 100 levels deep to avoid
221 # infinite loops: https://github.com/ipython/ipython/issues/9122
221 # infinite loops: https://github.com/ipython/ipython/issues/9122
222 assert fib_tracker[0] < 9000
222 assert fib_tracker[0] < 9000
223
223
224 def support_function_one(x, y=2, *a, **kw):
224 def support_function_one(x, y=2, *a, **kw):
225 """A simple function."""
225 """A simple function."""
226
226
227 def test_calldef_none():
227 def test_calldef_none():
228 # We should ignore __call__ for all of these.
228 # We should ignore __call__ for all of these.
229 for obj in [support_function_one, SimpleClass().method, any, str.upper]:
229 for obj in [support_function_one, SimpleClass().method, any, str.upper]:
230 i = inspector.info(obj)
230 i = inspector.info(obj)
231 assert i["call_def"] is None
231 assert i["call_def"] is None
232
232
233
233
234 def f_kwarg(pos, *, kwonly):
234 def f_kwarg(pos, *, kwonly):
235 pass
235 pass
236
236
237 def test_definition_kwonlyargs():
237 def test_definition_kwonlyargs():
238 i = inspector.info(f_kwarg, oname="f_kwarg") # analysis:ignore
238 i = inspector.info(f_kwarg, oname="f_kwarg") # analysis:ignore
239 assert i["definition"] == "f_kwarg(pos, *, kwonly)"
239 assert i["definition"] == "f_kwarg(pos, *, kwonly)"
240
240
241
241
242 def test_getdoc():
242 def test_getdoc():
243 class A(object):
243 class A(object):
244 """standard docstring"""
244 """standard docstring"""
245 pass
245 pass
246
246
247 class B(object):
247 class B(object):
248 """standard docstring"""
248 """standard docstring"""
249 def getdoc(self):
249 def getdoc(self):
250 return "custom docstring"
250 return "custom docstring"
251
251
252 class C(object):
252 class C(object):
253 """standard docstring"""
253 """standard docstring"""
254 def getdoc(self):
254 def getdoc(self):
255 return None
255 return None
256
256
257 a = A()
257 a = A()
258 b = B()
258 b = B()
259 c = C()
259 c = C()
260
260
261 assert oinspect.getdoc(a) == "standard docstring"
261 assert oinspect.getdoc(a) == "standard docstring"
262 assert oinspect.getdoc(b) == "custom docstring"
262 assert oinspect.getdoc(b) == "custom docstring"
263 assert oinspect.getdoc(c) == "standard docstring"
263 assert oinspect.getdoc(c) == "standard docstring"
264
264
265
265
266 def test_empty_property_has_no_source():
266 def test_empty_property_has_no_source():
267 i = inspector.info(property(), detail_level=1)
267 i = inspector.info(property(), detail_level=1)
268 assert i["source"] is None
268 assert i["source"] is None
269
269
270
270
271 def test_property_sources():
271 def test_property_sources():
272 import posixpath
273 # A simple adder whose source and signature stays
272 # A simple adder whose source and signature stays
274 # the same across Python distributions
273 # the same across Python distributions
275 def simple_add(a, b):
274 def simple_add(a, b):
276 "Adds two numbers"
275 "Adds two numbers"
277 return a + b
276 return a + b
278
277
279 class A(object):
278 class A(object):
280 @property
279 @property
281 def foo(self):
280 def foo(self):
282 return 'bar'
281 return 'bar'
283
282
284 foo = foo.setter(lambda self, v: setattr(self, 'bar', v))
283 foo = foo.setter(lambda self, v: setattr(self, 'bar', v))
285
284
286 dname = property(posixpath.dirname)
285 dname = property(oinspect.getdoc)
287 adder = property(simple_add)
286 adder = property(simple_add)
288
287
289 i = inspector.info(A.foo, detail_level=1)
288 i = inspector.info(A.foo, detail_level=1)
290 assert "def foo(self):" in i["source"]
289 assert "def foo(self):" in i["source"]
291 assert "lambda self, v:" in i["source"]
290 assert "lambda self, v:" in i["source"]
292
291
293 i = inspector.info(A.dname, detail_level=1)
292 i = inspector.info(A.dname, detail_level=1)
294 assert "def dirname(p)" in i["source"]
293 assert "def getdoc(obj)" in i["source"]
295
294
296 i = inspector.info(A.adder, detail_level=1)
295 i = inspector.info(A.adder, detail_level=1)
297 assert "def simple_add(a, b)" in i["source"]
296 assert "def simple_add(a, b)" in i["source"]
298
297
299
298
300 def test_property_docstring_is_in_info_for_detail_level_0():
299 def test_property_docstring_is_in_info_for_detail_level_0():
301 class A(object):
300 class A(object):
302 @property
301 @property
303 def foobar(self):
302 def foobar(self):
304 """This is `foobar` property."""
303 """This is `foobar` property."""
305 pass
304 pass
306
305
307 ip.user_ns["a_obj"] = A()
306 ip.user_ns["a_obj"] = A()
308 assert (
307 assert (
309 "This is `foobar` property."
308 "This is `foobar` property."
310 == ip.object_inspect("a_obj.foobar", detail_level=0)["docstring"]
309 == ip.object_inspect("a_obj.foobar", detail_level=0)["docstring"]
311 )
310 )
312
311
313 ip.user_ns["a_cls"] = A
312 ip.user_ns["a_cls"] = A
314 assert (
313 assert (
315 "This is `foobar` property."
314 "This is `foobar` property."
316 == ip.object_inspect("a_cls.foobar", detail_level=0)["docstring"]
315 == ip.object_inspect("a_cls.foobar", detail_level=0)["docstring"]
317 )
316 )
318
317
319
318
320 def test_pdef():
319 def test_pdef():
321 # See gh-1914
320 # See gh-1914
322 def foo(): pass
321 def foo(): pass
323 inspector.pdef(foo, 'foo')
322 inspector.pdef(foo, 'foo')
324
323
325
324
326 def test_pinfo_nonascii():
325 def test_pinfo_nonascii():
327 # See gh-1177
326 # See gh-1177
328 from . import nonascii2
327 from . import nonascii2
329 ip.user_ns['nonascii2'] = nonascii2
328 ip.user_ns['nonascii2'] = nonascii2
330 ip._inspect('pinfo', 'nonascii2', detail_level=1)
329 ip._inspect('pinfo', 'nonascii2', detail_level=1)
331
330
332 def test_pinfo_type():
331 def test_pinfo_type():
333 """
332 """
334 type can fail in various edge case, for example `type.__subclass__()`
333 type can fail in various edge case, for example `type.__subclass__()`
335 """
334 """
336 ip._inspect('pinfo', 'type')
335 ip._inspect('pinfo', 'type')
337
336
338
337
339 def test_pinfo_docstring_no_source():
338 def test_pinfo_docstring_no_source():
340 """Docstring should be included with detail_level=1 if there is no source"""
339 """Docstring should be included with detail_level=1 if there is no source"""
341 with AssertPrints('Docstring:'):
340 with AssertPrints('Docstring:'):
342 ip._inspect('pinfo', 'str.format', detail_level=0)
341 ip._inspect('pinfo', 'str.format', detail_level=0)
343 with AssertPrints('Docstring:'):
342 with AssertPrints('Docstring:'):
344 ip._inspect('pinfo', 'str.format', detail_level=1)
343 ip._inspect('pinfo', 'str.format', detail_level=1)
345
344
346
345
347 def test_pinfo_no_docstring_if_source():
346 def test_pinfo_no_docstring_if_source():
348 """Docstring should not be included with detail_level=1 if source is found"""
347 """Docstring should not be included with detail_level=1 if source is found"""
349 def foo():
348 def foo():
350 """foo has a docstring"""
349 """foo has a docstring"""
351
350
352 ip.user_ns['foo'] = foo
351 ip.user_ns['foo'] = foo
353
352
354 with AssertPrints('Docstring:'):
353 with AssertPrints('Docstring:'):
355 ip._inspect('pinfo', 'foo', detail_level=0)
354 ip._inspect('pinfo', 'foo', detail_level=0)
356 with AssertPrints('Source:'):
355 with AssertPrints('Source:'):
357 ip._inspect('pinfo', 'foo', detail_level=1)
356 ip._inspect('pinfo', 'foo', detail_level=1)
358 with AssertNotPrints('Docstring:'):
357 with AssertNotPrints('Docstring:'):
359 ip._inspect('pinfo', 'foo', detail_level=1)
358 ip._inspect('pinfo', 'foo', detail_level=1)
360
359
361
360
362 def test_pinfo_docstring_if_detail_and_no_source():
361 def test_pinfo_docstring_if_detail_and_no_source():
363 """ Docstring should be displayed if source info not available """
362 """ Docstring should be displayed if source info not available """
364 obj_def = '''class Foo(object):
363 obj_def = '''class Foo(object):
365 """ This is a docstring for Foo """
364 """ This is a docstring for Foo """
366 def bar(self):
365 def bar(self):
367 """ This is a docstring for Foo.bar """
366 """ This is a docstring for Foo.bar """
368 pass
367 pass
369 '''
368 '''
370
369
371 ip.run_cell(obj_def)
370 ip.run_cell(obj_def)
372 ip.run_cell('foo = Foo()')
371 ip.run_cell('foo = Foo()')
373
372
374 with AssertNotPrints("Source:"):
373 with AssertNotPrints("Source:"):
375 with AssertPrints('Docstring:'):
374 with AssertPrints('Docstring:'):
376 ip._inspect('pinfo', 'foo', detail_level=0)
375 ip._inspect('pinfo', 'foo', detail_level=0)
377 with AssertPrints('Docstring:'):
376 with AssertPrints('Docstring:'):
378 ip._inspect('pinfo', 'foo', detail_level=1)
377 ip._inspect('pinfo', 'foo', detail_level=1)
379 with AssertPrints('Docstring:'):
378 with AssertPrints('Docstring:'):
380 ip._inspect('pinfo', 'foo.bar', detail_level=0)
379 ip._inspect('pinfo', 'foo.bar', detail_level=0)
381
380
382 with AssertNotPrints('Docstring:'):
381 with AssertNotPrints('Docstring:'):
383 with AssertPrints('Source:'):
382 with AssertPrints('Source:'):
384 ip._inspect('pinfo', 'foo.bar', detail_level=1)
383 ip._inspect('pinfo', 'foo.bar', detail_level=1)
385
384
386
385
387 def test_pinfo_magic():
386 def test_pinfo_magic():
388 with AssertPrints('Docstring:'):
387 with AssertPrints('Docstring:'):
389 ip._inspect('pinfo', 'lsmagic', detail_level=0)
388 ip._inspect('pinfo', 'lsmagic', detail_level=0)
390
389
391 with AssertPrints('Source:'):
390 with AssertPrints('Source:'):
392 ip._inspect('pinfo', 'lsmagic', detail_level=1)
391 ip._inspect('pinfo', 'lsmagic', detail_level=1)
393
392
394
393
395 def test_init_colors():
394 def test_init_colors():
396 # ensure colors are not present in signature info
395 # ensure colors are not present in signature info
397 info = inspector.info(HasSignature)
396 info = inspector.info(HasSignature)
398 init_def = info["init_definition"]
397 init_def = info["init_definition"]
399 assert "[0m" not in init_def
398 assert "[0m" not in init_def
400
399
401
400
402 def test_builtin_init():
401 def test_builtin_init():
403 info = inspector.info(list)
402 info = inspector.info(list)
404 init_def = info['init_definition']
403 init_def = info['init_definition']
405 assert init_def is not None
404 assert init_def is not None
406
405
407
406
408 def test_render_signature_short():
407 def test_render_signature_short():
409 def short_fun(a=1): pass
408 def short_fun(a=1): pass
410 sig = oinspect._render_signature(
409 sig = oinspect._render_signature(
411 signature(short_fun),
410 signature(short_fun),
412 short_fun.__name__,
411 short_fun.__name__,
413 )
412 )
414 assert sig == "short_fun(a=1)"
413 assert sig == "short_fun(a=1)"
415
414
416
415
417 def test_render_signature_long():
416 def test_render_signature_long():
418 from typing import Optional
417 from typing import Optional
419
418
420 def long_function(
419 def long_function(
421 a_really_long_parameter: int,
420 a_really_long_parameter: int,
422 and_another_long_one: bool = False,
421 and_another_long_one: bool = False,
423 let_us_make_sure_this_is_looong: Optional[str] = None,
422 let_us_make_sure_this_is_looong: Optional[str] = None,
424 ) -> bool: pass
423 ) -> bool: pass
425
424
426 sig = oinspect._render_signature(
425 sig = oinspect._render_signature(
427 signature(long_function),
426 signature(long_function),
428 long_function.__name__,
427 long_function.__name__,
429 )
428 )
430 assert sig in [
429 assert sig in [
431 # Python >=3.9
430 # Python >=3.9
432 '''\
431 '''\
433 long_function(
432 long_function(
434 a_really_long_parameter: int,
433 a_really_long_parameter: int,
435 and_another_long_one: bool = False,
434 and_another_long_one: bool = False,
436 let_us_make_sure_this_is_looong: Optional[str] = None,
435 let_us_make_sure_this_is_looong: Optional[str] = None,
437 ) -> bool\
436 ) -> bool\
438 ''',
437 ''',
439 # Python >=3.7
438 # Python >=3.7
440 '''\
439 '''\
441 long_function(
440 long_function(
442 a_really_long_parameter: int,
441 a_really_long_parameter: int,
443 and_another_long_one: bool = False,
442 and_another_long_one: bool = False,
444 let_us_make_sure_this_is_looong: Union[str, NoneType] = None,
443 let_us_make_sure_this_is_looong: Union[str, NoneType] = None,
445 ) -> bool\
444 ) -> bool\
446 ''', # Python <=3.6
445 ''', # Python <=3.6
447 '''\
446 '''\
448 long_function(
447 long_function(
449 a_really_long_parameter:int,
448 a_really_long_parameter:int,
450 and_another_long_one:bool=False,
449 and_another_long_one:bool=False,
451 let_us_make_sure_this_is_looong:Union[str, NoneType]=None,
450 let_us_make_sure_this_is_looong:Union[str, NoneType]=None,
452 ) -> bool\
451 ) -> bool\
453 ''',
452 ''',
454 ]
453 ]
General Comments 0
You need to be logged in to leave comments. Login now