Skip to content

feat(python): add flatten_union_request_bodies config flag for Stripe-style request kwargs#15498

Open
patrickthornton wants to merge 14 commits intomainfrom
patrick/python/flatten-union-request-bodies
Open

feat(python): add flatten_union_request_bodies config flag for Stripe-style request kwargs#15498
patrickthornton wants to merge 14 commits intomainfrom
patrick/python/flatten-union-request-bodies

Conversation

@patrickthornton
Copy link
Copy Markdown
Contributor

Summary

  • New opt-in flatten_union_request_bodies config flag (default false). When enabled, endpoints whose referenced request body is a discriminated oneOf/union expose all variants' fields as flat kwargs — Stripe-style — rather than a single request: Union[...] parameter.
  • Non-breaking: existing SDK output is byte-identical until customers opt in; no migration required.
  • Customer scenario: Auth0's connections.create (53-strategy oneOf) — the new spec was technically correct but caused a major DX regression by collapsing the kwargs the customer previously had. Flat flattening restores idiomatic kwargs.

Example

Customer config:

generators:
  - name: fernapi/fern-python-sdk
    config:
      flatten_union_request_bodies: true

Before:

client.connections.create(
    request=CreateConnectionRequestContentAdParams(name="my-conn", strategy="ad"),
)

After:

client.connections.create(
    name="my-conn",
    strategy="ad",
    options={...},
)

The discriminator becomes Union[Literal["ad"], Literal["adfs"], ...]. Per-variant fields with the same wire name are merged (conflicting types are unioned). Loss of cross-field type narrowing matches Stripe's PaymentMethodCreateParams tradeoff.

Test plan

  • New seed fixture unions:flatten-union-request-bodies exercises the path with the existing Shape (circle/square) discriminated union.
  • All 180 python-sdk seed fixtures pass; only the new fixture's snapshot is added — no other generated output changed.
  • v1 config tests cover default-false and explicit-true parsing.
  • Manual smoke test against Auth0's spec produces flat kwargs (name=, strategy=, options=, etc.) with no request= wrapper.
  • v2 generator config schema accepts the field for forward-compat (no v2 behavior change since v2 only generates dynamic snippets + README).

🤖 Generated with Claude Code

Copy link
Copy Markdown

@claude claude Bot left a comment

Choose a reason for hiding this comment

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

Claude Code Review

This repository is configured for manual code reviews. Comment @claude review to trigger a review and subscribe this PR to future pushes, or @claude review once for a one-time review.

Tip: disable this comment in your organization's Code Review settings.

@patrickthornton patrickthornton self-assigned this Apr 28, 2026
@github-actions
Copy link
Copy Markdown
Contributor

🌱 Seed Test Selector

Select languages to run seed tests for:

  • Python
  • TypeScript
  • Java
  • Go
  • Ruby
  • C#
  • PHP
  • Swift
  • Rust
  • OpenAPI

How to use: Click the ⋯ menu above → "Edit" → check the boxes you want → click "Update comment". Tests will run automatically and snapshots will be committed to this PR.

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Apr 28, 2026

SDK Generation Benchmark Results

Comparing PR branch against median of 5 nightly run(s) on main (latest: 2026-04-23T04:59:11Z).

Full benchmark table (click to expand)
Generator Spec main (generator) main (E2E) PR (generator) Delta
python-sdk square 115s (n=5) 223s (n=5) 118s +3s (+2.6%)

main (generator): generator-only time via --skip-scripts (includes Docker image build, container startup, IR parsing, and code generation — this is the same Docker-based flow customers use via fern generate). main (E2E): full customer-observable time including build/test scripts (nightly baseline, informational). Delta is computed against generator-only baseline.
⚠️ = generation exited with a non-zero exit code (timing may not reflect a successful run).
Baseline from nightly runs on main (latest: 2026-04-23T04:59:11Z). Trigger benchmark-baseline to refresh.
Last updated: 2026-04-29 06:57 UTC

patrickthornton and others added 4 commits April 28, 2026 14:20
…odies

Updates the v1 docstring snippet generator and the v2 dynamic-snippets
generator (used by reference.md, wire tests, and dynamic snippet tests)
to honor flatten_union_request_bodies. Previously these example paths
still emitted `request=<UnionVariant>(...)` even when the function
signature accepted flat kwargs, producing examples that would raise
`TypeError: unexpected keyword argument 'request'` at call time.
- Drop the local _wire_string helper and reuse get_wire_value from name_resolver.
- Unify the destructuring path shared by flatten_union_request_bodies and
  inline_request_params so example_type_reference.shape.get_as_union() runs
  once instead of twice.
- Note the deliberate single_property no-op so the limitation is explicit.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

1 participant