{"record":{"id":"0fc7ef7b8eea2769","repo":"pytest-dev/pytest","slug":"not-a-valid-python-identifier-keyword-name-value","errorCode":null,"errorMessage":"not a valid python identifier {keyword_name.value}","messagePattern":"not a valid python identifier (.+?)","errorType":"exception","errorClass":"SyntaxError","httpStatus":null,"severity":"error","filePath":"src/_pytest/mark/expression.py","lineNumber":217,"sourceCode":"    if ident:\n        name = ast.Name(IDENT_PREFIX + ident.value, ast.Load())\n        if s.accept(TokenType.LPAREN):\n            ret = ast.Call(func=name, args=[], keywords=all_kwargs(s))\n            s.accept(TokenType.RPAREN, reject=True)\n        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)","sourceCodeStart":199,"sourceCodeEnd":235,"githubUrl":"https://github.com/pytest-dev/pytest/blob/0d6fbdeffa57c796123f62f81f7dd370d9b7ecdc/src/_pytest/mark/expression.py#L199-L235","documentation":"A SyntaxError raised in single_kwarg when the kwarg name token (parsed as an IDENT) is not a valid Python identifier according to str.isidentifier(). Because the tokenizer accepts a broad character class, an identifier-like token such as `1foo`, `a-b`, or `a.b` can reach this check and be rejected. The column reported is the 1-based position of the offending name.","triggerScenarios":"Marker expressions like `-m \"foo(1x='y')\"`, `-m \"foo(a-b='y')\"`, or `-m \"foo(a.b='y')\"` where the kwarg name fails Python identifier rules. Also triggered by tokens that the lexer grouped but which are not legal parameter names.","commonSituations":"Using numeric or symbol-laden kwarg names in marker filters, mistyping a kwarg name, or assuming Python attribute syntax (dashes, dots) works in marker expressions.","solutions":["Rename the kwarg to a valid Python identifier: letters, digits, underscores, not starting with a digit.","Check that you did not accidentally include a hyphen or dot from a CLI flag.","Define the marker's kwargs with proper identifier names in conftest or plugin code."],"exampleFix":"# before\npytest -m \"foo(1x='y')\"\n# after\npytest -m \"foo(x='y')\"","handlingStrategy":"type-guard","validationCode":"def valid_kwarg_name(name: str) -> bool:\n    return name.isidentifier() and not name[0].isdigit()","typeGuard":"def is_valid_marker_kwarg_name(name: str) -> bool:\n    return isinstance(name, str) and name.isidentifier()","tryCatchPattern":null,"preventionTips":["Use Python-identifier rules for marker kwarg names.","Avoid dots, dashes, and leading digits in kwarg names.","Generate kwarg names from an allow-list."],"tags":["pytest","marker-expression","kwarg","syntax-error","identifier"],"backgroundTag":null,"analyzedSha":"0d6fbdeffa57c796123f62f81f7dd370d9b7ecdc","analyzedAt":"2026-08-11T20:52:36.969Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}