pytest-dev/pytest · error · SyntaxError

unexpected reserved python keyword `{keyword_name.value}`

Error message

unexpected reserved python keyword `{keyword_name.value}`

What it means

The kwarg name in `ident(name=value)` lexes as a valid identifier but collides with a reserved Python keyword (`class`, `def`, `return`, `lambda`, ...). Because the compiled expression is turned into Python AST, reserved words cannot be used as kwarg names; this SyntaxError is raised at the name's column.

Source

Thrown at src/_pytest/mark/expression.py:222

        else:
            ret = name
        return ret

    s.reject((TokenType.NOT, TokenType.LPAREN, TokenType.IDENT))


BUILTIN_MATCHERS = {"True": True, "False": False, "None": None}


def single_kwarg(s: Scanner) -> ast.keyword:
    keyword_name = s.accept(TokenType.IDENT, reject=True)
    if not keyword_name.value.isidentifier():
        raise SyntaxError(
            f"not a valid python identifier {keyword_name.value}",
            (FILE_NAME, 1, keyword_name.pos + 1, s.input),
        )
    if keyword.iskeyword(keyword_name.value):
        raise SyntaxError(
            f"unexpected reserved python keyword `{keyword_name.value}`",
            (FILE_NAME, 1, keyword_name.pos + 1, s.input),
        )
    s.accept(TokenType.EQUAL, reject=True)

    if value_token := s.accept(TokenType.STRING):
        value: str | int | bool | None = value_token.value[1:-1]  # strip quotes
    else:
        value_token = s.accept(TokenType.IDENT, reject=True)
        if (number := value_token.value).isdigit() or (
            number.startswith("-") and number[1:].isdigit()
        ):
            value = int(number)
        elif value_token.value in BUILTIN_MATCHERS:
            value = BUILTIN_MATCHERS[value_token.value]
        else:
            raise SyntaxError(
                f'unexpected character/s "{value_token.value}"',

View on GitHub (pinned to 98b357f69e)

Solutions

  1. Pick a non-reserved synonym (e.g. `klass`, `returns`).
  2. When generating expressions programmatically, skip names where `keyword.iskeyword(name)` is True.

Example fix

# before
pytest -m 'schema(class=user)'
# after
pytest -m 'schema(klass="user")'
Defensive patterns

Strategy: validation

Validate before calling

import keyword
def not_reserved(name: str) -> bool:
    return not keyword.iskeyword(name)

Type guard

import keyword
def is_safe_kwarg_name(name: object) -> bool:
    return isinstance(name, str) and name.isidentifier() and not keyword.iskeyword(name)

Prevention

When it happens

Trigger: `foo(class=...)`, `foo(return=...)`, or any kwarg whose name is a Python keyword.

Common situations: Naming kwargs after Python keywords for domain readability; generating marker expressions programmatically without filtering keywords.

Related errors


AI-assisted analysis of pytest-dev/pytest@98b357f69e (2026-08-04). Data as JSON: /data/errors/ccbc86137abb25a9.json. Report an issue: GitHub.