feat: generate Literal type aliases instead of StrEnum classes (#759)
## Issue - Closes #576 ## 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](https://github.com/koxudaxi/datamodel-code-generator/issues/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.
V
Vlada Dusek committed
2bf5a75f38a74a48c5c9a9682d9eaf330da27935
Parent: 63b866a
Committed by GitHub <noreply@github.com>
on 5/3/2026, 5:34:14 PM