diff --git a/IPython/core/inputtransformer.py b/IPython/core/inputtransformer.py index c87c22b..6be225b 100644 --- a/IPython/core/inputtransformer.py +++ b/IPython/core/inputtransformer.py @@ -516,9 +516,17 @@ def strip_encoding_cookie(): while line is not None: line = (yield line) - -assign_system_re = re.compile(r'(?P(\s*)([\w\.]+)((\s*,\s*[\w\.]+)*))' - r'\s*=\s*!\s*(?P.*)') +_assign_pat = \ +r'''(?P(\s*) + ([\w\.]+) # Initial identifier + (\s*,\s* + \*?[\w\.]+)* # Further identifiers for unpacking + \s*?,? # Trailing comma + ) + \s*=\s* +''' + +assign_system_re = re.compile(r'{}!\s*(?P.*)'.format(_assign_pat), re.VERBOSE) assign_system_template = '%s = get_ipython().getoutput(%r)' @StatelessInputTransformer.wrap def assign_from_system(line): @@ -529,8 +537,7 @@ def assign_from_system(line): return assign_system_template % m.group('lhs', 'cmd') -assign_magic_re = re.compile(r'(?P(\s*)([\w\.]+)((\s*,\s*[\w\.]+)*))' - r'\s*=\s*%\s*(?P.*)') +assign_magic_re = re.compile(r'{}%\s*(?P.*)'.format(_assign_pat), re.VERBOSE) assign_magic_template = '%s = get_ipython().magic(%r)' @StatelessInputTransformer.wrap def assign_from_magic(line): diff --git a/IPython/core/tests/test_inputtransformer.py b/IPython/core/tests/test_inputtransformer.py index d55a63f..b3295b6 100644 --- a/IPython/core/tests/test_inputtransformer.py +++ b/IPython/core/tests/test_inputtransformer.py @@ -41,16 +41,29 @@ syntax = \ [(i,py3compat.u_format(o)) for i,o in \ [(u'a =! ls', "a = get_ipython().getoutput({u}'ls')"), (u'b = !ls', "b = get_ipython().getoutput({u}'ls')"), + (u'c= !ls', "c = get_ipython().getoutput({u}'ls')"), + (u'd == !ls', u'd == !ls'), # Invalid syntax, but we leave == alone. ('x=1', 'x=1'), # normal input is unmodified (' ',' '), # blank lines are kept intact + # Tuple unpacking + (u"a, b = !echo 'a\\nb'", u"a, b = get_ipython().getoutput({u}\"echo 'a\\\\nb'\")"), + (u"a,= !echo 'a'", u"a, = get_ipython().getoutput({u}\"echo 'a'\")"), + (u"a, *bc = !echo 'a\\nb\\nc'", u"a, *bc = get_ipython().getoutput({u}\"echo 'a\\\\nb\\\\nc'\")"), + # Tuple unpacking with regular Python expressions, not our syntax. + (u"a, b = range(2)", u"a, b = range(2)"), + (u"a, = range(1)", u"a, = range(1)"), + (u"a, *bc = range(3)", u"a, *bc = range(3)"), ]], assign_magic = [(i,py3compat.u_format(o)) for i,o in \ [(u'a =% who', "a = get_ipython().magic({u}'who')"), (u'b = %who', "b = get_ipython().magic({u}'who')"), + (u'c= %ls', "c = get_ipython().magic({u}'ls')"), + (u'd == %ls', u'd == %ls'), # Invalid syntax, but we leave == alone. ('x=1', 'x=1'), # normal input is unmodified (' ',' '), # blank lines are kept intact + (u"a, b = %foo", u"a, b = get_ipython().magic({u}'foo')"), ]], classic_prompt =