##// END OF EJS Templates
Fix autocall runs on getitem. (#14486)...
M Bussonnier -
r28820:128bd582 merge
parent child Browse files
Show More
@@ -476,8 +476,8 b' class PythonOpsChecker(PrefilterChecker):'
476 any python operator, we should simply execute the line (regardless of
476 any python operator, we should simply execute the line (regardless of
477 whether or not there's a possible autocall expansion). This avoids
477 whether or not there's a possible autocall expansion). This avoids
478 spurious (and very confusing) geattr() accesses."""
478 spurious (and very confusing) geattr() accesses."""
479 if line_info.the_rest and line_info.the_rest[0] in '!=()<>,+*/%^&|':
479 if line_info.the_rest and line_info.the_rest[0] in "!=()<>,+*/%^&|":
480 return self.prefilter_manager.get_handler_by_name('normal')
480 return self.prefilter_manager.get_handler_by_name("normal")
481 else:
481 else:
482 return None
482 return None
483
483
@@ -512,6 +512,8 b' class AutocallChecker(PrefilterChecker):'
512 callable(oinfo.obj)
512 callable(oinfo.obj)
513 and (not self.exclude_regexp.match(line_info.the_rest))
513 and (not self.exclude_regexp.match(line_info.the_rest))
514 and self.function_name_regexp.match(line_info.ifun)
514 and self.function_name_regexp.match(line_info.ifun)
515 and line_info.raw_the_rest.startswith(" ")
516 or not line_info.raw_the_rest.strip()
515 ):
517 ):
516 return self.prefilter_manager.get_handler_by_name("auto")
518 return self.prefilter_manager.get_handler_by_name("auto")
517 else:
519 else:
@@ -76,7 +76,7 b' def split_user_input(line, pattern=None):'
76
76
77 # print('line:<%s>' % line) # dbg
77 # print('line:<%s>' % line) # dbg
78 # print('pre <%s> ifun <%s> rest <%s>' % (pre,ifun.strip(),the_rest)) # dbg
78 # print('pre <%s> ifun <%s> rest <%s>' % (pre,ifun.strip(),the_rest)) # dbg
79 return pre, esc or '', ifun.strip(), the_rest.lstrip()
79 return pre, esc or "", ifun.strip(), the_rest
80
80
81
81
82 class LineInfo(object):
82 class LineInfo(object):
@@ -107,11 +107,15 b' class LineInfo(object):'
107
107
108 the_rest
108 the_rest
109 Everything else on the line.
109 Everything else on the line.
110
111 raw_the_rest
112 the_rest without whitespace stripped.
110 """
113 """
111 def __init__(self, line, continue_prompt=False):
114 def __init__(self, line, continue_prompt=False):
112 self.line = line
115 self.line = line
113 self.continue_prompt = continue_prompt
116 self.continue_prompt = continue_prompt
114 self.pre, self.esc, self.ifun, self.the_rest = split_user_input(line)
117 self.pre, self.esc, self.ifun, self.raw_the_rest = split_user_input(line)
118 self.the_rest = self.raw_the_rest.lstrip()
115
119
116 self.pre_char = self.pre.strip()
120 self.pre_char = self.pre.strip()
117 if self.pre_char:
121 if self.pre_char:
@@ -136,3 +140,6 b' class LineInfo(object):'
136
140
137 def __str__(self):
141 def __str__(self):
138 return "LineInfo [%s|%s|%s|%s]" %(self.pre, self.esc, self.ifun, self.the_rest)
142 return "LineInfo [%s|%s|%s|%s]" %(self.pre, self.esc, self.ifun, self.the_rest)
143
144 def __repr__(self):
145 return "<" + str(self) + ">"
@@ -7,17 +7,13 b''
7 # our own packages
7 # our own packages
8 from IPython.core import autocall
8 from IPython.core import autocall
9 from IPython.testing import tools as tt
9 from IPython.testing import tools as tt
10 import pytest
11 from collections.abc import Callable
10
12
11 #-----------------------------------------------------------------------------
13 #-----------------------------------------------------------------------------
12 # Globals
14 # Globals
13 #-----------------------------------------------------------------------------
15 #-----------------------------------------------------------------------------
14
16
15 # Get the public instance of IPython
16
17 failures = []
18 num_tests = 0
19
20 #-----------------------------------------------------------------------------
21 # Test functions
17 # Test functions
22 #-----------------------------------------------------------------------------
18 #-----------------------------------------------------------------------------
23
19
@@ -31,67 +27,49 b' class Autocallable(autocall.IPyAutocall):'
31 return "called"
27 return "called"
32
28
33
29
34 def run(tests):
30 @pytest.mark.parametrize(
35 """Loop through a list of (pre, post) inputs, where pre is the string
31 "autocall, input, output",
36 handed to ipython, and post is how that string looks after it's been
32 [
37 transformed (i.e. ipython's notion of _i)"""
38 tt.check_pairs(ip.prefilter_manager.prefilter_lines, tests)
39
40
41 def test_handlers():
42 call_idx = CallableIndexable()
43 ip.user_ns['call_idx'] = call_idx
44
45 # For many of the below, we're also checking that leading whitespace
33 # For many of the below, we're also checking that leading whitespace
46 # turns off the esc char, which it should unless there is a continuation
34 # turns off the esc char, which it should unless there is a continuation
47 # line.
35 # line.
48 run(
36 ("1", '"no change"', '"no change"'), # normal
49 [('"no change"', '"no change"'), # normal
37 ("1", "lsmagic", "get_ipython().run_line_magic('lsmagic', '')"), # magic
50 (u"lsmagic", "get_ipython().run_line_magic('lsmagic', '')"), # magic
51 #("a = b # PYTHON-MODE", '_i'), # emacs -- avoids _in cache
52 ])
53
54 # Objects which are instances of IPyAutocall are *always* autocalled
55 autocallable = Autocallable()
56 ip.user_ns['autocallable'] = autocallable
57
58 # auto
59 ip.run_line_magic("autocall", "0")
60 # Only explicit escapes or instances of IPyAutocallable should get
38 # Only explicit escapes or instances of IPyAutocallable should get
61 # expanded
39 # expanded
62 run(
40 ("0", 'len "abc"', 'len "abc"'),
63 [
41 ("0", "autocallable", "autocallable()"),
64 ('len "abc"', 'len "abc"'),
65 ("autocallable", "autocallable()"),
66 # Don't add extra brackets (gh-1117)
42 # Don't add extra brackets (gh-1117)
67 ("autocallable()", "autocallable()"),
43 ("0", "autocallable()", "autocallable()"),
68 ]
44 ("1", 'len "abc"', 'len("abc")'),
69 )
45 ("1", 'len "abc";', 'len("abc");'), # ; is special -- moves out of parens
70 ip.run_line_magic("autocall", "1")
71 run(
72 [
73 ('len "abc"', 'len("abc")'),
74 ('len "abc";', 'len("abc");'), # ; is special -- moves out of parens
75 # Autocall is turned off if first arg is [] and the object
46 # Autocall is turned off if first arg is [] and the object
76 # is both callable and indexable. Like so:
47 # is both callable and indexable. Like so:
77 ("len [1,2]", "len([1,2])"), # len doesn't support __getitem__...
48 ("1", "len [1,2]", "len([1,2])"), # len doesn't support __getitem__...
78 ("call_idx [1]", "call_idx [1]"), # call_idx *does*..
49 ("1", "call_idx [1]", "call_idx [1]"), # call_idx *does*..
79 ("call_idx 1", "call_idx(1)"),
50 ("1", "call_idx 1", "call_idx(1)"),
80 ("len", "len"), # only at 2 does it auto-call on single args
51 ("1", "len", "len"), # only at 2 does it auto-call on single args
81 ]
52 ("2", 'len "abc"', 'len("abc")'),
82 )
53 ("2", 'len "abc";', 'len("abc");'),
83 ip.run_line_magic("autocall", "2")
54 ("2", "len [1,2]", "len([1,2])"),
84 run(
55 ("2", "call_idx [1]", "call_idx [1]"),
85 [
56 ("2", "call_idx 1", "call_idx(1)"),
86 ('len "abc"', 'len("abc")'),
87 ('len "abc";', 'len("abc");'),
88 ("len [1,2]", "len([1,2])"),
89 ("call_idx [1]", "call_idx [1]"),
90 ("call_idx 1", "call_idx(1)"),
91 # This is what's different:
57 # T his is what's different:
92 ("len", "len()"), # only at 2 does it auto-call on single args
58 ("2", "len", "len()"), # only at 2 does it auto-call on single args
93 ]
59 ("0", "Callable[[int], None]", "Callable[[int], None]"),
60 ("1", "Callable[[int], None]", "Callable[[int], None]"),
61 ("1", "Callable[[int], None]", "Callable[[int], None]"),
62 ],
94 )
63 )
95 ip.run_line_magic("autocall", "1")
64 def test_handlers_I(autocall, input, output):
65 autocallable = Autocallable()
66 ip.user_ns["autocallable"] = autocallable
67
68 call_idx = CallableIndexable()
69 ip.user_ns["call_idx"] = call_idx
96
70
97 assert failures == []
71 ip.user_ns["Callable"] = Callable
72
73 ip.run_line_magic("autocall", autocall)
74 assert ip.prefilter_manager.prefilter_lines(input) == output
75 ip.run_line_magic("autocall", "1")
@@ -48,7 +48,7 b' def test_prefilter_shadowed():'
48
48
49 def test_autocall_binops():
49 def test_autocall_binops():
50 """See https://github.com/ipython/ipython/issues/81"""
50 """See https://github.com/ipython/ipython/issues/81"""
51 ip.magic('autocall 2')
51 ip.run_line_magic("autocall", "2")
52 f = lambda x: x
52 f = lambda x: x
53 ip.user_ns['f'] = f
53 ip.user_ns['f'] = f
54 try:
54 try:
@@ -71,8 +71,8 b' def test_autocall_binops():'
71 finally:
71 finally:
72 pm.unregister_checker(ac)
72 pm.unregister_checker(ac)
73 finally:
73 finally:
74 ip.magic('autocall 0')
74 ip.run_line_magic("autocall", "0")
75 del ip.user_ns['f']
75 del ip.user_ns["f"]
76
76
77
77
78 def test_issue_114():
78 def test_issue_114():
@@ -105,23 +105,35 b' def test_prefilter_attribute_errors():'
105 return x
105 return x
106
106
107 # Create a callable broken object
107 # Create a callable broken object
108 ip.user_ns['x'] = X()
108 ip.user_ns["x"] = X()
109 ip.magic('autocall 2')
109 ip.run_line_magic("autocall", "2")
110 try:
110 try:
111 # Even if x throws an attribute error when looking at its rewrite
111 # Even if x throws an attribute error when looking at its rewrite
112 # attribute, we should not crash. So the test here is simply making
112 # attribute, we should not crash. So the test here is simply making
113 # the prefilter call and not having an exception.
113 # the prefilter call and not having an exception.
114 ip.prefilter('x 1')
114 ip.prefilter('x 1')
115 finally:
115 finally:
116 del ip.user_ns['x']
116 del ip.user_ns["x"]
117 ip.magic('autocall 0')
117 ip.run_line_magic("autocall", "0")
118
119
120 def test_autocall_type_ann():
121 ip.run_cell("import collections.abc")
122 ip.run_line_magic("autocall", "1")
123 try:
124 assert (
125 ip.prefilter("collections.abc.Callable[[int], None]")
126 == "collections.abc.Callable[[int], None]"
127 )
128 finally:
129 ip.run_line_magic("autocall", "0")
118
130
119
131
120 def test_autocall_should_support_unicode():
132 def test_autocall_should_support_unicode():
121 ip.magic('autocall 2')
133 ip.run_line_magic("autocall", "2")
122 ip.user_ns['π'] = lambda x: x
134 ip.user_ns["π"] = lambda x: x
123 try:
135 try:
124 assert ip.prefilter("π 3") == "π(3)"
136 assert ip.prefilter("π 3") == "π(3)"
125 finally:
137 finally:
126 ip.magic('autocall 0')
138 ip.run_line_magic("autocall", "0")
127 del ip.user_ns['π']
139 del ip.user_ns["π"]
@@ -1,7 +1,8 b''
1 # coding: utf-8
1 # coding: utf-8
2
2
3 from IPython.core.splitinput import split_user_input, LineInfo
3 from IPython.core.splitinput import split_user_input, LineInfo
4 from IPython.testing import tools as tt
4
5 import pytest
5
6
6 tests = [
7 tests = [
7 ("x=1", ("", "", "x", "=1")),
8 ("x=1", ("", "", "x", "=1")),
@@ -25,12 +26,13 b' tests = ['
25 ("??%hist3", ("", "??", "%hist3", "")),
26 ("??%hist3", ("", "??", "%hist3", "")),
26 ("??%%hist4", ("", "??", "%%hist4", "")),
27 ("??%%hist4", ("", "??", "%%hist4", "")),
27 ("?x*", ("", "?", "x*", "")),
28 ("?x*", ("", "?", "x*", "")),
29 ("Pérez Fernando", ("", "", "Pérez", " Fernando")),
28 ]
30 ]
29 tests.append(("Pérez Fernando", ("", "", "Pérez", "Fernando")))
30
31
31
32
32 def test_split_user_input():
33 @pytest.mark.parametrize("input, output", tests)
33 return tt.check_pairs(split_user_input, tests)
34 def test_split_user_input(input, output):
35 assert split_user_input(input) == output
34
36
35
37
36 def test_LineInfo():
38 def test_LineInfo():
General Comments 0
You need to be logged in to leave comments. Login now