Skip to content

fix(codegen): allow custom scalars from scalar_map without explicit python type (#4741) - #4789

Open
Rajan-vish wants to merge 3 commits into
strawberry-graphql:mainfrom
Rajan-vish:fix-query-codegen-scalar-map-4741
Open

Rajan-vish wants to merge 3 commits into
strawberry-graphql:mainfrom
Rajan-vish:fix-query-codegen-scalar-map-4741

Conversation

@Rajan-vish

@Rajan-vish Rajan-vish commented Nov 3, 2026 •

Copy link
Copy Markdown

Description

Fixes #4741.

When custom scalars are mapped via StrawberryConfig(scalar_map={...}) or have ScalarDefinition without an explicit ScalarWrapper, _get_field_type in QueryCodegen was discarding the original Python type and passing None to _collect_scalar. This caused PythonPlugin._print_scalar_type to fail with:
AssertionError: Scalar type must have a python type:
and TypeScriptPlugin to fail with KeyError or assertion errors.

Solution

  1. In strawberry/codegen/query_codegen.py:
    • Preserve original_python_type from ield_type before scalar registry replacement.
    • For ScalarDefinition, if original_python_type is None, look up the matching scalar in self.schema.schema_converter.scalar_registry or fallback to ield_type.origin.
    • Unwrap supertype (e.g. for NewType).
  2. In strawberry/codegen/plugins/python.py:
    • Safely resolve the scalar's Python type and add module imports (skipping builtins/main).
    • Gracefully fallback to Any if python_type is None.
  3. In strawberry/codegen/plugins/typescript.py:
    • Map ype_.python_type via SCALARS_TO_TS_TYPE with a fallback to �ny.
  4. In ests/codegen/test_query_codegen.py:
    • Added unit tests covering custom scalars mapped via StrawberryConfig.scalar_map in both return types and input variables for Python and TypeScript codegen.
  5. Added RELEASE.md for patch release changelog.

Types of Changes

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to change)
  • Documentation update

Checklist

  • I have read the CONTRIBUTING doc
  • I have added tests to cover my changes
  • All new and existing tests passed
  • I have added entries to RELEASE.md

Summary by Sourcery

Fix client code generation for custom scalars by preserving their underlying Python types and falling back gracefully when type information is unavailable.

Bug Fixes:

  • Prevent code generation from failing for custom scalars registered through scalar maps or without explicit Python types.
  • Provide safe Python and TypeScript fallbacks for scalars whose underlying types cannot be resolved.

Enhancements:

  • Preserve and deduplicate custom scalar Python type information across generated client types.
  • Handle None-based scalars and unwrap NewType definitions during code generation.

Documentation:

  • Add a patch release note describing the custom scalar code generation fix.

Tests:

  • Add coverage for custom scalar return types, input variables, deduplication, void scalars, and unmapped scalar fallbacks in Python and TypeScript code generation.

@github-actions

github-actions Bot commented Nov 3, 2026

Copy link
Copy Markdown
Contributor

Thanks for adding the RELEASE.md file!

Below is the changelog that will be used for the release.


This release fixes an issue where running strawberry codegen crashed with
AssertionError: Scalar type must have a python type when custom scalars were
registered via StrawberryConfig.scalar_map.

QueryCodegen now correctly preserves and propagates the custom scalar's underlying
Python type (or falls back to Any when unmapped) when generating Python and
TypeScript client types.

This release was contributed by @Rajan-vish in #4789

@greptile-apps

greptile-apps Bot commented Nov 3, 2026 •

Copy link
Copy Markdown
Contributor

RetriggerView in GreptileConfidence Score: 3/5

[Medium impact] The changes affect the bounded workflow that generates Python and TypeScript client types.

Fix duplicate TypeScript aliases and undefined NoneType references before merging.

Findings

  1. P1 TypeScript aliases repeat ▶
  2. P1 Generated Python references undefined `NoneType` ▶

Summary

Fixes custom scalar code generation by retaining Python types from scalar_map.

  • Codegen carries custom scalar types into Python and TypeScript output.

Diagram

%%{init: {'theme': 'neutral'}}%%
flowchart LR
  A[Field or variable] --> B[Resolve scalar definition]
  B --> C[Find underlying Python type]
  C --> D[Collect scalar]
  D --> E[Python alias and imports]
  D --> F[TypeScript alias]
Loading

Reviews (1) · Last reviewed commit: "fix(codegen): allow custom scalars from ..." · Reviewed by Greptile

not isinstance(field_type, StrawberryType)
and field_type in self.schema.schema_converter.scalar_registry
):
original_python_type = field_type

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 TypeScript aliases repeat

Preserving each field's Python type can generate the same TypeScript alias twice. Mapping str and float to one scalar definition named Custom is accepted by the schema converter. Selecting fields of both types now collects two different GraphQLScalar objects and emits both type Custom = string and type Custom = number. TypeScript rejects the duplicate name.

Choose one Python type per scalar definition and reuse it for fields and variables.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed in commit 341a604 by caching GraphQLScalar in self._scalar_definitions by scalar name in QueryCodegen._collect_scalar, ensuring each scalar definition is collected once and reused across all fields and variables without duplicate alias generation.

Comment on lines +190 to +194
type_name = type_.python_type.__name__
if hasattr(
type_.python_type, "__module__"
) and type_.python_type.__module__ not in ("builtins", "__main__"):
self.imports[type_.python_type.__module__].add(type_name)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Generated Python references undefined NoneType

A field returning None resolves to the registered Void scalar with python_type=type(None). This branch emits Void = NewType("Void", NoneType) but skips its import because its module is builtins. NoneType is not a built-in name, so importing the generated file raises NameError.

Import NoneType from types, or print type(None) instead.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed in commit 341a604 by importing NoneType from types when type_.python_type is type(None), and mapping type(None) to null in TypeScript.

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - I've reviewed your changes and they look great!

Sourcery assessment

Approved.


Sourcery is free for open source - if you like our reviews please consider sharing them ✨

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Linked issues

#4741 codegen: allow mapping custom scalars to Python types (`Scalar type must have a python type`) · 1 of 3 met

# Requirement Status Notes
1 Prevent codegen from aborting on custom scalars registered through StrawberryConfig.scalar_map. ✅ Met QueryCodegen preserves or recovers the scalar's Python type, and the plugins fall back to Any/any when no type is available.
2 Allow scalar names to be mapped to Python annotations through the CLI or a config file. ❌ Not addressed The change uses types already present in the schema's scalar registry; it adds no CLI or config-file mapping for scalar names to annotations.
3 Fall back to Any for an unmapped scalar with a warning instead of raising an assertion. 🟡 Partly met Python codegen falls back to Any, but it does not emit a warning.

Comment thread strawberry/codegen/plugins/python.py Resolved

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

codegen: allow mapping custom scalars to Python types (Scalar type must have a python type)

1 participant