Repository navigation
feat: a header an operation declares is a keyword of its own - #20
Merged
Merged
Conversation
The generator now reads an operation's optional header parameters, where it skipped them before. Each becomes a keyword of that method's own, named from the header in snake_case and typed from the spec, so process_message takes payer_authorization, sent as Payer-Authorization. Every existing call works as it did. A header can carry a credential, so the method holds the keyword at once in HeldHeaders, which prints as <headers hidden> and is never pickled, and deletes the name, as it already does for a request body. A traceback that records each frame's local variables shows nothing of it. Before sending, the value is checked against the parameter's schema (its maxLength and pattern) in a frame of its own. A value the API would refuse is refused in a message that names the keyword, never the value. The keyword wins over the same header in extra_headers, as correlation_id does for X-Request-Id. The generator stops on a required header, on a parameter in a cookie, on a header whose name clashes with a query parameter or a call option, on a pattern Python cannot read, and on a header declared by an operation a batch can carry. api.md, each method's docstring and the README's Options say how it works. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…here nothing prints it Each test runs sync, on asyncio and on trio. The header is sent beside the key's own Authorization, and not sent when unset (the control). Any case of the scheme is taken. A bare token, another scheme, an empty value, a final line break and 4,097 characters are each refused before anything is sent, with the value nowhere in the error or in the SDK's frames printed with their local variables; 4,096 characters are taken. The keyword wins over the same header in extra_headers, is sent again on a retry, and is carried by no error the API answers. The holder never prints or pickles what it holds, and only process_message takes the keyword. A positive control checks that the SDK's frames are printed with their locals. The generator's tests: an optional header becomes a keyword of the operation's own, and the generator stops on a required header, a cookie, a name that clashes with a call option or a query parameter, a schema that is not a string, a pattern Python cannot read, and a header on an operation a batch can carry. The README test gives the new block its access token. Negative controls, each failing its tests and restored with cp: the keyword's name kept in the method's frame (5 fail), the pattern unchecked (9), extra_headers winning (3), the refusal raised in the frame that holds the value (9). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Merged
briankfung
pushed a commit
that referenced
this pull request
Oct 23, 2026
The next release of haau3, from the pull requests merged since the last one. Merging this makes the GitHub Release and builds the version, which is published to PyPI once a maintainer approves it. --- ## [0.2.0](v0.1.4...v0.2.0) (2026-10-23) ### ⚠ BREAKING CHANGES * the API's spec at 1.0.0-preview.12 ([#16](#16)) ### Features * a header an operation declares is a keyword of its own ([#20](#20)) ([94e5ed2](94e5ed2)) * the API's spec at 1.0.0-preview.12 ([#16](#16)) ([303f24c](303f24c)) --- This PR was generated with [Release Please](https://github.com/googleapis/release-please). See [documentation](https://github.com/googleapis/release-please#release-please). Co-authored-by: haau3-bot[bot] <344534095+haau3-bot[bot]@users.noreply.github.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What changes
An operation's own header is a keyword of its own. The generator reads each operation's optional header parameters, where it skipped them before. Each becomes a keyword of that method's own, named from the header in snake_case and typed from the spec.
process_messagetakespayer_authorization, sent asPayer-Authorization, and every existing call works as it did:Held where nothing prints it. A header can carry a credential, so the method holds the keyword at once in
HeldHeaders, which prints as<headers hidden>and is never pickled, and deletes the name, as it already does for a request body. A traceback that records each frame's local variables shows nothing of it.Checked before sending. The value is checked against the parameter's schema (its
maxLengthandpattern, plus no line break, since Python's$would match before a final one) in a frame of its own. A value the API would refuse raisesHaau3Errorbefore anything is sent, and the message names the keyword, never the value.Precedence. The keyword wins over the same header in
extra_headers, ascorrelation_iddoes forX-Request-Id.The generator stops on a required header, a cookie, a header whose name clashes with a query parameter or a call option, a schema that is not a string, a pattern Python cannot read, and a header on an operation a batch can carry.
Docs: api.md, the method's docstring and the README's Options say how it works.
Why
The spec declares
Payer-Authorizationonprocess_message, but the generator built methods from path and query parameters only, so the header could be sent only throughextra_headers, unchecked, as an ordinary local a traceback would print. The value is the whole header (Bearerand the token), as the spec's schema types it.Tests
tests/test_header_options.py, each run sync, on asyncio and on trio:Authorization, and not sent when unset (the control);extra_headers, is sent again on a retry, and is carried by no error the API answers;process_messagetakes the keyword.A positive control checks that the SDK's frames are printed with their locals.
tests/test_generate.pycovers the generator's new keyword and each case it stops on.Negative controls, each failing its tests: the keyword's name kept in the method's frame (5 fail), the pattern unchecked (9),
extra_headerswinning (3), the refusal raised in the frame that holds the value (9).pytest(672 passed),ruff check,ruff format --check,mypy,pyright,pyright --verifytypes(100%) andty checkpass. The generated files are current.🤖 Generated with Claude Code