# Source code for _pytest.mark

    # -*- coding: utf-8 -*-
    """ generic mechanism for marking and selecting python functions. """
    from __future__ import absolute_import
    from __future__ import division
    from __future__ import print_function

    from .legacy import matchkeyword
    from .legacy import matchmark
    from .structures import EMPTY_PARAMETERSET_OPTION
    from .structures import get_empty_parameterset_mark
    from .structures import Mark
    from .structures import MARK_GEN
    from .structures import MarkDecorator
    from .structures import MarkGenerator
    from .structures import ParameterSet
    from _pytest.config import UsageError

    __all__ = ["Mark", "MarkDecorator", "MarkGenerator", "get_empty_parameterset_mark"]

    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. "
            "-k 'not test_method and not test_other' will eliminate the matches. "
            "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 not keywordexpr:
            return

        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
        MARK_GEN._config = config

        empty_parameterset = config.getini(EMPTY_PARAMETERSET_OPTION)

        if empty_parameterset not in ("skip", "xfail", "fail_at_collect", None, ""):
            raise UsageError(
                "{!s} must be one of skip, xfail or fail_at_collect"
                " but it is {!r}".format(EMPTY_PARAMETERSET_OPTION, empty_parameterset)
            )

    def pytest_unconfigure(config):
        MARK_GEN._config = getattr(config, "_old_mark_config", None)