Repository navigation
fix(graphql): map scalar Literal to its shared GraphQL scalar on the main path - #317
Merged
Merged
Conversation
…main path Closes #316. Every Literal annotation previously fell through map_scalar_type's lenient string-matching fallback and became String: int/bool literals got a wrong type in SDL/introspection, and Literal[..., None] was forced NON_NULL although the Python type allows null. The compose path already validated Literals (_literal_info, backported in #313). This extracts those rules into graphql/type_mapping.py as the single source — the shared home for both paths (compose_schema imports from graphql.type_mapping, the same direction it already used for is_enum_type; utils/ would have inverted the layering): - literal_info / literal_is_nullable / literal_allowed_values / describe_literal_values are now shared; compose_schema's local copies are removed (behavior unchanged, 385 existing tests green). - map_scalar_type recognizes Literal and maps it to the scalar shared by its values, raising for mixed-type / enum-member / all-None Literals instead of silently mapping to String. This one choke point fixes the four direct map_scalar_type call sites in introspection_generator and the TypeMapper scalar branch. - map_python_to_graphql and TypeMapper.map_to_sdl treat a None member (Literal['open', None]) like Optional for the ! suffix; on the input side the field renders nullable, matching the Optional[T] treatment. - "Allowed values: ..." now surfaces on the entity-first path too: introspection entity fields, input fields and method args carry it in their descriptions, and SDL emits docstring blocks on Literal fields (only Literal fields — existing schemas without Literals are byte-identical). 988 tests pass (25 new in tests/graphql/test_literal_type_mapping.py). Backport candidate for KLR-Pattern/nexusx#153. Co-Authored-By: Claude Code <noreply@anthropic.com>
A mixed Literal (Literal['unlimited', 100]) is legal Python/Pydantic but has no correct GraphQL mapping — the rejection message now points at the working alternative: enum members can mix value types and map to a GraphQL enum. Shared literal_info, so both paths carry the guidance. Co-Authored-By: Claude Code <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Closes #316.
graphql/type_mapping.pyhad noLiteralhandling: every Literal annotation fell throughmap_scalar_type's lenient string-matching fallback and becameString— int/bool literals got a wrong type in SDL/introspection, andLiteral[..., None]was forcedNON_NULLalthough the Python type allows null.The compose path already validated Literals (
_literal_info, backported in #313). This PR extracts those rules intographql/type_mapping.pyas the single source for both paths (compose_schema already imports from graphql.type_mapping;utils/would have inverted the layering):literal_info/literal_is_nullable/literal_allowed_values/describe_literal_valueslive ingraphql/type_mapping.py; compose_schema's local copies removed (behavior unchanged — all 385 existing use_case/graphql tests green).map_scalar_typeitself now recognizesLiteraland maps it to the scalar shared by its values, raising for mixed-type / enum-member / all-None Literals. This fixes the four directmap_scalar_typecall sites inintrospection_generatorand theTypeMapperscalar branch with zero changes there.map_python_to_graphqlandTypeMapper.map_to_sdltreat aNonemember (Literal['open', None]) likeOptionalfor the!suffix; input-side fields render nullable, matching theOptional[T]treatment.Allowed values: ...now surfaces on the entity-first path: introspection entity fields, input fields, and method args carry it in their descriptions; SDL emits docstring blocks on Literal fields. Only Literal fields are affected — schemas without Literals are byte-identical.Before / after
Entity SDL gains the constraint docs:
Tests
25 new tests in
tests/graphql/test_literal_type_mapping.py(scalar mapping, nullability, wrappers, validation rejections, SDL, introspection viaGraphQLHandler). Full suite: 988 passed, 1 skipped.Note: the same shared-rules extraction is a backport candidate for KLR-Pattern/nexusx#153.
🤖 Generated with Claude Code