diff --git a/.appveyor.yml b/.appveyor.yml index af548f6e6..2d538a342 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -4,8 +4,8 @@ environment: matrix: # http://www.appveyor.com/docs/installed-software#python - - PYTHON: "C:\\Python34" - - PYTHON: "C:\\Python34-x64" + - PYTHON: "C:\\Python35" + - PYTHON: "C:\\Python35-x64" DISTUTILS_USE_SDK: "1" - XONSH_TEST_ENV: "MSYS2" MSYS2_PATH: "C:\\msys64" diff --git a/.circleci/config.yml b/.circleci/config.yml index fcb5e023b..2b6d721f3 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -1,51 +1,6 @@ version: 2 jobs: - build_34: - machine: true - environment: - PYTHON: "3.4" - ENV_NAME: "py34-xonsh-test" - steps: - - checkout - - restore_cache: - keys: - - miniconda-v1-{{ checksum "ci/environment-3.4.yml" }} - - run: - name: install miniconda - command: | - if [ ! -d "/home/circleci/miniconda" ]; then - wget https://repo.continuum.io/miniconda/Miniconda3-latest-Linux-x86_64.sh -O miniconda.sh - bash miniconda.sh -b -p $HOME/miniconda - export PATH="$HOME/miniconda/bin:$PATH" - conda config --set always_yes yes --set changeps1 no - fi - sudo chown -R $USER.$USER $HOME - - run: - name: configure conda - command: | - export PATH="$HOME/miniconda/bin:$PATH" - if [ ! -d "/home/circleci/miniconda/envs/py34-xonsh-test" ]; then - conda update -q conda - conda env create -f ci/environment-${PYTHON}.yml --name=${ENV_NAME} - source activate ${ENV_NAME} - fi - conda env list - conda list ${ENV_NAME} - - save_cache: - key: miniconda-v1-{{ checksum "ci/environment-3.4.yml" }} - paths: - - "/home/circleci/miniconda" - - run: - command: | - export PATH="$HOME/miniconda/bin:$PATH" - source activate ${ENV_NAME} - pip install . --no-deps - - run: - command: | - export PATH="$HOME/miniconda/bin:$PATH" - source activate ${ENV_NAME} - xonsh run-tests.xsh --timeout=10 build_35: machine: true environment: @@ -220,7 +175,6 @@ workflows: version: 2 run_all_pythons: jobs: - - build_34 - build_35 - build_36 - build_black diff --git a/README.rst b/README.rst index 8509afe40..5dd46d29d 100644 --- a/README.rst +++ b/README.rst @@ -22,7 +22,7 @@ xonsh :target: https://codecov.io/gh/xonsh/xonsh xonsh is a Python-powered, cross-platform, Unix-gazing shell language and command prompt. -The language is a superset of Python 3.4+ with additional shell primitives. +The language is a superset of Python 3.5+ with additional shell primitives. xonsh (pronounced *conch*) is meant for the daily use of experts and novices alike. Please visit https://xon.sh for more information. diff --git a/ci/environment-3.4.yml b/ci/environment-3.4.yml deleted file mode 100644 index 7d943d9a6..000000000 --- a/ci/environment-3.4.yml +++ /dev/null @@ -1,21 +0,0 @@ -name: py34-xonsh-test -channels: - - conda-forge - - defaults -dependencies: - - python=3.4 - - pygments - - prompt_toolkit - - pytest - - pytest-timeout - - numpy - - psutil - - matplotlib - - flake8 - - coverage - - pyflakes - - pytest-cov - - codecov - # conda forge doesn't have the following for Python v3.4 - - pip: - - pytest-flake8 diff --git a/docs/intro.rst b/docs/intro.rst index bf3490d1c..12177bef4 100644 --- a/docs/intro.rst +++ b/docs/intro.rst @@ -55,7 +55,7 @@ the xonsh shell

Xonsh is a Python-powered, cross-platform, Unix-gazing shell language and -command prompt. The language is a superset of Python 3.4+ with additional +command prompt. The language is a superset of Python 3.5+ with additional shell primitives that you are used to from Bash and IPython. It works on all major systems including Linux, Mac OSX, and Windows. Xonsh is meant for the daily use of experts and novices alike. diff --git a/setup.py b/setup.py index 2d59a804a..3be5ebba4 100755 --- a/setup.py +++ b/setup.py @@ -403,7 +403,7 @@ def main(): "linux": ["distro"], "proctitle": ["setproctitle"], } - skw["python_requires"] = ">=3.4" + skw["python_requires"] = ">=3.5" setup(**skw) diff --git a/tests/test_parser.py b/tests/test_parser.py index ae0ad74a7..c81df6331 100644 --- a/tests/test_parser.py +++ b/tests/test_parser.py @@ -11,7 +11,7 @@ from xonsh.ast import AST, With, Pass, pdump from xonsh.parser import Parser from xonsh.parsers.base import eval_fstr_fields -from tools import VER_FULL, skip_if_py34, skip_if_lt_py36, nodes_equal +from tools import VER_FULL, skip_if_lt_py36, nodes_equal # a lot of col_offset data changed from Py v3.5.0 -> v3.5.1 INC_ATTRS = (3, 5, 1) <= VER_FULL @@ -186,7 +186,6 @@ def test_binop_times(): check_ast("42 * 65") -@skip_if_py34 def test_binop_matmult(): check_ast("x @ y", False) @@ -682,57 +681,46 @@ def test_dict_three(): check_ast("{42: 65, 6: 28, 1: 2}") -@skip_if_py34 def test_dict_from_dict_two_xy(): check_ast('{"x": 1, **{"y": 2}}') -@skip_if_py34 def test_dict_from_dict_two_x_first(): check_ast('{"x": 1, **{"x": 2}}') -@skip_if_py34 def test_dict_from_dict_two_x_second(): check_ast('{**{"x": 2}, "x": 1}') -@skip_if_py34 def test_unpack_range_tuple(): check_stmts("*range(4),") -@skip_if_py34 def test_unpack_range_tuple_4(): check_stmts("*range(4), 4") -@skip_if_py34 def test_unpack_range_tuple_parens(): check_ast("(*range(4),)") -@skip_if_py34 def test_unpack_range_tuple_parens_4(): check_ast("(*range(4), 4)") -@skip_if_py34 def test_unpack_range_list(): check_ast("[*range(4)]") -@skip_if_py34 def test_unpack_range_list_4(): check_ast("[*range(4), 4]") -@skip_if_py34 def test_unpack_range_set(): check_ast("{*range(4)}") -@skip_if_py34 def test_unpack_range_set_4(): check_ast("{*range(4), 4}") @@ -1097,17 +1085,14 @@ def test_call_dict_kwargs(): check_ast('dict(**{"base": 8})') -@skip_if_py34 def test_call_list_many_star_args(): check_ast("min(*[1, 2], 3, *[4, 5])") -@skip_if_py34 def test_call_list_many_starstar_args(): check_ast('dict(**{"a": 2}, v=3, **{"c": 5})') -@skip_if_py34 def test_call_list_many_star_and_starstar_args(): check_ast('x(*[("a", 2)], *[("v", 3)], **{"c": 5})', False) @@ -1253,7 +1238,6 @@ def test_times_eq(): check_stmts("x = 42; x *= 2") -@skip_if_py34 def test_matmult_eq(): check_stmts("x @= y", False) @@ -1649,7 +1633,6 @@ def test_for_else(): check_stmts("for x in range(6):\n pass\nelse: pass") -@skip_if_py34 def test_async_for(): check_stmts("async def f():\n async for x in y:\n pass\n", False) @@ -1678,7 +1661,6 @@ def test_with_in_func(): check_stmts("def f():\n with x:\n pass\n") -@skip_if_py34 def test_async_with(): check_stmts("async def f():\n async with x as y:\n pass\n", False) @@ -2014,17 +1996,14 @@ def test_function_blank_line(): check_stmts(code, False) -@skip_if_py34 def test_async_func(): check_stmts("async def f():\n pass\n") -@skip_if_py34 def test_async_decorator(): check_stmts("@g\nasync def f():\n pass", False) -@skip_if_py34 def test_async_await(): check_stmts("async def f():\n await fut\n", False) diff --git a/tests/test_prompt.py b/tests/test_prompt.py index ea24305cb..d28e4b710 100644 --- a/tests/test_prompt.py +++ b/tests/test_prompt.py @@ -9,7 +9,7 @@ from xonsh.environ import Env from xonsh.prompt.base import PromptFormatter, PROMPT_FIELDS from xonsh.prompt import vc -from tools import skip_if_py34, DummyEnv +from tools import DummyEnv @pytest.fixture diff --git a/tests/test_tools.py b/tests/test_tools.py index df69c2898..22c1c672e 100644 --- a/tests/test_tools.py +++ b/tests/test_tools.py @@ -76,7 +76,7 @@ from xonsh.tools import ( ) from xonsh.environ import Env -from tools import skip_if_on_windows, skip_if_on_unix, skip_if_py34 +from tools import skip_if_on_windows, skip_if_on_unix LEXER = Lexer() LEXER.build() @@ -1653,7 +1653,6 @@ def test_iglobpath_no_dotfiles_recursive(xonsh_builtins): assert d + "/bin/.someotherdotfile" not in files -@skip_if_py34 @skip_if_on_windows def test_iglobpath_dotfiles_recursive(xonsh_builtins): d = os.path.dirname(__file__) diff --git a/tests/tools.py b/tests/tools.py index 3f1ac6219..1956862e6 100644 --- a/tests/tools.py +++ b/tests/tools.py @@ -18,7 +18,6 @@ from xonsh.base_shell import BaseShell from xonsh.platform import ptk_version_info -VER_3_4 = (3, 4) VER_3_5 = (3, 5) VER_3_6 = (3, 6) VER_MAJOR_MINOR = sys.version_info[:2] @@ -36,7 +35,6 @@ print("os.environ['TF_BUILD']", repr(os.environ.get("TF_BUILD", ""))) TEST_DIR = os.path.dirname(__file__) # pytest skip decorators -skip_if_py34 = pytest.mark.skipif(VER_MAJOR_MINOR < VER_3_5, reason="Py3.5+ only test") skip_if_lt_py36 = pytest.mark.skipif( VER_MAJOR_MINOR < VER_3_6, reason="Py3.6+ only test" ) diff --git a/xonsh-in-docker.py b/xonsh-in-docker.py index 8bed63021..5a5016a94 100755 --- a/xonsh-in-docker.py +++ b/xonsh-in-docker.py @@ -9,7 +9,7 @@ program_description = """Build and run Xonsh in a fresh, controlled parser = argparse.ArgumentParser(description=program_description) parser.add_argument("env", nargs="*", default=[], metavar="ENV=value") -parser.add_argument("--python", "-p", default="3.4", metavar="python_version") +parser.add_argument("--python", "-p", default="3.5", metavar="python_version") parser.add_argument("--pypy", default=None, metavar="pypy_version") parser.add_argument("--ptk", "-t", default="2.0.4", metavar="ptk_version") parser.add_argument("--keep", action="store_true") diff --git a/xonsh/parser.py b/xonsh/parser.py index f70a05c6a..71c7b8a7d 100644 --- a/xonsh/parser.py +++ b/xonsh/parser.py @@ -10,6 +10,4 @@ def Parser(): from xonsh.parsers.v36 import Parser as p elif PYTHON_VERSION_INFO > (3, 5): from xonsh.parsers.v35 import Parser as p - else: - from xonsh.parsers.v34 import Parser as p return p diff --git a/xonsh/parsers/v34.py b/xonsh/parsers/v34.py deleted file mode 100644 index f8d7961a8..000000000 --- a/xonsh/parsers/v34.py +++ /dev/null @@ -1,157 +0,0 @@ -# -*- coding: utf-8 -*- -"""Implements the xonsh parser for Python v3.4.""" -import xonsh.ast as ast -from xonsh.parsers.base import BaseParser - - -class Parser(BaseParser): - """A Python v3.4 compliant parser for the xonsh language.""" - - def __init__( - self, - lexer_optimize=True, - lexer_table="xonsh.lexer_table", - yacc_optimize=True, - yacc_table="xonsh.parser_table", - yacc_debug=False, - outputdir=None, - ): - """Parameters - ---------- - lexer_optimize : bool, optional - Set to false when unstable and true when lexer is stable. - lexer_table : str, optional - Lexer module used when optimized. - yacc_optimize : bool, optional - Set to false when unstable and true when parser is stable. - yacc_table : str, optional - Parser module used when optimized. - yacc_debug : debug, optional - Dumps extra debug info. - outputdir : str or None, optional - The directory to place generated tables within. - """ - # Rule creation and modification *must* take place before super() - opt_rules = ["argument_comma_list", "comma_argument_list"] - for rule in opt_rules: - self._opt_rule(rule) - - list_rules = ["argument_comma"] - for rule in list_rules: - self._list_rule(rule) - - super().__init__( - lexer_optimize=lexer_optimize, - lexer_table=lexer_table, - yacc_optimize=yacc_optimize, - yacc_table=yacc_table, - yacc_debug=yacc_debug, - outputdir=outputdir, - ) - - def p_classdef_or_funcdef(self, p): - """classdef_or_funcdef : classdef - | funcdef - """ - p[0] = p[1] - - def p_item(self, p): - """item : test COLON test""" - lenp = len(p) - if lenp == 4: - p0 = [p[1], p[3]] - elif lenp == 3: - p0 = [None, p[2]] - else: - assert False - p[0] = p0 - - def _set_arg(self, args, arg, ensure_kw=False): - if isinstance(arg, ast.keyword): - args["keywords"].append(arg) - elif ensure_kw: - args["kwargs"] = arg - else: - args["args"].append(arg) - - def p_arglist(self, p): - """arglist : argument comma_opt - | argument_comma_list argument comma_opt - | argument_comma_list_opt TIMES test comma_argument_list_opt - | argument_comma_list_opt TIMES test COMMA POW test - | argument_comma_list_opt TIMES test comma_argument_list COMMA POW test - | argument_comma_list_opt POW test - """ - lenp = len(p) - p1, p2 = p[1], p[2] - p0 = {"args": [], "keywords": [], "starargs": None, "kwargs": None} - if lenp == 3: - self._set_arg(p0, p1) - elif lenp == 4 and p2 != "**": - for arg in p1: - self._set_arg(p0, arg) - self._set_arg(p0, p2) - elif lenp == 4 and p2 == "**": - if p1 is not None: - for arg in p1: - self._set_arg(p0, arg) - self._set_arg(p0, p[3], ensure_kw=True) - elif lenp == 5: - p0["starargs"], p4 = p[3], p[4] - if p1 is not None: - for arg in p1: - self._set_arg(p0, arg) - if p4 is not None: - for arg in p4: - self._set_arg(p0, arg, ensure_kw=True) - elif lenp == 7: - p0["starargs"] = p[3] - if p1 is not None: - for arg in p1: - self._set_arg(p0, arg) - self._set_arg(p0, p[6], ensure_kw=True) - elif lenp == 8: - p0["starargs"], p4 = p[3], p[4] - if p1 is not None: - for arg in p1: - self._set_arg(p0, arg) - for arg in p4: - self._set_arg(p0, arg, ensure_kw=True) - self._set_arg(p0, p[7], ensure_kw=True) - else: - assert False - p[0] = p0 - - def p_argument_comma(self, p): - """argument_comma : argument COMMA""" - p[0] = [p[1]] - - def p_argument(self, p): - """argument : test - | test comp_for - | test EQUALS test - """ - # Really [keyword '='] test - # The reason that keywords are test nodes instead of NAME is that using - # NAME results in an ambiguity. - p1 = p[1] - lenp = len(p) - if lenp == 2: - p0 = p1 - elif lenp == 3: - if p1 == "**": - p0 = ast.keyword(arg=None, value=p[2]) - elif p1 == "*": - p0 = ast.Starred(value=p[2]) - else: - p0 = ast.GeneratorExp( - elt=p1, - generators=p[2]["comps"], - lineno=p1.lineno, - col_offset=p1.col_offset, - ) - elif lenp == 4: - p0 = ast.keyword(arg=p1.id, value=p[3]) - else: - assert False - p[0] = p0