Repository: xonsh/xonsh

Issue:
lambda incorrectly run in subprocess mode
Xonsh seems to think that lambda is a subprocess, for some reason. See trace below:

```
~ $ xonfig
+------------------+----------------------+
| xonsh            | 0.6.0                |
| Git SHA          | c29819c5             |
| Commit Date      | Nov 22 17:51:52 2017 |
| Python           | 3.5.2                |
| PLY              | 3.9                  |
| have readline    | True                 |
| prompt toolkit   | None                 |
| shell type       | readline             |
| pygments         | None                 |
| on posix         | True                 |
| on linux         | True                 |
| distro           | neon                 |
| on darwin        | False                |
| on windows       | False                |
| on cygwin        | False                |
| is superuser     | False                |
| default encoding | utf-8                |
| xonsh encoding   | utf-8                |
| encoding errors  | surrogateescape      |
+------------------+----------------------+

~ $ lambda x:x+1
xonsh: subprocess mode: command not found: x:x+1
x:x+1: command not found

~ $ (lambda x:x+1)
Traceback (most recent call last):
  File "/home/mike/.local/lib/python3.5/site-packages/xonsh/__amalgam__.py", line 19011, in _run_binary
    p = self.cls(self.cmd, bufsize=bufsize, **kwargs)
  File "/home/mike/.local/lib/python3.5/site-packages/xonsh/__amalgam__.py", line 11194, in __init__
    **kwargs)
  File "/usr/lib/python3.5/subprocess.py", line 947, in __init__
    restore_signals, start_new_session)
  File "/usr/lib/python3.5/subprocess.py", line 1551, in _execute_child
    raise child_exception_type(errno_num, err_msg)
FileNotFoundError: [Errno 2] No such file or directory: 'x:x+1'

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/home/mike/.local/bin/xonsh", line 3, in <module>
    main()
  File "/home/mike/.local/lib/python3.5/site-packages/xonsh/__amalgam__.py", line 20660, in main
    _failback_to_other_shells(args, err)
  File "/home/mike/.local/lib/python3.5/site-packages/xonsh/__amalgam__.py", line 20624, in _failback_to_other_shells
    raise err
  File "/home/mike/.local/lib/python3.5/site-packages/xonsh/__amalgam__.py", line 20658, in main
    return main_xonsh(args)
  File "/home/mike/.local/lib/python3.5/site-packages/xonsh/__amalgam__.py", line 20690, in main_xonsh
    run_code_with_cache(args.command.lstrip(), shell.execer, mode='single')
  File "/home/mike/.local/lib/python3.5/site-packages/xonsh/__amalgam__.py", line 2463, in run_code_with_cache
    run_compiled_code(ccode, glb, loc, mode)
  File "/home/mike/.local/lib/python3.5/site-packages/xonsh/__amalgam__.py", line 2322, in run_compiled_code
    func(code, glb, loc)
  File "b73dbc941f8bea108946fc78594cb3bc", line 1, in <module>
  File "/home/mike/.local/lib/python3.5/site-packages/xonsh/__amalgam__.py", line 19376, in subproc_captured_hiddenobject
    return run_subproc(cmds, captured='hiddenobject')
  File "/home/mike/.local/lib/python3.5/site-packages/xonsh/__amalgam__.py", line 19312, in run_subproc
    command = HiddenCommandPipeline(specs)
  File "/home/mike/.local/lib/python3.5/site-packages/xonsh/__amalgam__.py", line 12402, in __init__
    proc = spec.run(pipeline_group=pipeline_group)
  File "/home/mike/.local/lib/python3.5/site-packages/xonsh/__amalgam__.py", line 19001, in run
    p = self._run_binary(kwargs)
  File "/home/mike/.local/lib/python3.5/site-packages/xonsh/__amalgam__.py", line 19022, in _run_binary
    raise XonshError(e)
xonsh.__amalgam__.XonshError: xonsh: subprocess mode: command not found: x:x+1
x:x+1: command not found
~ $
```


Discussion:
Wow, interesting.  Thanks for reporting @battaglia01! This is a definitely a bug.

Resolving patch:
diff --git a/xonsh/ast.py b/xonsh/ast.py
--- a/xonsh/ast.py
+++ b/xonsh/ast.py
@@ -327,7 +327,7 @@ def visit_Expr(self, node):
         """Handle visiting an expression."""
         if isdescendable(node.value):
             node.value = self.visit(node.value)  # this allows diving into BoolOps
-        if self.is_in_scope(node):
+        if self.is_in_scope(node) or isinstance(node.value, Lambda):
             return node
         else:
             newnode = self.try_subproc_toks(node)
diff --git a/xonsh/parsers/base.py b/xonsh/parsers/base.py
--- a/xonsh/parsers/base.py
+++ b/xonsh/parsers/base.py
@@ -292,7 +292,7 @@ def __init__(self,
                      'dollar_lbrace', 'dollar_lbracket', 'try',
                      'bang_lparen', 'bang_lbracket', 'comma', 'rparen',
                      'rbracket', 'at_lparen', 'atdollar_lparen', 'indent',
-                     'dedent', 'newline']
+                     'dedent', 'newline', 'lambda']
         for rule in tok_rules:
             self._tok_rule(rule)
 
@@ -1522,15 +1522,15 @@ def p_test_nocond(self, p):
         p[0] = p[1]
 
     def p_lambdef(self, p):
-        """lambdef : LAMBDA varargslist_opt COLON test"""
-        p2, p4 = p[2], p[4]
+        """lambdef : lambda_tok varargslist_opt COLON test"""
+        p1, p2, p4 = p[1], p[2], p[4]
         if p2 is None:
             args = ast.arguments(args=[], vararg=None, kwonlyargs=[],
                                  kw_defaults=[], kwarg=None, defaults=[])
         else:
             args = p2
-        p0 = ast.Lambda(args=args, body=p4, lineno=self.lineno,
-                        col_offset=self.col)
+        p0 = ast.Lambda(args=args, body=p4, lineno=p1.lineno,
+                        col_offset=p1.lexpos)
         p[0] = p0
 
     def p_lambdef_nocond(self, p):


Test patch:
diff --git a/tests/test_ast.py b/tests/test_ast.py
--- a/tests/test_ast.py
+++ b/tests/test_ast.py
@@ -100,6 +100,8 @@ def test_multilline_no_transform():
     paths.extend(os.path.join(root, d) for d in dirs)
     paths.extend(os.path.join(root, f) for f in files)
 """,
+"""lambda x: x + 1
+""",
 ])
 def test_unmodified(inp):
     # Context sensitive parsing should not modify AST