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 diff --git a/_pytest/mark.py b/_pytest/mark.py index 3f1f01b1a2e..6d095a592ff 100644 --- a/_pytest/mark.py +++ b/_pytest/mark.py @@ -2,11 +2,14 @@ from __future__ import absolute_import, division, print_function import inspect +import keyword import warnings import attr 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 @@ -222,6 +225,9 @@ def __getitem__(self, subname): return False +python_keywords_allowed_list = ["or", "and", "not"] + + 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 kwd in keywordexpr.split(): + if keyword.iskeyword(kwd) and kwd not in python_keywords_allowed_list: + raise UsageError("Python keyword '{}' not accepted in expressions passed to '-k'".format(kwd)) + try: + return eval(keywordexpr, {}, mapping) + except SyntaxError: + raise UsageError("Wrong expression passed to '-k': {}".format(keywordexpr)) def pytest_configure(config): diff --git a/changelog/2953.trivial b/changelog/2953.trivial new file mode 100644 index 00000000000..25d9115c1a0 --- /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 the ``import`` keyword in expressions). diff --git a/testing/test_mark.py b/testing/test_mark.py index 46bf0b0e778..45e88ae8f4b 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", "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(""" + def test_func(arg): + pass + """) + opt, expected_result = spec + testdir.inline_run("-k", opt) + out = capsys.readouterr().err + 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.