diff --git a/IPython/core/prefilter.py b/IPython/core/prefilter.py index e611b4b..a29df0c 100644 --- a/IPython/core/prefilter.py +++ b/IPython/core/prefilter.py @@ -476,8 +476,8 @@ class PythonOpsChecker(PrefilterChecker): any python operator, we should simply execute the line (regardless of whether or not there's a possible autocall expansion). This avoids spurious (and very confusing) geattr() accesses.""" - if line_info.the_rest and line_info.the_rest[0] in '!=()<>,+*/%^&|': - return self.prefilter_manager.get_handler_by_name('normal') + if line_info.the_rest and line_info.the_rest[0] in "!=()<>,+*/%^&|": + return self.prefilter_manager.get_handler_by_name("normal") else: return None @@ -512,6 +512,8 @@ class AutocallChecker(PrefilterChecker): callable(oinfo.obj) and (not self.exclude_regexp.match(line_info.the_rest)) and self.function_name_regexp.match(line_info.ifun) + and line_info.raw_the_rest.startswith(" ") + or not line_info.raw_the_rest.strip() ): return self.prefilter_manager.get_handler_by_name("auto") else: diff --git a/IPython/core/splitinput.py b/IPython/core/splitinput.py index 0cd70ec..33e462b 100644 --- a/IPython/core/splitinput.py +++ b/IPython/core/splitinput.py @@ -76,7 +76,7 @@ def split_user_input(line, pattern=None): # print('line:<%s>' % line) # dbg # print('pre <%s> ifun <%s> rest <%s>' % (pre,ifun.strip(),the_rest)) # dbg - return pre, esc or '', ifun.strip(), the_rest.lstrip() + return pre, esc or "", ifun.strip(), the_rest class LineInfo(object): @@ -107,11 +107,15 @@ class LineInfo(object): the_rest Everything else on the line. + + raw_the_rest + the_rest without whitespace stripped. """ def __init__(self, line, continue_prompt=False): self.line = line self.continue_prompt = continue_prompt - self.pre, self.esc, self.ifun, self.the_rest = split_user_input(line) + self.pre, self.esc, self.ifun, self.raw_the_rest = split_user_input(line) + self.the_rest = self.raw_the_rest.lstrip() self.pre_char = self.pre.strip() if self.pre_char: @@ -136,3 +140,6 @@ class LineInfo(object): def __str__(self): return "LineInfo [%s|%s|%s|%s]" %(self.pre, self.esc, self.ifun, self.the_rest) + + def __repr__(self): + return "<" + str(self) + ">" diff --git a/IPython/core/tests/test_handlers.py b/IPython/core/tests/test_handlers.py index 604dade..905d9ab 100644 --- a/IPython/core/tests/test_handlers.py +++ b/IPython/core/tests/test_handlers.py @@ -7,17 +7,13 @@ # our own packages from IPython.core import autocall from IPython.testing import tools as tt +import pytest +from collections.abc import Callable #----------------------------------------------------------------------------- # Globals #----------------------------------------------------------------------------- -# Get the public instance of IPython - -failures = [] -num_tests = 0 - -#----------------------------------------------------------------------------- # Test functions #----------------------------------------------------------------------------- @@ -31,67 +27,49 @@ class Autocallable(autocall.IPyAutocall): return "called" -def run(tests): - """Loop through a list of (pre, post) inputs, where pre is the string - handed to ipython, and post is how that string looks after it's been - transformed (i.e. ipython's notion of _i)""" - tt.check_pairs(ip.prefilter_manager.prefilter_lines, tests) - +@pytest.mark.parametrize( + "autocall, input, output", + [ + # For many of the below, we're also checking that leading whitespace + # turns off the esc char, which it should unless there is a continuation + # line. + ("1", '"no change"', '"no change"'), # normal + ("1", "lsmagic", "get_ipython().run_line_magic('lsmagic', '')"), # magic + # Only explicit escapes or instances of IPyAutocallable should get + # expanded + ("0", 'len "abc"', 'len "abc"'), + ("0", "autocallable", "autocallable()"), + # Don't add extra brackets (gh-1117) + ("0", "autocallable()", "autocallable()"), + ("1", 'len "abc"', 'len("abc")'), + ("1", 'len "abc";', 'len("abc");'), # ; is special -- moves out of parens + # Autocall is turned off if first arg is [] and the object + # is both callable and indexable. Like so: + ("1", "len [1,2]", "len([1,2])"), # len doesn't support __getitem__... + ("1", "call_idx [1]", "call_idx [1]"), # call_idx *does*.. + ("1", "call_idx 1", "call_idx(1)"), + ("1", "len", "len"), # only at 2 does it auto-call on single args + ("2", 'len "abc"', 'len("abc")'), + ("2", 'len "abc";', 'len("abc");'), + ("2", "len [1,2]", "len([1,2])"), + ("2", "call_idx [1]", "call_idx [1]"), + ("2", "call_idx 1", "call_idx(1)"), + # T his is what's different: + ("2", "len", "len()"), # only at 2 does it auto-call on single args + ("0", "Callable[[int], None]", "Callable[[int], None]"), + ("1", "Callable[[int], None]", "Callable[[int], None]"), + ("1", "Callable[[int], None]", "Callable[[int], None]"), + ], +) +def test_handlers_I(autocall, input, output): + autocallable = Autocallable() + ip.user_ns["autocallable"] = autocallable -def test_handlers(): call_idx = CallableIndexable() - ip.user_ns['call_idx'] = call_idx + ip.user_ns["call_idx"] = call_idx - # For many of the below, we're also checking that leading whitespace - # turns off the esc char, which it should unless there is a continuation - # line. - run( - [('"no change"', '"no change"'), # normal - (u"lsmagic", "get_ipython().run_line_magic('lsmagic', '')"), # magic - #("a = b # PYTHON-MODE", '_i'), # emacs -- avoids _in cache - ]) + ip.user_ns["Callable"] = Callable - # Objects which are instances of IPyAutocall are *always* autocalled - autocallable = Autocallable() - ip.user_ns['autocallable'] = autocallable - - # auto - ip.run_line_magic("autocall", "0") - # Only explicit escapes or instances of IPyAutocallable should get - # expanded - run( - [ - ('len "abc"', 'len "abc"'), - ("autocallable", "autocallable()"), - # Don't add extra brackets (gh-1117) - ("autocallable()", "autocallable()"), - ] - ) + ip.run_line_magic("autocall", autocall) + assert ip.prefilter_manager.prefilter_lines(input) == output ip.run_line_magic("autocall", "1") - run( - [ - ('len "abc"', 'len("abc")'), - ('len "abc";', 'len("abc");'), # ; is special -- moves out of parens - # Autocall is turned off if first arg is [] and the object - # is both callable and indexable. Like so: - ("len [1,2]", "len([1,2])"), # len doesn't support __getitem__... - ("call_idx [1]", "call_idx [1]"), # call_idx *does*.. - ("call_idx 1", "call_idx(1)"), - ("len", "len"), # only at 2 does it auto-call on single args - ] - ) - ip.run_line_magic("autocall", "2") - run( - [ - ('len "abc"', 'len("abc")'), - ('len "abc";', 'len("abc");'), - ("len [1,2]", "len([1,2])"), - ("call_idx [1]", "call_idx [1]"), - ("call_idx 1", "call_idx(1)"), - # This is what's different: - ("len", "len()"), # only at 2 does it auto-call on single args - ] - ) - ip.run_line_magic("autocall", "1") - - assert failures == [] diff --git a/IPython/core/tests/test_prefilter.py b/IPython/core/tests/test_prefilter.py index 91c3c86..999cd43 100644 --- a/IPython/core/tests/test_prefilter.py +++ b/IPython/core/tests/test_prefilter.py @@ -48,7 +48,7 @@ def test_prefilter_shadowed(): def test_autocall_binops(): """See https://github.com/ipython/ipython/issues/81""" - ip.magic('autocall 2') + ip.run_line_magic("autocall", "2") f = lambda x: x ip.user_ns['f'] = f try: @@ -71,8 +71,8 @@ def test_autocall_binops(): finally: pm.unregister_checker(ac) finally: - ip.magic('autocall 0') - del ip.user_ns['f'] + ip.run_line_magic("autocall", "0") + del ip.user_ns["f"] def test_issue_114(): @@ -105,23 +105,35 @@ def test_prefilter_attribute_errors(): return x # Create a callable broken object - ip.user_ns['x'] = X() - ip.magic('autocall 2') + ip.user_ns["x"] = X() + ip.run_line_magic("autocall", "2") try: # Even if x throws an attribute error when looking at its rewrite # attribute, we should not crash. So the test here is simply making # the prefilter call and not having an exception. ip.prefilter('x 1') finally: - del ip.user_ns['x'] - ip.magic('autocall 0') + del ip.user_ns["x"] + ip.run_line_magic("autocall", "0") + + +def test_autocall_type_ann(): + ip.run_cell("import collections.abc") + ip.run_line_magic("autocall", "1") + try: + assert ( + ip.prefilter("collections.abc.Callable[[int], None]") + == "collections.abc.Callable[[int], None]" + ) + finally: + ip.run_line_magic("autocall", "0") def test_autocall_should_support_unicode(): - ip.magic('autocall 2') - ip.user_ns['π'] = lambda x: x + ip.run_line_magic("autocall", "2") + ip.user_ns["π"] = lambda x: x try: assert ip.prefilter("π 3") == "π(3)" finally: - ip.magic('autocall 0') - del ip.user_ns['π'] + ip.run_line_magic("autocall", "0") + del ip.user_ns["π"] diff --git a/IPython/core/tests/test_splitinput.py b/IPython/core/tests/test_splitinput.py index 1462e7f..f5fc53f 100644 --- a/IPython/core/tests/test_splitinput.py +++ b/IPython/core/tests/test_splitinput.py @@ -1,7 +1,8 @@ # coding: utf-8 from IPython.core.splitinput import split_user_input, LineInfo -from IPython.testing import tools as tt + +import pytest tests = [ ("x=1", ("", "", "x", "=1")), @@ -19,18 +20,19 @@ tests = [ (";ls", ("", ";", "ls", "")), (" ;ls", (" ", ";", "ls", "")), ("f.g(x)", ("", "", "f.g", "(x)")), - ("f.g (x)", ("", "", "f.g", "(x)")), + ("f.g (x)", ("", "", "f.g", " (x)")), ("?%hist1", ("", "?", "%hist1", "")), ("?%%hist2", ("", "?", "%%hist2", "")), ("??%hist3", ("", "??", "%hist3", "")), ("??%%hist4", ("", "??", "%%hist4", "")), ("?x*", ("", "?", "x*", "")), + ("Pérez Fernando", ("", "", "Pérez", " Fernando")), ] -tests.append(("Pérez Fernando", ("", "", "Pérez", "Fernando"))) -def test_split_user_input(): - return tt.check_pairs(split_user_input, tests) +@pytest.mark.parametrize("input, output", tests) +def test_split_user_input(input, output): + assert split_user_input(input) == output def test_LineInfo():