Repository navigation
Conversation
…ureMeta properties
Declaring a Signature field whose name collides with a SignatureMeta
property (e.g. `instructions`, which is a class-level property on the
metaclass for reading/writing the docstring) crashed signature class
construction with an opaque internal error:
class MySig(dspy.Signature):
instructions: str = dspy.InputField()
answer: str = dspy.OutputField()
# TypeError: 'NoneType' object is not subscriptable
Pydantic detects the name collision (it emits its own "shadows an
attribute in parent" UserWarning) and, for that field only, produces a
plain FieldInfo with json_schema_extra=None instead of preserving the
InputField()/OutputField() instance from the class body. SignatureMeta
already has validation for exactly this shape of problem -
_validate_fields() raises a clear TypeError when a field's
json_schema_extra doesn't carry the expected __dspy_field_type key -
but it ran too late: SignatureMeta.__new__ computes default
instructions first when no docstring is given, via
_default_instructions(cls) -> cls.input_fields ->
_get_fields_with_type(), which indexes json_schema_extra directly and
crashes before _validate_fields() ever gets a chance to run.
Move the existing _validate_fields() call to run immediately after
Pydantic builds the class, before anything reads json_schema_extra.
No new validation logic - just correct ordering of what was already
there.
Fixes stanfordnlp#1710.
Greptile SummaryThis change validates DSPy Signature fields immediately after Pydantic creates the class, preventing metadata access from obscuring malformed field declarations. The focused before-and-after check confirmed that normal Signature construction, field classification, generated instructions, and serialization still work, while an T-Rex validation blockedThe focused repository pytest command could not collect because the Confidence Score: 5/5Safe to merge based on the exercised Signature construction behavior. The before-and-after harness directly exercised the changed metaclass path: valid Signatures remained usable, and the property-shadowing failure now produces the intended validation error. No defects remain to report. Files Needing Attention: No additional files need attention; the focused implementation path in
What T-Rex did
Reviews (1): Last reviewed commit: "fix(signatures): raise clear error for f..." | Re-trigger Greptile |
📝 Changes Description
Declaring a
Signaturefield whose name collides with aSignatureMetaproperty (e.g.instructions, the metaclass property used to read/write the docstring) crashes signature class construction with an opaque internal error instead of a clear validation message:Root cause: Pydantic detects the name collision itself (it emits its own
UserWarning: Field name "instructions" ... shadows an attribute in parent "Signature") and, for that field only, produces a plainFieldInfowithjson_schema_extra=Noneinstead of preserving theInputField()/OutputField()instance from the class body.SignatureMetaalready has validation for exactly this shape of problem —_validate_fields()raises a clearTypeErrorwhen a field'sjson_schema_extradoesn't carry the expected__dspy_field_typekey — but it ran too late in__new__: when no docstring is given,_default_instructions(cls)runs first, which callscls.input_fields→_get_fields_with_type(), which indexesjson_schema_extradirectly and crashes before_validate_fields()ever gets a chance to run.Fix: move the existing
_validate_fields()call to run immediately after Pydantic builds the class, before anything readsjson_schema_extra. No new validation logic — just correct ordering of what was already there. After the fix:Fixes #1710.
✅ Contributor Checklist
fix(signatures): ...None — this only changes the order of two pre-existing steps in
SignatureMeta.__new__; no new validation logic was added, so behavior for non-colliding field names (the common case) is unchanged.Testing
TypeError: 'NoneType' object is not subscriptablebefore reapplying it._validate_fieldserror is now raised instead.uv run pytest tests/signatures/ tests/primitives/ -q: 172 passed (6 deselected pre-existing Windows temp-file-permission failures, confirmed present on unmodifiedmaintoo and unrelated to signature construction).uv run pytest tests/ -q(full suite, minus pre-existing environment failures — see companion PR fix(primitives): prevent named_parameters()/named_sub_modules() dict-key collisions #10241 for the same three confirmed-pre-existing exclusions): all passing.