{"record":{"id":"ccbc86137abb25a9","repo":"pytest-dev/pytest","slug":"unexpected-reserved-python-keyword-keyword-name","errorCode":null,"errorMessage":"unexpected reserved python keyword `{keyword_name.value}`","messagePattern":"unexpected reserved python keyword `(.+?)`","errorType":"exception","errorClass":"SyntaxError","httpStatus":null,"severity":"error","filePath":"src/_pytest/mark/expression.py","lineNumber":222,"sourceCode":"        else:\n            ret = name\n        return ret\n\n    s.reject((TokenType.NOT, TokenType.LPAREN, TokenType.IDENT))\n\n\nBUILTIN_MATCHERS = {\"True\": True, \"False\": False, \"None\": None}\n\n\ndef single_kwarg(s: Scanner) -> ast.keyword:\n    keyword_name = s.accept(TokenType.IDENT, reject=True)\n    if not keyword_name.value.isidentifier():\n        raise SyntaxError(\n            f\"not a valid python identifier {keyword_name.value}\",\n            (FILE_NAME, 1, keyword_name.pos + 1, s.input),\n        )\n    if keyword.iskeyword(keyword_name.value):\n        raise SyntaxError(\n            f\"unexpected reserved python keyword `{keyword_name.value}`\",\n            (FILE_NAME, 1, keyword_name.pos + 1, s.input),\n        )\n    s.accept(TokenType.EQUAL, reject=True)\n\n    if value_token := s.accept(TokenType.STRING):\n        value: str | int | bool | None = value_token.value[1:-1]  # strip quotes\n    else:\n        value_token = s.accept(TokenType.IDENT, reject=True)\n        if (number := value_token.value).isdigit() or (\n            number.startswith(\"-\") and number[1:].isdigit()\n        ):\n            value = int(number)\n        elif value_token.value in BUILTIN_MATCHERS:\n            value = BUILTIN_MATCHERS[value_token.value]\n        else:\n            raise SyntaxError(\n                f'unexpected character/s \"{value_token.value}\"',","sourceCodeStart":204,"sourceCodeEnd":240,"githubUrl":"https://github.com/pytest-dev/pytest/blob/0d6fbdeffa57c796123f62f81f7dd370d9b7ecdc/src/_pytest/mark/expression.py#L204-L240","documentation":"A SyntaxError raised in single_kwarg when the kwarg name is a valid identifier but collides with a reserved Python keyword (checked via keyword.iskeyword). Names like `class`, `for`, `lambda`, `return` are syntactically valid identifiers per str.isidentifier yet illegal as kwarg names because they would break the generated AST. The reserved keyword is shown in the message.","triggerScenarios":"Marker expressions like `-m \"foo(class='y')\"`, `-m \"foo(for='y')\"`, or `-m \"foo(return=1)\"` where the name is a Python reserved word. The earlier isidentifier check passes, then this guard fires.","commonSituations":"Naming marker kwargs after Python keywords, or programmatically generating kwarg names from user data that happens to be a keyword.","solutions":["Rename the kwarg to a non-keyword (e.g. `klass` instead of `class`, `for_` instead of `for`).","If generating names from data, prefix or sanitize them to avoid the keyword set (keyword.kwlist)."],"exampleFix":"# before\npytest -m \"foo(class='y')\"\n# after\npytest -m \"foo(klass='y')\"","handlingStrategy":"type-guard","validationCode":"import keyword\ndef safe_kwarg_name(name: str) -> bool:\n    return name.isidentifier() and not keyword.iskeyword(name)","typeGuard":"import keyword\ndef is_usable_kwarg_name(name: str) -> bool:\n    return isinstance(name, str) and name.isidentifier() and not keyword.iskeyword(name)","tryCatchPattern":null,"preventionTips":["Cross-check kwarg names against keyword.kwlist.","Suffix reserved words (for_, klass) when derived from data.","Validate names from external input before using as marker kwargs."],"tags":["pytest","marker-expression","kwarg","syntax-error","reserved-keyword"],"backgroundTag":null,"analyzedSha":"0d6fbdeffa57c796123f62f81f7dd370d9b7ecdc","analyzedAt":"2026-08-11T20:52:36.969Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}