{"record":{"id":"562932498be5706f","repo":"pandas-dev/pandas","slug":"k-is-a-python-keyword","errorCode":null,"errorMessage":"{k} is a python keyword","messagePattern":"(.+?) is a python keyword","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"pandas/_config/config.py","lineNumber":576,"sourceCode":"    key = key.lower()\n\n    if key in _registered_options:\n        raise OptionError(f\"Option '{key}' has already been registered\")\n    if key in _reserved_keys:\n        raise OptionError(f\"Option '{key}' is a reserved key\")\n\n    # the default value should be legal\n    if validator:\n        validator(defval)\n\n    # walk the nested dict, creating dicts as needed along the path\n    path = key.split(\".\")\n\n    for k in path:\n        if not re.match(\"^\" + tokenize.Name + \"$\", k):\n            raise ValueError(f\"{k} is not a valid identifier\")\n        if keyword.iskeyword(k):\n            raise ValueError(f\"{k} is a python keyword\")\n\n    cursor = _global_config\n    msg = \"Path prefix to option '{option}' is already an option\"\n\n    for i, p in enumerate(path[:-1]):\n        if not isinstance(cursor, dict):\n            raise OptionError(msg.format(option=\".\".join(path[:i])))\n        if p not in cursor:\n            cursor[p] = {}\n        cursor = cursor[p]\n\n    if not isinstance(cursor, dict):\n        raise OptionError(msg.format(option=\".\".join(path[:-1])))\n\n    # a namespace already lives here, i.e. `key` is a path prefix to one or\n    # more already-registered options; registering it would clobber them\n    # (GH#29242)\n    if isinstance(cursor.get(path[-1]), dict):","sourceCodeStart":558,"sourceCodeEnd":594,"githubUrl":"https://github.com/pandas-dev/pandas/blob/3b7651241d4da534b3559b60ef128e1c34f54116/pandas/_config/config.py#L558-L594","documentation":"Raised by `register_option` when a path component is a valid identifier syntactically but is a Python keyword (`if`, `for`, `class`, `lambda`, ...). Keywords are rejected via `keyword.iskeyword(k)` because they cannot be used as attribute names on `DictWrapper`.","triggerScenarios":"`register_option('for.value', ...)`, `register_option('class.enabled', ...)`, or any component that is a Python keyword.","commonSituations":"Naming an option after a language concept that happens to be reserved; auto-generating keys from external vocabulary that collides with keywords.","solutions":["Rename the keyword component to a non-keyword (e.g. `for_`, `class_`, or a descriptive name).","Check each segment with `import keyword; keyword.iskeyword(seg)` before registering."],"exampleFix":"# before\nregister_option('class.enabled', True)   # ValueError: python keyword\n\n# after\nregister_option('class_.enabled', True)","handlingStrategy":"validation","validationCode":"import keyword\ndef no_keyword_segment(key: str) -> bool:\n    return not any(keyword.iskeyword(p) for p in key.split('.'))\nif no_keyword_segment(new_key):\n    register_option(new_key, default)","typeGuard":"def is_keyword_free_path(key: str) -> bool:\n    import keyword\n    return not any(keyword.iskeyword(p) for p in key.split('.'))","tryCatchPattern":"try:\n    register_option(key, default)\nexcept ValueError as e:\n    if 'python keyword' in str(e):\n        # rename keyword segments (e.g. append '_') and retry\n        ...\n    raise","preventionTips":["Screen generated keys with `keyword.iskeyword` per segment.","Avoid naming options after language concepts (`for`, `class`, `if`, ...).","Suffix colliding segments with `_` as a simple rename policy."],"tags":["config","register-option","keyword","validation"],"backgroundTag":null,"analyzedSha":"3b7651241d4da534b3559b60ef128e1c34f54116","analyzedAt":"2026-08-11T22:10:44.015Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}