Skip to content

Commit 36b6927

Browse files
vdusekclaude
andcommitted
refactor: drop explicit TypeAlias annotation from generated literals
`X = Literal[...]` is already recognized as a type alias by every Python type checker, so the `: TypeAlias` annotation adds noise without adding any semantic value. Drop it from the postprocess-generated aliases and from the hand-maintained `TerminalActorJobStatus`. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 842c82d commit 36b6927

4 files changed

Lines changed: 65 additions & 90 deletions

File tree

scripts/postprocess_generated_models.py

Lines changed: 18 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,12 @@
44
- Fix discriminator field names that use camelCase instead of snake_case (known issue with
55
discriminators on schemas referenced from array items).
66
- Deduplicate the inlined `Type(StrEnum)` that comes from ErrorResponse.yaml; rewire to `ErrorType`.
7-
- Rewrite every `class X(StrEnum)` as `X: TypeAlias = Literal[...]` so downstream code can pass
8-
plain strings (and reuse the named alias in resource-client signatures) instead of enum members.
7+
- Rewrite every `class X(StrEnum)` as `X = Literal[...]` so downstream code can pass plain strings
8+
(and reuse the named alias in resource-client signatures) instead of enum members.
99
- Convert camelCase string values in each literal alias to snake_case (Pythonic), and emit a
1010
`_<NAME>_WIRE_VALUES` mapping the Python value back to the original camelCase form so the
1111
resource clients can still produce the exact string the API expects on the wire.
12-
- Move the resulting `TypeAlias = Literal[...]` definitions into `_literals_generated.py`, leaving
12+
- Move the resulting `X = Literal[...]` definitions into `_literals_generated.py`, leaving
1313
`_models_generated.py` importing them — so consumers can depend on a dedicated literals module
1414
without pulling in every Pydantic model.
1515
- Add `@docs_group('Models')` to every model class (plus the required import).
@@ -107,7 +107,7 @@ def deduplicate_error_type_enum(content: str) -> str:
107107

108108

109109
def convert_enums_to_literals(content: str) -> str:
110-
"""Rewrite every `class X(StrEnum): ...` into an `X: TypeAlias = Literal[...]` alias.
110+
"""Rewrite every `class X(StrEnum): ...` into an `X = Literal[...]` alias.
111111
112112
Each member assignment (`NAME = 'value'`) contributes its string value to the literal in
113113
declaration order. The class docstring, if present, is preserved as a trailing bare-string
@@ -139,7 +139,7 @@ def convert_enums_to_literals(content: str) -> str:
139139
]
140140
docstring = ast.get_docstring(node)
141141

142-
new_lines: list[str] = [f'{node.name}: TypeAlias = Literal[']
142+
new_lines: list[str] = [f'{node.name} = Literal[']
143143
new_lines.extend(f' {v!r},' for v in values)
144144
new_lines.append(']')
145145
if docstring is not None:
@@ -160,15 +160,15 @@ def convert_enums_to_literals(content: str) -> str:
160160
for start, end, new in sorted(replacements, key=lambda r: r[0], reverse=True):
161161
lines[start:end] = new
162162

163-
return _ensure_typing_import(_collapse_blank_lines('\n'.join(lines)), 'TypeAlias')
163+
return _collapse_blank_lines('\n'.join(lines))
164164

165165

166166
LITERALS_FILE_HEADER = """\
167167
# generated by postprocess_generated_models
168168
169169
from __future__ import annotations
170170
171-
from typing import Literal, TypeAlias
171+
from typing import Literal
172172
173173
174174
"""
@@ -182,12 +182,11 @@ def _camel_to_snake(value: str) -> str:
182182

183183

184184
def _is_literal_alias(node: ast.stmt) -> bool:
185-
"""Return True if `node` is a top-level `Name: TypeAlias = Literal[...]` statement."""
185+
"""Return True if `node` is a top-level `Name = Literal[...]` statement."""
186186
return (
187-
isinstance(node, ast.AnnAssign)
188-
and isinstance(node.target, ast.Name)
189-
and isinstance(node.annotation, ast.Name)
190-
and node.annotation.id == 'TypeAlias'
187+
isinstance(node, ast.Assign)
188+
and len(node.targets) == 1
189+
and isinstance(node.targets[0], ast.Name)
191190
and isinstance(node.value, ast.Subscript)
192191
and isinstance(node.value.value, ast.Name)
193192
and node.value.value.id == 'Literal'
@@ -197,9 +196,9 @@ def _is_literal_alias(node: ast.stmt) -> bool:
197196
def snake_case_camelcase_literal_values(content: str) -> str:
198197
"""Rewrite camelCase string values in `Literal[...]` aliases into snake_case.
199198
200-
Scans each `Name: TypeAlias = Literal[...]` block and, for any value matching the camelCase
201-
pattern (lowercase-first followed by an uppercase letter), converts it to snake_case. For each
202-
alias that had at least one conversion, emits a `_<NAME>_WIRE_VALUES: dict[<NAME>, str] = ...`
199+
Scans each `Name = Literal[...]` block and, for any value matching the camelCase pattern
200+
(lowercase-first followed by an uppercase letter), converts it to snake_case. For each alias
201+
that had at least one conversion, emits a `_<NAME>_WIRE_VALUES: dict[<NAME>, str] = ...`
203202
mapping right after the alias so consumers can translate back to the API wire format.
204203
205204
SCREAMING_SNAKE_CASE, dotted, hyphenated, and HTTP-method values pass through unchanged.
@@ -249,7 +248,7 @@ def snake_case_camelcase_literal_values(content: str) -> str:
249248

250249

251250
def split_literals_to_file(content: str) -> tuple[str, str]:
252-
"""Move every top-level `Name: TypeAlias = Literal[...]` block into a separate literals module.
251+
"""Move every top-level `Name = Literal[...]` block into a separate literals module.
253252
254253
Walks the top-level AST, collects each literal alias plus its trailing bare-string docstring,
255254
deletes them from `_models_generated.py`, and rebuilds `_literals_generated.py` from the blocks
@@ -370,7 +369,7 @@ def _extract_top_level_symbols(tree: ast.Module) -> list[tuple[str, ast.stmt, in
370369
371370
If a top-level string expression immediately follows a symbol, it is absorbed into that
372371
symbol's `end_line` so they get pruned together (datamodel-codegen emits the schema description
373-
for TypeAlias statements as a bare string right after the alias).
372+
for type-alias statements as a bare string right after the alias).
374373
"""
375374
symbols: list[tuple[str, ast.stmt, int]] = []
376375
body = tree.body
@@ -382,6 +381,8 @@ def _extract_top_level_symbols(tree: ast.Module) -> list[tuple[str, ast.stmt, in
382381
name = node.name
383382
elif isinstance(node, ast.AnnAssign) and isinstance(node.target, ast.Name):
384383
name = node.target.id
384+
elif isinstance(node, ast.Assign) and len(node.targets) == 1 and isinstance(node.targets[0], ast.Name):
385+
name = node.targets[0].id
385386

386387
if name is not None:
387388
assert node.end_lineno is not None # noqa: S101

src/apify_client/_literals.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
from __future__ import annotations
22

33
from datetime import timedelta
4-
from typing import Literal, TypeAlias
4+
from typing import Literal
55

66
from apify_client._models import WebhookRepresentation
77
from apify_client._models_generated import WebhookCreate
@@ -19,7 +19,7 @@
1919
`condition`) are ignored at runtime.
2020
"""
2121

22-
TerminalActorJobStatus: TypeAlias = Literal['SUCCEEDED', 'FAILED', 'TIMED-OUT', 'ABORTED']
22+
TerminalActorJobStatus = Literal['SUCCEEDED', 'FAILED', 'TIMED-OUT', 'ABORTED']
2323
"""Subset of `ActorJobStatus` values that indicate the job has finished and will not change again."""
2424

2525
Timeout = timedelta | Literal['no_timeout', 'short', 'medium', 'long']

src/apify_client/_literals_generated.py

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,9 @@
22

33
from __future__ import annotations
44

5-
from typing import Literal, TypeAlias
5+
from typing import Literal
66

7-
ActorJobStatus: TypeAlias = Literal[
7+
ActorJobStatus = Literal[
88
'READY',
99
'RUNNING',
1010
'SUCCEEDED',
@@ -17,14 +17,14 @@
1717
"""Status of an Actor job (run or build)."""
1818

1919

20-
ActorPermissionLevel: TypeAlias = Literal[
20+
ActorPermissionLevel = Literal[
2121
'LIMITED_PERMISSIONS',
2222
'FULL_PERMISSIONS',
2323
]
2424
"""Determines permissions that the Actor requires to run. For more information, see the [Actor permissions documentation](https://docs.apify.com/platform/actors/development/permissions)."""
2525

2626

27-
ErrorType: TypeAlias = Literal[
27+
ErrorType = Literal[
2828
'3d-secure-auth-failed',
2929
'access-right-already-exists',
3030
'action-not-found',
@@ -418,7 +418,7 @@
418418
"""Machine-processable error type identifier."""
419419

420420

421-
GeneralAccess: TypeAlias = Literal[
421+
GeneralAccess = Literal[
422422
'ANYONE_WITH_ID_CAN_READ',
423423
'ANYONE_WITH_NAME_CAN_READ',
424424
'FOLLOW_USER_SETTING',
@@ -427,7 +427,7 @@
427427
"""Defines the general access level for the resource."""
428428

429429

430-
HttpMethod: TypeAlias = Literal[
430+
HttpMethod = Literal[
431431
'GET',
432432
'HEAD',
433433
'POST',
@@ -440,7 +440,7 @@
440440
]
441441

442442

443-
RunOrigin: TypeAlias = Literal[
443+
RunOrigin = Literal[
444444
'DEVELOPMENT',
445445
'WEB',
446446
'API',
@@ -453,13 +453,13 @@
453453
]
454454

455455

456-
SourceCodeFileFormat: TypeAlias = Literal[
456+
SourceCodeFileFormat = Literal[
457457
'BASE64',
458458
'TEXT',
459459
]
460460

461461

462-
StorageOwnership: TypeAlias = Literal[
462+
StorageOwnership = Literal[
463463
'owned_by_me',
464464
'shared_with_me',
465465
]
@@ -471,23 +471,23 @@
471471
"""Maps snake_case `StorageOwnership` values to the camelCase form expected on the API wire."""
472472

473473

474-
VersionSourceType: TypeAlias = Literal[
474+
VersionSourceType = Literal[
475475
'SOURCE_FILES',
476476
'GIT_REPO',
477477
'TARBALL',
478478
'GITHUB_GIST',
479479
]
480480

481481

482-
WebhookDispatchStatus: TypeAlias = Literal[
482+
WebhookDispatchStatus = Literal[
483483
'ACTIVE',
484484
'SUCCEEDED',
485485
'FAILED',
486486
]
487487
"""Status of the webhook dispatch indicating whether the HTTP request was successful."""
488488

489489

490-
WebhookEventType: TypeAlias = Literal[
490+
WebhookEventType = Literal[
491491
'ACTOR.BUILD.ABORTED',
492492
'ACTOR.BUILD.CREATED',
493493
'ACTOR.BUILD.FAILED',

0 commit comments

Comments
 (0)