diff --git a/CHANGELOG.md b/CHANGELOG.md index de39050..5cad9e3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,7 +13,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - First‑class support for **open‑source models via third‑party providers** (e.g. OpenRouter, Groq and similar gateways), alongside existing Ollama + cloud integrations. ### Fixed -- TBC +- Reject Python keywords as field names before generating DSPy signatures. --- diff --git a/dspy_code/validation/input_validator.py b/dspy_code/validation/input_validator.py index f250f88..e990e23 100644 --- a/dspy_code/validation/input_validator.py +++ b/dspy_code/validation/input_validator.py @@ -4,6 +4,7 @@ Provides validation methods for user input during interactive task collection. """ +import keyword from typing import Any @@ -27,7 +28,7 @@ def validate_field_name(self, name: str) -> str: # Check for valid Python identifier name = name.strip() - if not name.isidentifier(): + if not name.isidentifier() or keyword.iskeyword(name): raise ValidationError(f"'{name}' is not a valid Python identifier") return name diff --git a/tests/test_input_field_names.py b/tests/test_input_field_names.py new file mode 100644 index 0000000..9d6225a --- /dev/null +++ b/tests/test_input_field_names.py @@ -0,0 +1,20 @@ +"""Field names must be usable in generated Python signatures.""" + +import keyword + +import pytest + +from dspy_code.validation.input_validator import InputValidator, ValidationError + + +@pytest.mark.parametrize("name", keyword.kwlist) +def test_field_name_rejects_python_keywords(name): + with pytest.raises(ValidationError, match="not a valid Python identifier"): + InputValidator().validate_field_name(name) + + +@pytest.mark.parametrize("name", ["question", "class_", "_answer", "café", "match", "case"]) +def test_field_name_accepts_usable_identifiers(name): + validated = InputValidator().validate_field_name(" " + name + " ") + assert validated == name + compile(f"class Signature:\n {validated}: str\n", "", "exec")