Repository: pytest-dev/pytest
File: src/_pytest/mark/__init__.py (Python)

Code before:
    """A matcher for markers which are present.

    Tries to match on any marker names, attached to the given colitem.
    """

    __slots__ = ("own_mark_name_mapping",)

    own_mark_name_mapping: Dict[str, List["Mark"]]

    @classmethod
    def from_item(cls, item: "Item") -> "MarkMatcher":
        mark_name_mapping = collections.defaultdict(list)
        for mark in item.iter_markers():
            mark_name_mapping[mark.name].append(mark)
        return cls(mark_name_mapping)

    def __call__(self, name: str, /, **kwargs: object) -> bool:
        if not (matches := self.own_mark_name_mapping.get(name, [])):
            return False

        if not kwargs:
            return True

        for mark in matches:
            if all(
                mark.kwargs.get(k, NOT_NONE_SENTINEL) == v for k, v in kwargs.items()
            ):
                return True

        return False


def deselect_by_mark(items: "List[Item]", config: Config) -> None:
    matchexpr = config.option.markexpr
    if not matchexpr:
        return

    expr = _parse_expression(matchexpr, "Wrong expression passed to '-m'")
    remaining: List[Item] = []
    deselected: List[Item] = []
    for item in items:
        if expr.evaluate(MarkMatcher.from_item(item)):
            remaining.append(item)
        else:
            deselected.append(item)
    if deselected:
        config.hook.pytest_deselected(items=deselected)
        items[:] = remaining


def _parse_expression(expr: str, exc_message: str) -> Expression:

Review comment (suggestion):
Sentinel objects are typically compared though the identity operator, not equality (`is None`, `is not False` etc.)

So the following might be more correct:
```suggestion
                mark.kwargs.get(k, NOT_NONE_SENTINEL) is v for k, v in kwargs.items()
```

Code after:
    Tries to match on any marker names, attached to the given colitem.
    """

    __slots__ = ("own_mark_name_mapping",)

    own_mark_name_mapping: dict[str, list[Mark]]

    @classmethod
    def from_markers(cls, markers: Iterable[Mark]) -> MarkMatcher:
        mark_name_mapping = collections.defaultdict(list)
        for mark in markers:
            mark_name_mapping[mark.name].append(mark)
        return cls(mark_name_mapping)

    def __call__(self, name: str, /, **kwargs: str | int | bool | None) -> bool:
        if not (matches := self.own_mark_name_mapping.get(name, [])):
            return False

        for mark in matches:
            if all(mark.kwargs.get(k, NOT_SET) == v for k, v in kwargs.items()):
                return True

        return False


def deselect_by_mark(items: list[Item], config: Config) -> None:
    matchexpr = config.option.markexpr
    if not matchexpr:
        return

    expr = _parse_expression(matchexpr, "Wrong expression passed to '-m'")
    remaining: list[Item] = []
    deselected: list[Item] = []
    for item in items:
        if expr.evaluate(MarkMatcher.from_markers(item.iter_markers())):
            remaining.append(item)
        else:
            deselected.append(item)
    if deselected:
        config.hook.pytest_deselected(items=deselected)
        items[:] = remaining


def _parse_expression(expr: str, exc_message: str) -> Expression:
    try:
        return Expression.compile(expr)
    except ParseError as e:
        raise UsageError(f"{exc_message}: {expr}: {e}") from None


def pytest_collection_modifyitems(items: list[Item], config: Config) -> None: