| """ generic mechanism for marking and selecting python functions. """ |
| from __future__ import absolute_import, division, print_function |
| from _pytest.config import UsageError |
| from .structures import ( |
| ParameterSet, |
| EMPTY_PARAMETERSET_OPTION, |
| MARK_GEN, |
| Mark, |
| MarkInfo, |
| MarkDecorator, |
| MarkGenerator, |
| transfer_markers, |
| get_empty_parameterset_mark, |
| ) |
| from .legacy import matchkeyword, matchmark |
| |
| __all__ = [ |
| "Mark", |
| "MarkInfo", |
| "MarkDecorator", |
| "MarkGenerator", |
| "transfer_markers", |
| "get_empty_parameterset_mark", |
| ] |
| |
| |
| class MarkerError(Exception): |
| |
| """Error in use of a pytest marker/attribute.""" |
| |
| |
| def param(*values, **kw): |
| """Specify a parameter in `pytest.mark.parametrize`_ calls or |
| :ref:`parametrized fixtures <fixture-parametrize-marks>`. |
| |
| .. code-block:: python |
| |
| @pytest.mark.parametrize("test_input,expected", [ |
| ("3+5", 8), |
| pytest.param("6*9", 42, marks=pytest.mark.xfail), |
| ]) |
| def test_eval(test_input, expected): |
| assert eval(test_input) == expected |
| |
| :param values: variable args of the values of the parameter set, in order. |
| :keyword marks: a single mark or a list of marks to be applied to this parameter set. |
| :keyword str id: the id to attribute to this parameter set. |
| """ |
| return ParameterSet.param(*values, **kw) |
| |
| |
| def pytest_addoption(parser): |
| group = parser.getgroup("general") |
| group._addoption( |
| "-k", |
| action="store", |
| dest="keyword", |
| default="", |
| metavar="EXPRESSION", |
| help="only run tests which match the given substring expression. " |
| "An expression is a python evaluatable expression " |
| "where all names are substring-matched against test names " |
| "and their parent classes. Example: -k 'test_method or test_" |
| "other' matches all test functions and classes whose name " |
| "contains 'test_method' or 'test_other', while -k 'not test_method' " |
| "matches those that don't contain 'test_method' in their names. " |
| "Additionally keywords are matched to classes and functions " |
| "containing extra names in their 'extra_keyword_matches' set, " |
| "as well as functions which have names assigned directly to them.", |
| ) |
| |
| group._addoption( |
| "-m", |
| action="store", |
| dest="markexpr", |
| default="", |
| metavar="MARKEXPR", |
| help="only run tests matching given mark expression. " |
| "example: -m 'mark1 and not mark2'.", |
| ) |
| |
| group.addoption( |
| "--markers", |
| action="store_true", |
| help="show markers (builtin, plugin and per-project ones).", |
| ) |
| |
| parser.addini("markers", "markers for test functions", "linelist") |
| parser.addini(EMPTY_PARAMETERSET_OPTION, "default marker for empty parametersets") |
| |
| |
| def pytest_cmdline_main(config): |
| import _pytest.config |
| |
| if config.option.markers: |
| config._do_configure() |
| tw = _pytest.config.create_terminal_writer(config) |
| for line in config.getini("markers"): |
| parts = line.split(":", 1) |
| name = parts[0] |
| rest = parts[1] if len(parts) == 2 else "" |
| tw.write("@pytest.mark.%s:" % name, bold=True) |
| tw.line(rest) |
| tw.line() |
| config._ensure_unconfigure() |
| return 0 |
| |
| |
| pytest_cmdline_main.tryfirst = True |
| |
| |
| def deselect_by_keyword(items, config): |
| keywordexpr = config.option.keyword.lstrip() |
| if keywordexpr.startswith("-"): |
| keywordexpr = "not " + keywordexpr[1:] |
| selectuntil = False |
| if keywordexpr[-1:] == ":": |
| selectuntil = True |
| keywordexpr = keywordexpr[:-1] |
| |
| remaining = [] |
| deselected = [] |
| for colitem in items: |
| if keywordexpr and not matchkeyword(colitem, keywordexpr): |
| deselected.append(colitem) |
| else: |
| if selectuntil: |
| keywordexpr = None |
| remaining.append(colitem) |
| |
| if deselected: |
| config.hook.pytest_deselected(items=deselected) |
| items[:] = remaining |
| |
| |
| def deselect_by_mark(items, config): |
| matchexpr = config.option.markexpr |
| if not matchexpr: |
| return |
| |
| remaining = [] |
| deselected = [] |
| for item in items: |
| if matchmark(item, matchexpr): |
| remaining.append(item) |
| else: |
| deselected.append(item) |
| |
| if deselected: |
| config.hook.pytest_deselected(items=deselected) |
| items[:] = remaining |
| |
| |
| def pytest_collection_modifyitems(items, config): |
| deselect_by_keyword(items, config) |
| deselect_by_mark(items, config) |
| |
| |
| def pytest_configure(config): |
| config._old_mark_config = MARK_GEN._config |
| if config.option.strict: |
| MARK_GEN._config = config |
| |
| empty_parameterset = config.getini(EMPTY_PARAMETERSET_OPTION) |
| |
| if empty_parameterset not in ("skip", "xfail", None, ""): |
| raise UsageError( |
| "{!s} must be one of skip and xfail," |
| " but it is {!r}".format(EMPTY_PARAMETERSET_OPTION, empty_parameterset) |
| ) |
| |
| |
| def pytest_unconfigure(config): |
| MARK_GEN._config = getattr(config, "_old_mark_config", None) |