Skip to content

Consider accepting both snake_case and camelCase values for dicts and Literals #756

Closed

Description

@vdusek

Context

Spun out of a review discussion on #738: #738 (comment)

I think there is a case for accepting camelCase here instead - it is exactly what the API accepts. On the other hand, snake_case fits in better with the rest of the Python code and works better for the "I just don't want to look up what model I need to import" use case for TypedDicts.

The same tension already exists today for Literal values used in resource-client methods, e.g.:

sort_by: Literal['created_at', 'last_run_started_at'] | None = 'created_at'

The Apify API expects these as camelCase (createdAt, lastRunStartedAt), while the Python client exposes them in snake_case to match Python conventions. The same question now arises for the newly generated input-side TypedDicts from #738 (field names in dicts passed to client methods).

Proposal

Consider — and, if agreed, implement — accepting both snake_case and camelCase values in:

  1. TypedDict inputs introduced in feat: generate TypedDict types for input-side models #738 (_typeddicts_generated.py, _typeddicts.py) — allow users to pass either naming style as keys.
  2. Literal arguments already present across resource clients (e.g. sort_by, pricing-model literals, webhook event-type literals, etc.) — accept both forms.

Trade-offs to weigh

  • Pro snake_case only: consistent with Pydantic models and the rest of the Python code; idiomatic for Python users.
  • Pro camelCase only: matches the API 1:1 — users who already know the API don't need a translation table, which is especially valuable for the "I don't want to import the Pydantic model, just pass a dict" use case.
  • Pro accepting both: maximum ergonomics, no wrong answer for the user; cost is implementation complexity (normalization layer) and documentation/typing clarity (both forms must be reflected in the type hints so the type checker accepts them).

Scope

  • Decide on the policy (one style only vs. both accepted).
  • If accepting both: define normalization boundary (input translation to API payload), update generation scripts (scripts/postprocess_generated_models.py) and the hand-written literals in resource clients, and ensure type checkers (ty) see both forms.
  • Update docs/examples accordingly.

cc @janbuchar

Activity

  1. added
    t-toolingIssues with this label are in the ownership of the tooling team.
    on Apr 23, 2026
  2. self-assigned this
    on Apr 23, 2026
  3. added this to the 3.0 milestone on Apr 23, 2026
  4. vdusek commented on May 11, 2026

    @vdusek
    ContributorAuthor

    Update...

    Literals

    • No action here. We've already decided to keep literal values verbatim as the API spells them (UPPERCASE enums, 'createdAt', etc.) rather than introducing snake_case variants with a translation layer.

    TypedDicts

    • Open question, runtime already does the right thing.
    • All TypedDicts in _typeddicts.py use snake_case keys.
    • On every resource-client method that accepts <Name>Dict | <Name>, the dict is round-tripped through <Name>.model_validate(dict) before being sent.
      • Which means it accepts both snake_case and camelCase keys at runtime. Only the type checker rejects it.
    • The remaining question is whether to make the type checker accept camelCase keys too. Python's TypedDict has no alias mechanism, so the only ways are:
      1. Generate a snake variant + camel variant per shape and union them — doubles the public surface, no mixed-key dicts, autocomplete becomes ambiguous.
      2. Status quo + document that camelCase keys are runtime-tolerated.

    I'm not convinced the downsides of option 1 are justified, so I'd rather go with option 2.

    @janbuchar please let me know what you think.

  5. janbuchar commented on May 11, 2026

    @janbuchar
    Contributor
    * The remaining question is whether to make the type checker accept `camelCase` keys too. Python's TypedDict has no alias mechanism, so the only ways are:
      
      1. Generate a snake variant + camel variant per shape and union them 
          - doubles the public surface
    

    With respect to the number of attributes, that's a conservative estimate - not every key is made of multiple words 🙂 Of course, there would be two TypedDicts instead of one, that much is true.

          - no mixed-key dicts
    

    I'd argue that this is desirable.

          - autocomplete becomes ambiguous.
    

    In the sense that it would offer both versions of a key, correct? I'd try this out on a simple mock function to see how vscode intellisense handles it.

      2. Status quo + document that `camelCase` keys are runtime-tolerated.
    

    Documentation that says that you don't really need to trust the type checker is not great for multiple reasons.

    I'm not convinced the downsides of option 1 are justified, so I'd rather go with option 2.

    Do you mean the three mentioned above or did something slip? If not, then the only serious issue I see is the ambiguous autocompletion, and I think we should at least try it live to see the actual practical impact would be.

  6. vdusek commented on May 14, 2026

    @vdusek
    ContributorAuthor

    Thanks for your opinion. I'll try to implement a PoC, see how big it's gonna be, and whether/how the IDEs & ty are able to deal with it.

  7. added 2 commits that reference this issue on May 18, 2026
    5914254
    7a579bf
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

t-toolingIssues with this label are in the ownership of the tooling team.

Type

No type

Projects

No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions