Redirection no longer require a space

This commit is contained in:
Anthony Scopatz 2017-02-25 23:25:42 -05:00
parent 1a209547b8
commit 1b2a4a5e52
3 changed files with 61 additions and 8 deletions

15
news/nsredir.rst Normal file
View file

@ -0,0 +1,15 @@
**Added:**
* Subprocess redirection may now forego the whitespace between the
redirection and a file name. For example,
``echo hello world >/dev/null``.
**Changed:** None
**Deprecated:** None
**Removed:** None
**Fixed:** None
**Security:** None

View file

@ -82,10 +82,10 @@ def check_tokens(inp, exp):
return assert_tokens_equal(exp, obs)
def check_tokens_subproc(inp, exp):
def check_tokens_subproc(inp, exp, stop=-1):
l = Lexer()
l.input('$[{}]'.format(inp))
obs = list(l)[1:-1]
obs = list(l)[1:stop]
return assert_tokens_equal(exp, obs)
@ -242,11 +242,23 @@ def test_regex_globs():
def test_float_literals(case):
assert check_token(case, ['NUMBER', case, 0])
@pytest.mark.parametrize('case', [
'2>1', 'err>out', 'o>', 'all>', 'e>o', 'e>', 'out>', '2>&1'
])
def test_ioredir(case):
assert check_tokens_subproc(case, [('IOREDIRECT', case, 2)], stop=-2)
def test_ioredir():
cases = ['2>1', 'err>out', 'o>', 'all>', 'e>o', 'e>', 'out>', '2>&1']
for s in cases:
assert check_tokens_subproc(s, [('IOREDIRECT', s, 2)])
@pytest.mark.parametrize('case', [
'>', '>>', '<', 'e>',
'> ', '>> ', '< ', 'e> ',
])
def test_redir_whitespace(case):
inp = '![{}/path/to/file]'.format(case)
l = Lexer()
l.input(inp)
obs = list(l)
assert obs[2].type == 'WS'
@pytest.mark.parametrize('s, exp', [

View file

@ -14,8 +14,9 @@ except ImportError:
from xonsh.lazyasd import lazyobject
from xonsh.platform import PYTHON_VERSION_INFO
from xonsh.tokenize import (OP, IOREDIRECT, STRING, DOLLARNAME, NUMBER,
SEARCHPATH, NEWLINE, INDENT, DEDENT, NL, COMMENT, ENCODING,
ENDMARKER, NAME, ERRORTOKEN, tokenize, TokenError)
SEARCHPATH, NEWLINE, INDENT, DEDENT, NL, COMMENT,
ENCODING, ENDMARKER, NAME, ERRORTOKEN, GREATER,
LESS, RIGHTSHIFT, tokenize, TokenError)
@lazyobject
@ -167,6 +168,24 @@ def handle_double_pipe(state, token):
yield _new_token('OR', 'or', token.start)
def handle_redirect(state, token):
# The parser expects whitespace after a redirection in subproc mode.
# If whitespace does not exist, we'll issue an empty whitespace
# token before proceeding.
state['last'] = token
typ = token.type
st = token.string
key = (typ, st) if (typ, st) in token_map else typ
yield _new_token(token_map[key], st, token.start)
if state['pymode'][-1][0]:
return
# add a whitespace token after a redirection, if we need to
next_tok = next(state['stream'])
if next_tok.start == token.end:
yield _new_token('WS', '', token.end)
yield from handle_token(state, next_tok)
def _make_matcher_handler(tok, typ, pymode, ender, handlers):
matcher = (')' if tok.endswith('(') else
'}' if tok.endswith('{') else
@ -192,6 +211,13 @@ def special_handlers():
ENDMARKER: handle_ignore,
NAME: handle_name,
ERRORTOKEN: handle_error_token,
LESS: handle_redirect,
GREATER: handle_redirect,
RIGHTSHIFT: handle_redirect,
IOREDIRECT: handle_redirect,
(OP, '<'): handle_redirect,
(OP, '>'): handle_redirect,
(OP, '>>'): handle_redirect,
(OP, ')'): handle_rparen,
(OP, '}'): handle_rbrace,
(OP, ']'): handle_rbracket,