From 076fb56f8569ac6424182a174fc3a777c5ae75f4 Mon Sep 17 00:00:00 2001 From: feuillemorte Date: Tue, 16 Jan 2018 21:30:44 +0300 Subject: [PATCH 1/6] show a simple and easy error when keyword expressions trigger a syntax error --- _pytest/mark.py | 14 +++++++++++++- testing/test_mark.py | 15 +++++++++++++++ 2 files changed, 28 insertions(+), 1 deletion(-) diff --git a/_pytest/mark.py b/_pytest/mark.py index 3f1f01b1a2e..da11fc563d0 100644 --- a/_pytest/mark.py +++ b/_pytest/mark.py @@ -222,6 +222,12 @@ def __getitem__(self, subname): return False +# python keywords except or, and, not +python_keywords_list = ["False", "None", "True", "as", "assert", "break", "class", "continue", "def", "del", + "elif", "else", "except", "finally", "for", "from", "global", "if", "import", "in", "is", + "lambda", "nonlocal", "pass", "raise", "return", "try", "while", "with", "yield"] + + def matchmark(colitem, markexpr): """Tries to match on any marker names, attached to the given colitem.""" return eval(markexpr, {}, MarkMapping.from_keywords(colitem.keywords)) @@ -259,7 +265,13 @@ def matchkeyword(colitem, keywordexpr): return mapping[keywordexpr] elif keywordexpr.startswith("not ") and " " not in keywordexpr[4:]: return not mapping[keywordexpr[4:]] - return eval(keywordexpr, {}, mapping) + for keyword in keywordexpr.split(): + if keyword in python_keywords_list: + raise AttributeError("Python keyword '{}' not accepted in expressions passed to '-k'".format(keyword)) + try: + return eval(keywordexpr, {}, mapping) + except SyntaxError: + raise AttributeError("Wrong expression passed to '-k': {}".format(keywordexpr)) def pytest_configure(config): diff --git a/testing/test_mark.py b/testing/test_mark.py index 46bf0b0e778..f3a7af1d1dd 100644 --- a/testing/test_mark.py +++ b/testing/test_mark.py @@ -344,6 +344,21 @@ def test_func(arg): assert list(passed) == list(passed_result) +@pytest.mark.parametrize("spec", [ + ("foo or import", "AttributeError: Python keyword 'import' not accepted in expressions passed to '-k'"), + ("foo or", "AttributeError: Wrong expression passed to '-k': foo or") +]) +def test_keyword_option_wrong_arguments(spec, testdir, capsys): + testdir.makepyfile(""" + def test_func(arg): + pass + """) + opt, expected_result = spec + testdir.inline_run("-k", opt) + out = capsys.readouterr()[0] + assert expected_result in out + + def test_parametrized_collected_from_command_line(testdir): """Parametrized test not collected if test named specified in command line issue#649. From dff597dcd02f65eaefdc2be2e1a6123c3d65b35f Mon Sep 17 00:00:00 2001 From: feuillemorte Date: Tue, 16 Jan 2018 21:34:13 +0300 Subject: [PATCH 2/6] Add changelog entry --- changelog/2953.trivial | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelog/2953.trivial diff --git a/changelog/2953.trivial b/changelog/2953.trivial new file mode 100644 index 00000000000..52ea6cf31b5 --- /dev/null +++ b/changelog/2953.trivial @@ -0,0 +1 @@ +Show a simple and easy error when keyword expressions trigger a syntax error (for example, "-k foo and import" will show an error that you can not use import in expressions) From 648d5d0c6bdd5769580b047208891729b8c6d12f Mon Sep 17 00:00:00 2001 From: feuillemorte Date: Tue, 16 Jan 2018 22:55:24 +0300 Subject: [PATCH 3/6] #2953 fix comments: use keyword module --- _pytest/mark.py | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/_pytest/mark.py b/_pytest/mark.py index da11fc563d0..4f6fc5813bc 100644 --- a/_pytest/mark.py +++ b/_pytest/mark.py @@ -2,6 +2,7 @@ from __future__ import absolute_import, division, print_function import inspect +import keyword import warnings import attr from collections import namedtuple @@ -222,10 +223,7 @@ def __getitem__(self, subname): return False -# python keywords except or, and, not -python_keywords_list = ["False", "None", "True", "as", "assert", "break", "class", "continue", "def", "del", - "elif", "else", "except", "finally", "for", "from", "global", "if", "import", "in", "is", - "lambda", "nonlocal", "pass", "raise", "return", "try", "while", "with", "yield"] +python_keywords_allowed_list = ["or", "and", "not"] def matchmark(colitem, markexpr): @@ -265,9 +263,9 @@ def matchkeyword(colitem, keywordexpr): return mapping[keywordexpr] elif keywordexpr.startswith("not ") and " " not in keywordexpr[4:]: return not mapping[keywordexpr[4:]] - for keyword in keywordexpr.split(): - if keyword in python_keywords_list: - raise AttributeError("Python keyword '{}' not accepted in expressions passed to '-k'".format(keyword)) + for kwd in keywordexpr.split(): + if keyword.iskeyword(kwd) and kwd not in python_keywords_allowed_list: + raise AttributeError("Python keyword '{}' not accepted in expressions passed to '-k'".format(kwd)) try: return eval(keywordexpr, {}, mapping) except SyntaxError: From 86e1b442308d71ca6ecb79d5a35d532330eecf46 Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Tue, 16 Jan 2018 18:10:15 -0200 Subject: [PATCH 4/6] Improve changelog formatting --- changelog/2953.trivial | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/changelog/2953.trivial b/changelog/2953.trivial index 52ea6cf31b5..25d9115c1a0 100644 --- a/changelog/2953.trivial +++ b/changelog/2953.trivial @@ -1 +1 @@ -Show a simple and easy error when keyword expressions trigger a syntax error (for example, "-k foo and import" will show an error that you can not use import in expressions) +Show a simple and easy error when keyword expressions trigger a syntax error (for example, ``"-k foo and import"`` will show an error that you can not use the ``import`` keyword in expressions). From 8433e2ba04b39a4edca9795f4df69f843ce8106a Mon Sep 17 00:00:00 2001 From: feuillemorte Date: Tue, 16 Jan 2018 23:35:57 +0300 Subject: [PATCH 5/6] #2953 fix comments: fix exception type --- _pytest/mark.py | 6 ++++-- testing/test_mark.py | 6 +++--- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/_pytest/mark.py b/_pytest/mark.py index 4f6fc5813bc..6d095a592ff 100644 --- a/_pytest/mark.py +++ b/_pytest/mark.py @@ -8,6 +8,8 @@ from collections import namedtuple from operator import attrgetter from six.moves import map + +from _pytest.config import UsageError from .deprecated import MARK_PARAMETERSET_UNPACKING from .compat import NOTSET, getfslineno @@ -265,11 +267,11 @@ def matchkeyword(colitem, keywordexpr): return not mapping[keywordexpr[4:]] for kwd in keywordexpr.split(): if keyword.iskeyword(kwd) and kwd not in python_keywords_allowed_list: - raise AttributeError("Python keyword '{}' not accepted in expressions passed to '-k'".format(kwd)) + raise UsageError("Python keyword '{}' not accepted in expressions passed to '-k'".format(kwd)) try: return eval(keywordexpr, {}, mapping) except SyntaxError: - raise AttributeError("Wrong expression passed to '-k': {}".format(keywordexpr)) + raise UsageError("Wrong expression passed to '-k': {}".format(keywordexpr)) def pytest_configure(config): diff --git a/testing/test_mark.py b/testing/test_mark.py index f3a7af1d1dd..45e88ae8f4b 100644 --- a/testing/test_mark.py +++ b/testing/test_mark.py @@ -345,8 +345,8 @@ def test_func(arg): @pytest.mark.parametrize("spec", [ - ("foo or import", "AttributeError: Python keyword 'import' not accepted in expressions passed to '-k'"), - ("foo or", "AttributeError: Wrong expression passed to '-k': foo or") + ("foo or import", "ERROR: Python keyword 'import' not accepted in expressions passed to '-k'"), + ("foo or", "ERROR: Wrong expression passed to '-k': foo or") ]) def test_keyword_option_wrong_arguments(spec, testdir, capsys): testdir.makepyfile(""" @@ -355,7 +355,7 @@ def test_func(arg): """) opt, expected_result = spec testdir.inline_run("-k", opt) - out = capsys.readouterr()[0] + out = capsys.readouterr().err assert expected_result in out From e3406e0818f4496f5d3c601da66b6dca9f53c3ba Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Tue, 16 Jan 2018 19:35:32 -0200 Subject: [PATCH 6/6] Show usage errors in red --- _pytest/config.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/_pytest/config.py b/_pytest/config.py index ce7468f7204..22bf6c60c1d 100644 --- a/_pytest/config.py +++ b/_pytest/config.py @@ -60,8 +60,9 @@ def main(args=None, plugins=None): finally: config._ensure_unconfigure() except UsageError as e: + tw = py.io.TerminalWriter(sys.stderr) for msg in e.args: - sys.stderr.write("ERROR: %s\n" % (msg,)) + tw.line("ERROR: {}\n".format(msg), red=True) return 4