Merge branch 'master' into aspace

This commit is contained in:
Anthony Scopatz 2017-12-09 16:37:36 -05:00
commit 73e6e4b34e
8 changed files with 97 additions and 11 deletions

14
news/iglob.rst Normal file
View file

@ -0,0 +1,14 @@
**Added:** None
**Changed:** None
**Deprecated:** None
**Removed:** None
**Fixed:**
* Made an exceptional case in ``iglobpath()`` more robust when Python globbing
fails for due to strange scrandir issue.
**Security:** None

15
news/synstar.rst Normal file
View file

@ -0,0 +1,15 @@
**Added:** None
**Changed:** None
**Deprecated:** None
**Removed:** None
**Fixed:**
* Properly throw ``SyntaxError`` when no kwargs are defined
in a kwarg-only function. This used to throw a
``TypeError: 'NoneType' object is not iterable``.
**Security:** None

13
news/teenc.rst Normal file
View file

@ -0,0 +1,13 @@
**Added:** None
**Changed:** None
**Deprecated:** None
**Removed:** None
**Fixed:**
* Addressed issue where encoding and errors were None when teeing output.
**Security:** None

View file

@ -2310,3 +2310,8 @@ def test_syntax_error_augassign_ops(exp):
def test_syntax_error_augassign_cmp(exp): def test_syntax_error_augassign_cmp(exp):
with pytest.raises(SyntaxError): with pytest.raises(SyntaxError):
PARSER.parse('{} += a'.format(exp)) PARSER.parse('{} += a'.format(exp))
def test_syntax_error_bar_kwonlyargs():
with pytest.raises(SyntaxError):
PARSER.parse('def spam(*):\n pass\n', mode='exec')

View file

@ -27,7 +27,7 @@ from xonsh.tools import (
pathsep_to_upper_seq, seq_to_upper_pathsep, expandvars, is_int_as_str, is_slice_as_str, pathsep_to_upper_seq, seq_to_upper_pathsep, expandvars, is_int_as_str, is_slice_as_str,
ensure_timestamp, get_portions, is_balanced, subexpr_before_unbalanced, ensure_timestamp, get_portions, is_balanced, subexpr_before_unbalanced,
swap_values, get_logical_line, replace_logical_line, check_quotes, deprecated, swap_values, get_logical_line, replace_logical_line, check_quotes, deprecated,
is_writable_file, balanced_parens) is_writable_file, balanced_parens, iglobpath)
from xonsh.environ import Env from xonsh.environ import Env
from tools import skip_if_on_windows, skip_if_on_unix from tools import skip_if_on_windows, skip_if_on_unix
@ -1360,3 +1360,17 @@ def test_deprecated_past_expiry_raises_assertion_error(expired_version):
with pytest.raises(AssertionError): with pytest.raises(AssertionError):
my_function() my_function()
def test_iglobpath_empty_str(monkeypatch, xonsh_builtins):
# makes sure that iglobpath works, even when os.scandir() and os.listdir()
# fail to return valid results, like an empty filename
def mockscandir(path):
yield ''
if hasattr(os, 'scandir'):
monkeypatch.setattr(os, 'scandir', mockscandir)
def mocklistdir(path):
return ['']
monkeypatch.setattr(os, 'listdir', mocklistdir)
paths = list(iglobpath('some/path'))
assert len(paths) == 0

View file

@ -42,10 +42,11 @@ class _TeeStdBuf(io.RawIOBase):
The in memory stream buffer. The in memory stream buffer.
encoding : str or None, optional encoding : str or None, optional
The encoding of the stream. Only used if stdbuf is a text stream, The encoding of the stream. Only used if stdbuf is a text stream,
rather than a binary one. rather than a binary one. Defaults to $XONSH_ENCODING if None.
errors : str or None, optional errors : str or None, optional
The error form for the encoding of the stream. Only used if stdbuf The error form for the encoding of the stream. Only used if stdbuf
is a text stream, rather than a binary one. is a text stream, rather than a binary one. Deafults to
$XONSH_ENCODING_ERRORS if None.
prestd : bytes, optional prestd : bytes, optional
The prefix to prepend to the standard buffer. The prefix to prepend to the standard buffer.
poststd : bytes, optional poststd : bytes, optional
@ -53,8 +54,9 @@ class _TeeStdBuf(io.RawIOBase):
""" """
self.stdbuf = stdbuf self.stdbuf = stdbuf
self.membuf = membuf self.membuf = membuf
self.encoding = encoding env = builtins.__xonsh_env__
self.errors = errors self.encoding = env.get('XONSH_ENCODING') if encoding is None else encoding
self.errors = env.get('XONSH_ENCODING_ERRORS') if errors is None else errors
self.prestd = prestd self.prestd = prestd
self.poststd = poststd self.poststd = poststd
self._std_is_binary = not hasattr(stdbuf, 'encoding') self._std_is_binary = not hasattr(stdbuf, 'encoding')

View file

@ -646,21 +646,37 @@ class BaseParser(object):
p[0] = ast.arguments(args=[], vararg=None, kwonlyargs=[], p[0] = ast.arguments(args=[], vararg=None, kwonlyargs=[],
kw_defaults=[], kwarg=p[2], defaults=[]) kw_defaults=[], kwarg=p[2], defaults=[])
def p_typedargslist_times4(self, p): def p_typedargslist_times4_tfpdef(self, p):
"""typedargslist : TIMES tfpdef_opt comma_pow_tfpdef_opt""" """typedargslist : TIMES tfpdef comma_pow_tfpdef_opt"""
# *args, **kwargs
p0 = ast.arguments(args=[], vararg=None, kwonlyargs=[], kw_defaults=[], p0 = ast.arguments(args=[], vararg=None, kwonlyargs=[], kw_defaults=[],
kwarg=p[3], defaults=[]) kwarg=p[3], defaults=[])
self._set_var_args(p0, p[2], None) self._set_var_args(p0, p[2], None)
p[0] = p0 p[0] = p0
def p_typedargslist_times5(self, p): def p_typedargslist_times4_comma(self, p):
"""typedargslist : TIMES tfpdef_opt comma_tfpdef_list comma_pow_tfpdef_opt""" """typedargslist : TIMES comma_pow_tfpdef"""
# *, **kwargs
p0 = ast.arguments(args=[], vararg=None, kwonlyargs=[], kw_defaults=[],
kwarg=p[2], defaults=[])
p[0] = p0
def p_typedargslist_times5_tdpdef(self, p):
"""typedargslist : TIMES tfpdef comma_tfpdef_list comma_pow_tfpdef_opt"""
# *args, x, **kwargs # *args, x, **kwargs
p0 = ast.arguments(args=[], vararg=None, kwonlyargs=[], kw_defaults=[], p0 = ast.arguments(args=[], vararg=None, kwonlyargs=[], kw_defaults=[],
kwarg=p[4], defaults=[]) kwarg=p[4], defaults=[])
self._set_var_args(p0, p[2], p[3]) # *args self._set_var_args(p0, p[2], p[3]) # *args
p[0] = p0 p[0] = p0
def p_typedargslist_times5_comma(self, p):
"""typedargslist : TIMES comma_tfpdef_list comma_pow_tfpdef_opt"""
# *, x, **kwargs
p0 = ast.arguments(args=[], vararg=None, kwonlyargs=[], kw_defaults=[],
kwarg=p[3], defaults=[])
self._set_var_args(p0, None, p[2]) # *args
p[0] = p0
def p_typedargslist_t5(self, p): def p_typedargslist_t5(self, p):
"""typedargslist : tfpdef equals_test_opt comma_tfpdef_list_opt comma_opt""" """typedargslist : tfpdef equals_test_opt comma_tfpdef_list_opt comma_opt"""
# x # x
@ -733,6 +749,9 @@ class BaseParser(object):
def _set_args_def(self, argmts, vals, kwargs=False): def _set_args_def(self, argmts, vals, kwargs=False):
args, defs = (argmts.kwonlyargs, argmts.kw_defaults) if kwargs else \ args, defs = (argmts.kwonlyargs, argmts.kw_defaults) if kwargs else \
(argmts.args, argmts.defaults) (argmts.args, argmts.defaults)
if vals is None and kwargs:
loc = self.currloc(self.lineno, self.col)
self._parse_error('named arguments must follow bare *', loc)
for v in vals: for v in vals:
args.append(v['arg']) args.append(v['arg'])
d = v['default'] d = v['default']
@ -758,7 +777,7 @@ class BaseParser(object):
self._set_args_def(p0, p3) self._set_args_def(p0, p3)
def _set_var_args(self, p0, vararg, kwargs): def _set_var_args(self, p0, vararg, kwargs):
if vararg is None: if vararg is None and kwargs is not None:
self._set_args_def(p0, kwargs, kwargs=True) self._set_args_def(p0, kwargs, kwargs=True)
elif vararg is not None and kwargs is None: elif vararg is not None and kwargs is None:
# *args # *args

View file

@ -1888,7 +1888,11 @@ def _iglobpath(s, ignore_case=False, sort_result=None):
def iglobpath(s, ignore_case=False, sort_result=None): def iglobpath(s, ignore_case=False, sort_result=None):
"""Simple wrapper around iglob that also expands home and env vars.""" """Simple wrapper around iglob that also expands home and env vars."""
try:
return _iglobpath(s, ignore_case=ignore_case, sort_result=sort_result)[0] return _iglobpath(s, ignore_case=ignore_case, sort_result=sort_result)[0]
except IndexError:
# something went wrong in the actual iglob() call
return iter(())
def ensure_timestamp(t, datetime_format=None): def ensure_timestamp(t, datetime_format=None):