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( |
|
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 |
|
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)""" |
|
33 | # For many of the below, we're also checking that leading whitespace | |
38 | tt.check_pairs(ip.prefilter_manager.prefilter_lines, tests) |
|
34 | # turns off the esc char, which it should unless there is a continuation | |
39 |
|
35 | # line. | ||
|
36 | ("1", '"no change"', '"no change"'), # normal | |||
|
37 | ("1", "lsmagic", "get_ipython().run_line_magic('lsmagic', '')"), # magic | |||
|
38 | # Only explicit escapes or instances of IPyAutocallable should get | |||
|
39 | # expanded | |||
|
40 | ("0", 'len "abc"', 'len "abc"'), | |||
|
41 | ("0", "autocallable", "autocallable()"), | |||
|
42 | # Don't add extra brackets (gh-1117) | |||
|
43 | ("0", "autocallable()", "autocallable()"), | |||
|
44 | ("1", 'len "abc"', 'len("abc")'), | |||
|
45 | ("1", 'len "abc";', 'len("abc");'), # ; is special -- moves out of parens | |||
|
46 | # Autocall is turned off if first arg is [] and the object | |||
|
47 | # is both callable and indexable. Like so: | |||
|
48 | ("1", "len [1,2]", "len([1,2])"), # len doesn't support __getitem__... | |||
|
49 | ("1", "call_idx [1]", "call_idx [1]"), # call_idx *does*.. | |||
|
50 | ("1", "call_idx 1", "call_idx(1)"), | |||
|
51 | ("1", "len", "len"), # only at 2 does it auto-call on single args | |||
|
52 | ("2", 'len "abc"', 'len("abc")'), | |||
|
53 | ("2", 'len "abc";', 'len("abc");'), | |||
|
54 | ("2", "len [1,2]", "len([1,2])"), | |||
|
55 | ("2", "call_idx [1]", "call_idx [1]"), | |||
|
56 | ("2", "call_idx 1", "call_idx(1)"), | |||
|
57 | # T his is what's different: | |||
|
58 | ("2", "len", "len()"), # only at 2 does it auto-call on single args | |||
|
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 | ], | |||
|
63 | ) | |||
|
64 | def test_handlers_I(autocall, input, output): | |||
|
65 | autocallable = Autocallable() | |||
|
66 | ip.user_ns["autocallable"] = autocallable | |||
40 |
|
67 | |||
41 | def test_handlers(): |
|
|||
42 | call_idx = CallableIndexable() |
|
68 | call_idx = CallableIndexable() | |
43 |
ip.user_ns[ |
|
69 | ip.user_ns["call_idx"] = call_idx | |
44 |
|
70 | |||
45 | # For many of the below, we're also checking that leading whitespace |
|
71 | ip.user_ns["Callable"] = Callable | |
46 | # turns off the esc char, which it should unless there is a continuation |
|
|||
47 | # line. |
|
|||
48 | run( |
|
|||
49 | [('"no change"', '"no change"'), # normal |
|
|||
50 | (u"lsmagic", "get_ipython().run_line_magic('lsmagic', '')"), # magic |
|
|||
51 | #("a = b # PYTHON-MODE", '_i'), # emacs -- avoids _in cache |
|
|||
52 | ]) |
|
|||
53 |
|
72 | |||
54 | # Objects which are instances of IPyAutocall are *always* autocalled |
|
73 | ip.run_line_magic("autocall", autocall) | |
55 | autocallable = Autocallable() |
|
74 | assert ip.prefilter_manager.prefilter_lines(input) == output | |
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 |
|
|||
61 | # expanded |
|
|||
62 | run( |
|
|||
63 | [ |
|
|||
64 | ('len "abc"', 'len "abc"'), |
|
|||
65 | ("autocallable", "autocallable()"), |
|
|||
66 | # Don't add extra brackets (gh-1117) |
|
|||
67 | ("autocallable()", "autocallable()"), |
|
|||
68 | ] |
|
|||
69 | ) |
|
|||
70 | ip.run_line_magic("autocall", "1") |
|
75 | 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 |
|
|||
76 | # is both callable and indexable. Like so: |
|
|||
77 | ("len [1,2]", "len([1,2])"), # len doesn't support __getitem__... |
|
|||
78 | ("call_idx [1]", "call_idx [1]"), # call_idx *does*.. |
|
|||
79 | ("call_idx 1", "call_idx(1)"), |
|
|||
80 | ("len", "len"), # only at 2 does it auto-call on single args |
|
|||
81 | ] |
|
|||
82 | ) |
|
|||
83 | ip.run_line_magic("autocall", "2") |
|
|||
84 | run( |
|
|||
85 | [ |
|
|||
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: |
|
|||
92 | ("len", "len()"), # only at 2 does it auto-call on single args |
|
|||
93 | ] |
|
|||
94 | ) |
|
|||
95 | ip.run_line_magic("autocall", "1") |
|
|||
96 |
|
||||
97 | assert failures == [] |
|
@@ -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( |
|
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( |
|
74 | ip.run_line_magic("autocall", "0") | |
75 |
del ip.user_ns[ |
|
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[ |
|
108 | ip.user_ns["x"] = X() | |
109 |
ip.magic( |
|
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[ |
|
116 | del ip.user_ns["x"] | |
117 |
ip.magic( |
|
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( |
|
133 | ip.run_line_magic("autocall", "2") | |
122 |
ip.user_ns[ |
|
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( |
|
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")), | |
@@ -19,18 +20,19 b' tests = [' | |||||
19 | (";ls", ("", ";", "ls", "")), |
|
20 | (";ls", ("", ";", "ls", "")), | |
20 | (" ;ls", (" ", ";", "ls", "")), |
|
21 | (" ;ls", (" ", ";", "ls", "")), | |
21 | ("f.g(x)", ("", "", "f.g", "(x)")), |
|
22 | ("f.g(x)", ("", "", "f.g", "(x)")), | |
22 | ("f.g (x)", ("", "", "f.g", "(x)")), |
|
23 | ("f.g (x)", ("", "", "f.g", " (x)")), | |
23 | ("?%hist1", ("", "?", "%hist1", "")), |
|
24 | ("?%hist1", ("", "?", "%hist1", "")), | |
24 | ("?%%hist2", ("", "?", "%%hist2", "")), |
|
25 | ("?%%hist2", ("", "?", "%%hist2", "")), | |
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