Skip to content

chore(open-api#py-client): add hidden Python httpx client generator#586

Open
nx-plugin-for-aws wants to merge 4 commits into
mainfrom
feat/open-api-py-client
Open

chore(open-api#py-client): add hidden Python httpx client generator#586
nx-plugin-for-aws wants to merge 4 commits into
mainfrom
feat/open-api-py-client

Conversation

@nx-plugin-for-aws

@nx-plugin-for-aws nx-plugin-for-aws commented Apr 18, 2026

Copy link
Copy Markdown
Collaborator

Reason for this change

Future connection generators (React → FastAPI Python client, Python client → tRPC API over OpenAPI, etc.) need a Python client generator symmetric to open-api#ts-client. This PR lands the foundation as a hidden generator so it can evolve without release-note surface until a connection generator consumes it.

Description of changes

Adds open-api#py-client (hidden) that emits:

  • types_gen.py — pydantic v2 models for every schema, per-operation error wrapper classes, per-operation request TypedDicts.
  • client_gen.py — sync client using httpx.Client (when clientType is sync or both).
  • async_client_gen.py — async client using httpx.AsyncClient (when clientType is async or both).

The shape mirrors open-api#ts-client end-to-end:

  • One class per spec. Methods are keyword-only with required-first argument ordering.
  • Tag grouping: operations with tags are exposed as api.pet.add_pet(...) rather than flat methods.
  • Object-body flattening: when the request body is an object that won't collide with path/query/header params, its fields become top-level kwargs.
  • Positional single-arg for list/dict bodies (api.user.create_users_with_list_input([User(...), ...])).
  • Typed per-op exceptions (GetPetByIdApiError) with .error narrowable to a discriminated union (GetPetById404Error | GetPetById5XXError | GetPetByIdDefaultError).
  • Streaming for application/jsonl + itemSchema and application/x-ndjson returning Iterator[T] / AsyncIterator[T].
  • Cookies, deprecation warnings, Field(frozen=True) for readOnly, Field(description=...) from spec, multipart/form-data + binary request bodies.

Shared codegen changes

To keep the Python generator as thin as the TypeScript one, language-agnostic concerns moved into codegen-data.ts:

  • op.parameterGroups = { path, query, header, cookie, body } — templates no longer filter by in.
  • op.requestShape — neutral description of operation inputs with a source tag so language templates translate once.
  • op.errorShape — neutral error taxonomy (one entry per non-success response) with statusCodes precomputed for ranges like 5XX.
  • toPythonType now returns idiomatic PEP-585 types; added toPythonAnnotation for forward-ref-quoted use.
  • Models gain pythonType / pythonAnnotation / effectiveProperties (allOf flattening) / isInlinedByAllOf / referencedCollectionKind — all neutral.

Only Python-specific concerns stay in open-api/py-client/generator.ts.

PythonVerifier test harness

A new long-lived uv run --with pydantic --with httpx worker compiles generated modules with py_compile and invokes their methods against a mocked httpx transport that records every request. Lets the TS test suite verify generated code both syntactically and behaviourally.

Description of how you validated changes

  • 103 py-client unit tests (5 spec files, it.each-parameterised) covering shapes, operations, errors, real specs (petstore + FastAPI).
  • All 1733 nx-plugin tests pass — ts-client unchanged.

Issue # (if applicable)

N/A — scoped as chore because the generator is hidden and intended for internal use by upcoming connection generators. Not advertised in release notes until consumed.

Checklist


By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license


Rebase notes (2026-07-10)

Rebased onto latest main (post-#886 codegen rewrite). Because the shared OpenAPI pipeline was rewritten on main (typed parser.ts + codegen-data.ts), the shared-codegen portion of this PR was re-implemented against the new architecture rather than merged textually:

  • requestShape / errorShape / parameter annotations now live in typed form in codegen-data.ts + codegen-data/types.ts (no as any).
  • Python type rendering (toPythonType, toPythonAnnotation, qualifyPythonType, toPythonClassName) integrated into main's languages.ts, emitting PEP-585 generics and Literal[...] enums.

New parity features (added on main after this PR forked):

Metric moved from g38 (now taken by ts#rdb) to g56. All 2661 unit tests pass locally.

Rebase notes (2026-07-13)

Rebased onto latest main again and ported the ts-client spec-compliance fixes that landed since:

  • fix(open-api#ts-client): urlencoded bodies, tuples, JSON primitive responses, union marshalling #922 parity: application/x-www-form-urlencoded bodies route through httpx data= (repeated keys for arrays, None omitted) instead of JSON; fixed-length tuples (3.1 prefixItems) render as tuple[A, B] validated via TypeAdapter; JSON-wire primitive responses parse correctly; non-discriminated unions with conflicting member marshalling fail loudly at generation time (shared assertNoConflictingUnionMemberMarshalling).
  • fix(open-api#ts-client): multipart encoding, path styles, content-based params #930 parity: matrix/label path styles (incl. explode) serialise per RFC 6570; allowReserved query parameters keep reserved characters literal; content-based (content: application/json) parameters JSON-serialise as a single value; multipart parts honour encoding-declared content types.
  • Python worker now reads streamed multipart request bodies so behavioural tests can assert on the wire payload.

All 2753 unit tests pass locally; build green.

Rebase notes (2026-07-15)

Rebased onto latest main (clean rebase, no conflicts) and ported the two urlencoded ts-client fixes that landed since:

All 2778 unit tests pass locally; build green.

@github-actions

Copy link
Copy Markdown
Contributor

📚 Documentation translations have been updated and committed (e0e8b17) to this PR.

docs/src/i18n/schema-translations.json

@codecov-commenter

codecov-commenter commented Apr 18, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 85.00000% with 39 lines in your changes missing coverage. Please review.
✅ Project coverage is 87.68%. Comparing base (335f89f) to head (efd3431).

Files with missing lines Patch % Lines
...kages/nx-plugin/src/open-api/utils/codegen-data.ts 82.97% 1 Missing and 23 partials ⚠️
...lugin/src/open-api/utils/codegen-data/languages.ts 85.29% 2 Missing and 13 partials ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #586      +/-   ##
==========================================
- Coverage   87.83%   87.68%   -0.15%     
==========================================
  Files         166      167       +1     
  Lines        6067     6310     +243     
  Branches     1467     1557      +90     
==========================================
+ Hits         5329     5533     +204     
- Misses        367      370       +3     
- Partials      371      407      +36     

☔ View full report in Codecov by Harness.
📢 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.

@nx-plugin-for-aws nx-plugin-for-aws force-pushed the feat/open-api-py-client branch from e0e8b17 to ce94e62 Compare April 19, 2026 13:28
@github-actions

Copy link
Copy Markdown
Contributor

📚 Documentation translations have been updated and committed (3ff14ec) to this PR.

docs/src/i18n/schema-translations.json

@github-actions

Copy link
Copy Markdown
Contributor

📚 Documentation translations have been updated and committed (904ee26) to this PR.

docs/src/content/docs/es/guides/astro-docs.mdx
docs/src/content/docs/es/guides/connection.mdx
docs/src/content/docs/es/guides/connection/react-agui.mdx
docs/src/content/docs/es/guides/connection/react-py-strands-agent.mdx
docs/src/content/docs/es/guides/docker-bundling.mdx
docs/src/content/docs/es/index.mdx
docs/src/content/docs/es/snippets/connection/react-agent-infrastructure.mdx
docs/src/content/docs/fr/guides/astro-docs.mdx
docs/src/content/docs/fr/guides/connection.mdx
docs/src/content/docs/fr/guides/connection/react-agui.mdx
docs/src/content/docs/fr/guides/connection/react-py-strands-agent.mdx
docs/src/content/docs/fr/guides/docker-bundling.mdx
docs/src/content/docs/fr/guides/py-mcp-server.mdx
docs/src/content/docs/fr/index.mdx
docs/src/content/docs/fr/snippets/agent/bedrock-deployment.mdx
docs/src/content/docs/fr/snippets/connection/react-agent-infrastructure.mdx
docs/src/content/docs/it/guides/astro-docs.mdx
docs/src/content/docs/it/guides/connection.mdx
docs/src/content/docs/it/guides/connection/react-agui.mdx
docs/src/content/docs/it/guides/connection/react-py-strands-agent.mdx
docs/src/content/docs/it/guides/docker-bundling.mdx
docs/src/content/docs/it/guides/ts-strands-agent.mdx
docs/src/content/docs/it/index.mdx
docs/src/content/docs/it/snippets/connection/react-agent-infrastructure.mdx
docs/src/content/docs/jp/guides/astro-docs.mdx
docs/src/content/docs/jp/guides/connection.mdx
docs/src/content/docs/jp/guides/connection/react-agui.mdx
docs/src/content/docs/jp/index.mdx
docs/src/content/docs/jp/snippets/connection/react-agent-infrastructure.mdx
docs/src/content/docs/ko/guides/astro-docs.mdx
docs/src/content/docs/ko/guides/connection.mdx
docs/src/content/docs/ko/guides/connection/react-agui.mdx
docs/src/content/docs/ko/guides/docker-bundling.mdx
docs/src/content/docs/ko/guides/ts-strands-agent.mdx
docs/src/content/docs/ko/index.mdx
docs/src/content/docs/ko/snippets/agent/bedrock-deployment.mdx
docs/src/content/docs/ko/snippets/connection/react-agent-infrastructure.mdx
docs/src/content/docs/pt/guides/astro-docs.mdx
docs/src/content/docs/pt/guides/connection.mdx
docs/src/content/docs/pt/guides/connection/react-agui.mdx
docs/src/content/docs/pt/guides/connection/react-py-strands-agent.mdx
docs/src/content/docs/pt/guides/connection/react-ts-strands-agent.mdx
docs/src/content/docs/pt/guides/py-mcp-server.mdx
docs/src/content/docs/pt/index.mdx
docs/src/content/docs/pt/snippets/agent/bedrock-deployment.mdx
docs/src/content/docs/pt/snippets/connection/react-agent-infrastructure.mdx
docs/src/content/docs/vi/guides/astro-docs.mdx
docs/src/content/docs/vi/guides/connection.mdx
docs/src/content/docs/vi/guides/connection/react-agui.mdx
docs/src/content/docs/vi/guides/connection/react-py-strands-agent.mdx
docs/src/content/docs/vi/guides/connection/react-ts-strands-agent.mdx
docs/src/content/docs/vi/guides/docker-bundling.mdx
docs/src/content/docs/vi/guides/ts-mcp-server.mdx
docs/src/content/docs/vi/index.mdx
docs/src/content/docs/vi/snippets/agent/bedrock-deployment.mdx
docs/src/content/docs/vi/snippets/connection/react-agent-infrastructure.mdx
docs/src/content/docs/zh/guides/connection.mdx
docs/src/content/docs/zh/guides/connection/react-agui.mdx
docs/src/content/docs/zh/guides/connection/react-py-strands-agent.mdx
docs/src/content/docs/zh/guides/docker-bundling.mdx
docs/src/content/docs/zh/index.mdx
docs/src/content/docs/zh/snippets/agent/bedrock-deployment.mdx
docs/src/content/docs/zh/snippets/connection/react-agent-infrastructure.mdx

@github-actions

Copy link
Copy Markdown
Contributor

📚 Documentation translations have been updated and committed (2da12ce) to this PR.

docs/src/i18n/schema-translations.json

@nx-plugin-for-aws nx-plugin-for-aws force-pushed the feat/open-api-py-client branch 3 times, most recently from ee94c32 to 4b9e07d Compare May 5, 2026 00:13
@nx-plugin-for-aws nx-plugin-for-aws force-pushed the feat/open-api-py-client branch 2 times, most recently from 34d7d5d to 352d62c Compare May 13, 2026 20:16
@nx-plugin-for-aws nx-plugin-for-aws force-pushed the feat/open-api-py-client branch from fc1239b to d612646 Compare May 29, 2026 10:49
nx-plugin-for-aws pushed a commit that referenced this pull request Jul 10, 2026
Rebased port of #586 onto main's rewritten OpenAPI codegen pipeline
(parser.ts + typed codegen-data). Emits pydantic v2 models and
httpx-based sync/async clients mirroring open-api#ts-client.
@nx-plugin-for-aws nx-plugin-for-aws force-pushed the feat/open-api-py-client branch from d612646 to f8ce907 Compare July 10, 2026 15:19
nx-plugin-for-aws pushed a commit that referenced this pull request Jul 13, 2026
Rebased port of #586 onto main's rewritten OpenAPI codegen pipeline
(parser.ts + typed codegen-data). Emits pydantic v2 models and
httpx-based sync/async clients mirroring open-api#ts-client.
@nx-plugin-for-aws nx-plugin-for-aws force-pushed the feat/open-api-py-client branch from f8ce907 to 4842013 Compare July 13, 2026 05:31
test added 4 commits July 15, 2026 00:18
Rebased port of #586 onto main's rewritten OpenAPI codegen pipeline
(parser.ts + typed codegen-data). Emits pydantic v2 models and
httpx-based sync/async clients mirroring open-api#ts-client.
- Tagged discriminated unions: discriminated oneOf/anyOf renders as a
  pydantic tagged union (Annotated[Union[...], Field(discriminator=...)])
  with Literal-typed discriminator fields on each member, mirroring the
  ts-client tagged-union narrowing from #913.
- multipart/form-data: flattened-body multipart requests route fields
  through httpx files=/data= instead of JSON-encoding, matching the
  ts-client FormData handling from #914; binary fields stream as file
  parts.
- deepObject query parameters serialise as key[prop]=value pairs.
- toPythonName also tests the snake-cased form against the Python keyword
  set so spec properties like 'from_' don't emit invalid syntax.
… from #922 and #930

- application/x-www-form-urlencoded bodies route through httpx data=
  (arrays as repeated keys, None fields omitted) instead of being
  JSON-encoded.
- Fixed-length tuples (3.1 prefixItems) render as tuple[A, B] with
  TypeAdapter validation; open tuples degrade to plain lists.
- matrix and label path styles serialise per RFC 6570 (with explode).
- allowReserved query parameters keep reserved characters literal via a
  manually built query string.
- Content-based (content: application/json) parameters JSON-serialise
  their value as a single query/header/cookie entry.
- Multipart parts honour content types declared by the request body
  encoding object.
- Python worker reads streamed (multipart) request bodies so tests can
  assert on the wire payload.
…batim

Parity with #937: a urlencoded body whose schema is a primitive (e.g. a
raw pre-encoded string) is sent as-is via httpx content= with the
declared Content-Type, rather than being dict-iterated for form
encoding. Object bodies are unchanged. The #938 generation-time guard
for non-encodable urlencoded array bodies is inherited from shared
codegen; covered with a py-client test.
@nx-plugin-for-aws nx-plugin-for-aws force-pushed the feat/open-api-py-client branch from 4842013 to efd3431 Compare July 15, 2026 00:43
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants