Skip to content

feat: generate Literal type aliases instead of StrEnum classes#759

Open
vdusek wants to merge 1 commit intomasterfrom
feat/literals-instead-of-enums
Open

feat: generate Literal type aliases instead of StrEnum classes#759
vdusek wants to merge 1 commit intomasterfrom
feat/literals-instead-of-enums

Conversation

@vdusek
Copy link
Copy Markdown
Contributor

@vdusek vdusek commented Apr 23, 2026

Issue

Description

Replaces every generated class X(StrEnum) with a X = Literal[...] alias. Users pass plain strings (status='RUNNING') instead of enum members, the aliases are reusable across method signatures, and behavior matches Crawlee and the Apify SDK — avoiding mixing-enum-with-string footguns like #575.

Why post-processing

The natural fix — --enum-field-as-literal=all + --use-type-alias on datamodel-code-generator — only handles enums referenced from operation parameters; the rest get inlined at every reference site with no stable name to import. Filed upstream as koxudaxi/datamodel-code-generator#3104. Until that lands, scripts/postprocess_generated_models.py does the rewrite ourselves; once fixed, we can drop the post-processing.

What the post-process does

  • convert_enums_to_literals — rewrites every top-level class X(StrEnum) to X = Literal[...], preserving value order and class docstring.
  • deduplicate_error_type_enum — removes the duplicate inlined class Type(StrEnum) datamodel-codegen emits alongside the named ErrorType (same upstream issue), and rewires Type annotations to ErrorType. AST-based.
  • split_literals_to_file — moves the 11 alias blocks into _literals_generated.py so consumers don't pull in every Pydantic model.
  • snake_case_camelcase_literal_values — converts camelCase string values (StorageOwnership's 'ownedByMe'/'sharedWithMe') to snake_case and emits _<NAME>_WIRE_VALUES so resource clients can convert back to the wire format.

The hand-maintained _types.py is renamed to _literals.py for symmetry.

Consumers

  • All internal code imports the aliases via TYPE_CHECKING where possible.
  • dataset_collection, key_value_store_collection, request_queue_collection translate ownership back to wire format via _STORAGE_OWNERSHIP_WIRE_VALUES.
  • _TERMINAL_STATUSES in _resource_client.py is derived from TerminalActorJobStatus via typing.get_args() — single source of truth.
  • Docstrings, integration tests, unit tests, and the two docs/02_concepts/code/03_nested_*.py examples no longer reference .MEMBER / .MEMBER.value.

Tests

tests/unit/test_postprocess_generated_models.py covers each step independently plus a full-pipeline integration test.

Breaking change

Tracked under v3 on #576. ActorJobStatus.RUNNING and similar enum-member access no longer exists — pass the plain string instead.

@vdusek vdusek added adhoc Ad-hoc unplanned task added during the sprint. t-tooling Issues with this label are in the ownership of the tooling team. labels Apr 23, 2026
@vdusek vdusek self-assigned this Apr 23, 2026
@github-actions github-actions Bot added this to the 139th sprint - Tooling team milestone Apr 23, 2026
@github-actions github-actions Bot added the tested Temporary label used only programatically for some analytics. label Apr 23, 2026
@codecov
Copy link
Copy Markdown

codecov Bot commented Apr 23, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 95.55%. Comparing base (4ca99fd) to head (965eb8b).
⚠️ Report is 1 commits behind head on master.

Additional details and impacted files
@@            Coverage Diff             @@
##           master     #759      +/-   ##
==========================================
- Coverage   95.90%   95.55%   -0.36%     
==========================================
  Files          48       49       +1     
  Lines        5228     4788     -440     
==========================================
- Hits         5014     4575     -439     
+ Misses        214      213       -1     
Flag Coverage Δ
integration 95.55% <100.00%> (-0.36%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@vdusek vdusek force-pushed the feat/literals-instead-of-enums branch from 36b6927 to b306913 Compare April 27, 2026 15:01
@vdusek vdusek marked this pull request as ready for review April 27, 2026 15:57
@vdusek vdusek requested review from Pijukatel and janbuchar April 27, 2026 15:58
@vdusek vdusek force-pushed the feat/literals-instead-of-enums branch from ff6fa87 to 965eb8b Compare April 28, 2026 14:08
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.

This rename is 1) pointless and 2) wrong - the file contains non-literal types too

)


def snake_case_camelcase_literal_values(content: str) -> str:
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.

Please improve the name so that it's clear what is the input convention and what is on the output

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

Labels

adhoc Ad-hoc unplanned task added during the sprint. t-tooling Issues with this label are in the ownership of the tooling team. tested Temporary label used only programatically for some analytics.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Prefer using literal types over enums

3 participants